Something wrong with TCP Client in C Language - objective-c

i have wierd problem to send informations and the data to TCP server.
My code was made with C and Objective-C.
I send the information and after i send my data in same connection.
TCPClient *tcp = [[TCPClient alloc] init];
TCPResponse c = [tcp connectWithHost:#"127.0.0.1" onPort:#"2506" withTimeout:30];
TCPResponse sendInfo = [tcp sendString:msg];
TCPResponse send = [tcp sendData: data];
The sendInfo arrives fine, but the other...
const void *dataBytes = [data bytes];
unsigned long length = [data length];
//NSLog(#"[####]\n %s [%ld][%ld]", dataBytes, sizeof(dataBytes), [data length]);
ssize_t bytes_sent = send(_sockfd, dataBytes, length, 0);
if (bytes_sent < length) {
/* Se não enviar: retorna erro */
return [TCPClient setupError:errno withDefault: TCPResponseErrorToSend];
}
If the commented line is commented i have error in the server, if not the data arrives fine.
If I put sleep instead the commented line the data arrives fine.
My server was implemented in Java.
Thank you.

Related

What is wrong with my sockets code?

I'm working on an iOS version of my Android application which constantly sends some binary data to server over regular TCP connection. And while Java code works fine, my code in plain C doesn't - the connection breaks after the first bunch of data is sent to server. I managed to reimplement Java solution using CFNetwork framework, however my C code is still a problem...
C code (error checking and uninteresting code is removed):
mSocket = socket(AF_INET, SOCK_STREAM, 0);
//...
connect(mSocket, (struct sockaddr *)&sin, sizeof(sin));
int keepalive = 1;
int res = setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,
sizeof(int));
int set = 1;
setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
unsigned char buffer* = //...;
int ret = send(mSocket, buffer, bufferLen, 0); //handshake with server, runs fine
while(/*there is new data*/) {
buffer = new_data(); //some fake method
ret = send(mSocket, buffer, bufferLen, 0); //On the second iteration
//write returns -1
}
However, this Objective-C code based on CFNetwork works just fine...
Objective-C code:
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
host,
port,
&readStream,
&writeStream);
CFWriteStreamOpen(mServerWriteStream);
while(/*there is new data*/) {
NSData* data = //....
int wroteBytes = CFWriteStreamWrite(mServerWriteStream, [data bytes], [data length]);
}
So I'm puzzled with what am I doing wrong in C code and will appreciate any hints
Thank you
If you need a regular TCP connection you need to create the socket with such protocol. Instead of:
mSocket = socket(AF_INET, SOCK_STREAM, 0);
You should create it as:
#include <netinet/in.h>
mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
On my system IPPROTO_TCP is defined as 6 and not 0. I suspect you have something similar.
The setsockopt() has to execute before connect() or it is ignored.

UDP client prints received data twice [i.e. received data is appended on itself twice]

GOAL: I'm making a UDP socket to send and receive data.
I am testing this on my laptop, so I have a server running on the background that listens to incoming messages and echoing this back.
PROBLEM: I am seeing that the server receives one string, and when it echoes back, the client reads the string 2 times instead of ONE and adds gibberish.How to solve this?
Output from the code is: HelloHello09[btw i als get some questionsmarks that are upside down in front of the 09 and behind it, but I cannot paste it, lolz]
Code:
#define BUFLEN 5
#define PORT 12345
#import <Foundation/Foundation.h>
#define srvr_IP "127.0.0.1"
void errorSig(char *);
int main (int argc, const char * argv[])
{
int sockSend = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in si_other;
char buf[BUFLEN] = "Hello";
char bufrec[BUFLEN];
#autoreleasepool {
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
inet_pton(AF_INET, srvr_IP, &si_other.sin_addr);
memset(&si_other.sin_zero, 0, sizeof(si_other.sin_zero));
int size = sizeof(si_other);
sendto(sockSend, buf, BUFLEN, 0,
(struct sockaddr *)&si_other, size);
recvfrom(sockSend, bufrec, BUFLEN, 0, (struct sockaddr *)&si_other, (unsigned int*)&size);
NSString *test = [[NSString alloc]initWithUTF8String:bufrec];
NSLog(#" data is: %#", test);
close(sockSend);
}
return 0;
}
I've just seen that you did in fact define BUFLEN as 5 (sorry I missed that when I wrote my comment above). As I suspected, you have a NULL termination issue. The length of a string in C is one more than the number of characters you want to store, to make room for the NULL terminator indicating the end of the string.
Change your BUFLEN definition to 6 and you should find it works much better.

CFSocketSetAddress fails if not host computer IP?

I am making a simple WOL application.
So far I can successfully create a socket, however when setting the address using CFSocketSetAddress I can only use the network ip of my computer (WiFi ip = 192.168.0.5) or local ip (127.0.0.1).
For WOL I would like to send the data to the broadcast address (255.255.255.255). If this is entered I am returned with the error 'address could not be set'.
Am I miss-understanding the use of CFSocketSetAddress, and the address is supposed to be the hosts IP, or the destination IP? In either case, what do I need to do, so that my destination ip is the broadcast address?
Below is some of my code:
/*************************************************************************/
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT); //port
inet_aton(IP, &addr.sin_addr);//IP is the host network ip: 192.168.0.5
NSData *address = [NSData dataWithBytes: &addr length: sizeof(addr)];
if (CFSocketSetAddress(WOLsocket, (CFDataRef)address) != kCFSocketSuccess){
NSLog(#"Address could not be set!");
}
I solved my problem by using a different method (Got native socket), and then instead of using CFSocketSetAddress, I passed the address in the second argument of CFSocketSendData.
I don't have a link/reference for the changes, as it was some code stored on my HDD from days of heavy googling.
Would like to say thanks to David Gelhar Who pointed me in the right direction from my previous question.
for anyone else who might need this this is my code;
//Gets native & sets options
/*************************************************************************/
int desc = -1;
desc = CFSocketGetNative(WOLsocket);
int yes = 1;
if (setsockopt (desc, SOL_SOCKET, SO_BROADCAST, (char *)&yes, sizeof (yes)) < 0) {
NSLog(#"Set Socket options failed");
return EXIT_FAILURE;
}
//sets address socket - doesn't bind this is done in CFSocketSendData
/*************************************************************************/
unsigned long bcast = 0xffffffff;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT); //port
addr.sin_addr.s_addr = bcast;
NSData *address = [NSData dataWithBytes: &addr length: sizeof(addr)];
//Sends Data
/*************************************************************************/
char ethadd []= "helloworld";
CFDataRef Data = CFDataCreate(NULL, (const UInt8*)ethadd, sizeof(ethadd));
if (CFSocketSendData(WOLsocket, address, Data, 0) < 0){
NSLog(#"Data could not be sent!");
return EXIT_FAILURE;
}
else NSLog(#"Data Sent");
}
For my app, the problem is that it was sandboxed, but it wasn't given network permissions.
If you're using App Sandbox, you need to check one of these:

Cocoa Touch Bonjour how to deal with NSNetService addresses and uint8_t

I'm attempting to make an iOS app communicate with a server that uses Bonjour and uses HTTP commands. So far I have been able to find the local domain and locate the particular service I'm looking for. I am able to resolve the address of the service, but I don't know how to get something useful out of the address. The address from the NSNetService is a NSData object and I have no idea what to do with it. I need to send commands like GET and PUT. What cocoa classes handle things like this?
I also tried getting input and output streams from the Service, but they seem to be extremely low level streams and I don't know how to properly deal with buffers and all that.
[service getInputStream:&inputStream outputStream:&outputStream]
the NSOutputStream write method takes in a uint8_t buffer which I have no idea how to create.
the NSInputStream read method returns a uint8_t buffer and I don't know how to interpret it.
I am able to communicate with this server using terminal commands. For instance, sending it the command LIST causes it to print out the list of files I am looking for. How do I send and get information like this in Cocoa?
To write data to the output stream, therefore sending it to the server:
NSString * stringToSend = #"Hello World!\n"; //The "\n" lets the receiving method described below function correctly. I don't know if you need it or not.
NSData * dataToSend = [stringToSend dataUsingEncoding:NSUTF8StringEncoding];
if (outputStream) {
int remainingToWrite = [dataToSend length];
void * marker = (void *)[dataToSend bytes];
while (0 < remainingToWrite) {
int actuallyWritten = 0;
actuallyWritten = [outputStream write:marker maxLength:remainingToWrite];
remainingToWrite -= actuallyWritten;
marker += actuallyWritten;
}
}
You can send any data like this, just put it in a NSData object.
To receive data from the server use this code in the input stream's NSStreamDelegate:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)streamEvent {
NSInputStream * istream;
NSOutputStream * ostream;
switch(streamEvent) {
case NSStreamEventHasBytesAvailable:;
istream = (NSInputStream *)aStream;
ostream = (NSOutputStream *)CFDictionaryGetValue(connections, istream);
uint8_t buffer[2048];
int actuallyRead = [istream read:(uint8_t *)buffer maxLength:2048];
if (actuallyRead > 0) {
NSData *data;
data = [NSData dataWithBytes:buffer length:actuallyRead];
NSString *string = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]autorelease];
string = [string stringByReplacingOccurrencesOfString:#"\n" withString:#""];
//Do something with the string...
}
break;
case NSStreamEventEndEncountered:;
istream = (NSInputStream *)aStream;
ostream = nil;
if (CFDictionaryGetValueIfPresent(connections, istream, (const void **)&ostream)) {
[self shutdownInputStream:istream outputStream:ostream];
}
break;
case NSStreamEventHasSpaceAvailable:
case NSStreamEventErrorOccurred:
case NSStreamEventOpenCompleted:
case NSStreamEventNone:
default:
break;
}
}
Take a look at Apple's CocoaEcho Sample Code. It should help you.

How to use CFNetwork to get byte array from sockets?

I'm working in a project for the iPad, it is a small program and I need it to communicate with another software that runs on windows and act like a server; so the application that I'm creating for the iPad will be the client.
I'm using CFNetwork to do sockets communication, this is the way I'm establishing the connection:
char ip[] = "192.168.0.244";
NSString *ipAddress = [[NSString alloc] initWithCString:ip];
/* Build our socket context; this ties an instance of self to the socket */
CFSocketContext CTX = { 0, self, NULL, NULL, NULL };
/* Create the server socket as a TCP IPv4 socket and set a callback */
/* for calls to the socket's lower-level connect() function */
TCPClient = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketDataCallBack, (CFSocketCallBack)ConnectCallBack, &CTX);
if (TCPClient == NULL)
return;
/* Set the port and address we want to listen on */
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr([ipAddress UTF8String]);
CFDataRef connectAddr = CFDataCreate(NULL, (unsigned char *)&addr, sizeof(addr));
CFSocketConnectToAddress(TCPClient, connectAddr, -1);
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, TCPClient, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
CFRelease(sourceRef);
CFRunLoopRun();
And this is the way I sent the data, which basically is a byte array
/* The native socket, used for various operations */
// TCPClient is a CFSocketRef member variable
CFSocketNativeHandle sock = CFSocketGetNative(TCPClient);
Byte byteData[3];
byteData[0] = 0;
byteData[1] = 4;
byteData[2] = 0;
send(sock, byteData, strlen(byteData)+1, 0);
Finally, as you may have noticed, when I create the server socket, I registered a callback for the kCFSocketDataCallBack type, this is the code.
void ConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
// SocketsViewController is the class that contains all the methods
SocketsViewController *obj = (SocketsViewController*)info;
UInt8 *unsignedData = (UInt8 *) CFDataGetBytePtr(data);
char *value = (char*)unsignedData;
NSString *text = [[NSString alloc]initWithCString:value length:strlen(value)];
[obj writeToTextView:text];
[text release];
}
Actually, this callback is being invoked in my code, the problem is that I don't know how can I get the data that the windows client sent me, I'm expecting to receive an array of bytes, but I don't know how can I get those bytes from the data param.
If anyone can help me to find a way to do this, or maybe me point me to another way to get the data from the server in my client application I would really appreciate it.
Thanks.
In your callback, the data parameter is indeed a CFDataRef value for the kCFSocketDataCallBack callback type.
CFDataRef dataRef = (CFDataRef) data;
Byte *array = new Byte[CFDataGetLength(dataRef)]; // Or use a fixed length
CFDataGetBytes(dataRef, CFRangeMake(0, CFDataGetLength(theData)), array);
The array will then contains the array of byte.