We have following code for connecting to our server. This is part of iPhone App.
Problem is that recv(CFSocketGetNative(inSocketRef), &length, sizeof(length), 0);
call returns 0 after exactly 60 sec. We are not sending anything from server. I want it to wait for data or disconnection (either server or client initiated). However, it always returns after 60 sec.
What am I doing wrong here?
void CallbackHandlerConnectionHandler(CFSocketRef inSocketRef, CFSocketCallBackType inType,
CFDataRef inAddress, const void *inData, void *inInfo)
{
ChatServerConnectionHandler *conHandler = (ChatServerConnectionHandler *)inInfo;
if([conHandler respondsToSelector:#selector(dataRecievedFromServer:)])
{
int res = 0;
SInt32 length;
res = recv(CFSocketGetNative(inSocketRef), &length, sizeof(length), MSG_PEEK);
if(0 >= res || (length < 0))
{
//Disconnect the connection, as some has occcured in the sockets..
[conHandler performSelector:#selector(errorEncountered)];
NSLog(#"Error occured in server!!");
return;
}
printf ("good data")
}
}
-(BOOL) connectToServer:(NSString *)ipAddress Port:(int)portNumber
{
connectionState = eConnectionEstablishInProgress;
self.threadStopped = NO;
CFRunLoopSourceRef source;
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *host;
host = gethostbyname([ipAddress cStringUsingEncoding:NSUTF8StringEncoding]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct timeval timeout;
if (sockfd < 0)
{
error("ERROR opening socket");
connectionState = eNoConnection;
return NO;
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portNumber);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
/* Creates a CFSocket object for a pre-existing native socket */
CFSocketContext socketContext={0,self,NULL,NULL,NULL};
socketRef = CFSocketCreateWithNative(kCFAllocatorDefault,
sockfd,
kCFSocketReadCallBack,
CallbackHandlerConnectionHandler,
&socketContext);
source = CFSocketCreateRunLoopSource(NULL, socketRef, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
source = nil;
InstallSignalHandlers();
CFDataRef socketAddress;
socketAddress = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (UInt8 *)&serv_addr, sizeof(serv_addr), kCFAllocatorNull);
CFSocketError result=CFSocketConnectToAddress(socketRef, socketAddress, 0);
CFRelease(socketAddress);
if(kCFSocketSuccess == result)
{
//printf("Socket connection established");
self.serverRunning = YES;
connectionState = eConnectionEstablised;
NSLog(#"\nCall Connection accepted callback.");
if([delegate respondsToSelector:#selector(conectionEstablishedWithTheServer)])
{
[delegate performSelector:#selector(conectionEstablishedWithTheServer)];
}
}
else
{
//printf("Unable to create socket why???***");
connectionState = eNoConnection;
if(socketRef)
CFRelease(socketRef);
socketRef = nil;
return NO;
}
while(FALSE == self.threadStopped)
{
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, FALSE);
}
NSLog(#"\nOut of Server socket connection thread.");
self.serverRunning = NO;
if([delegate respondsToSelector:#selector(conectionFinishedWithServer)])
{
[delegate performSelector:#selector(conectionFinishedWithServer)];
}
if(socketRef)
{
if(CFSocketIsValid(socketRef))
CFSocketInvalidate(socketRef);
}
return YES;
}
This looks like a server problem - or rather functionality: closing inactive connections to keep enough free resources.
If you didn't write the server code try to contact the author.
The packet you mention [FIN, ACK] is a packet that closes your socket: you should check the IP adresses but the server is most probably the initiator of this packet.
Related
I am struggling to get anything to send from the iphone, I followed This Guide At first I started off with cfSocketRef as I thought you used them for UDP and and TCP by changing protocol but I had no luck.
So here is the code for the BSD sockets. Nothing seems to be sending. I have a java socket server waiting on localhost:port.
Any ideas? or maybe a guide/sample xcode project that works.
#import "ViewController.h"
#include <CFNetwork/CFNetwork.h> //temp //dont leave here put it in a header
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#interface ViewController ()
#end
#implementation ViewController
static void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType
type, CFDataRef address, const void *data, void *userInfo)
{
NSLog(#"socketCallback called");
}
//
- (void)viewDidLoad
{
[super viewDidLoad];
int sock = 0; /// ?
unsigned int echolen;
NSLog(#"starting udp testing");
cfSocketRef = CFSocketCreate(/*CFAllocatorRef allocator*/ NULL,
/*SInt32 protocolFamily*/ PF_INET,
/*SInt32 socketType*/ SOCK_DGRAM,
/*SInt32 protocol*/ IPPROTO_UDP,
/*CFOptionFlags callBackTypes*/ kCFSocketAcceptCallBack | kCFSocketDataCallBack,
/*CFSocketCallBack callout*/ (CFSocketCallBack)socketCallback,
/*const CFSocketContext *context*/ NULL);
struct sockaddr_in destination;
memset(&destination, 0, sizeof(struct sockaddr_in));
destination.sin_len = sizeof(struct sockaddr_in);
destination.sin_family = AF_INET;
NSString *ip = #"localhost";
destination.sin_addr.s_addr = inet_addr([ip UTF8String]);
destination.sin_port = htons(33033); //port
NSString *msg = #"message sent from iPhone";
/* server port */
setsockopt(sock,
IPPROTO_IP,
IP_MULTICAST_IF,
&destination,
sizeof(destination));
const char *cmsg = [msg UTF8String];
echolen = strlen(cmsg);
if (sendto(sock,
cmsg,
echolen,
0,
(struct sockaddr *) &destination,
sizeof(destination)) != echolen)
{
NSLog(#"did send");
}
else
{
NSLog(#"did not send");
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
First problem: You forgot to create the socket:
if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
NSLog(#"Failed to create socket, error=%s", strerror(errno));
}
This part does not work:
NSString *ip = #"localhost";
destination.sin_addr.s_addr = inet_addr([ip UTF8String]);
inet_addr converts strings representing IPv4 addresses in the dot notation, such as "127.0.0.1".
To convert a host name such as "localhost" to an IP address, you have to use gethostbyname, or getaddrinfo (the latter works with IPv4 and IPv6).
There is another error when you check the return value of sendto: sendto returns the number of bytes sent in the success case, and (-1) in error case. So it should look like:
if (sendto(sock, ...) == -1) {
NSLog(#"did not send, error=%s",strerror(errno));
} else {
NSLog(#"did send");
}
Checking the value of errno would have revealed your problem quickly. If you forget to create the socket, the error message is
did not send, error=Socket operation on non-socket
Remarks:
cfSocketRef is completely unused in your function.
Why do you set the IP_MULTICAST_IF socket option? As far as I know, that is not needed for unicast messages, only for multicast messages.
I have just started learning to design iphone apps and I am trying to set up a client server environment. As a starter I would like to first make sure that the service I publish is visible to the client. I have written server and client codes wit help from Apple's Docs. However I find that though my service is getting published (netServiceWillPublish(NSNetService *)sender is called), my client is not identifying this service. At the client's side the delegate function netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser is called making me believe that the client is searching for the service. However the didFindService delegate function is never called. i am stumped as to why this is not working. I have followed the sample codes for the client side from various places and my code matches theirs. Some help on this would be greatly appreciated
Also I have the following questions
Can the service name (that comes along with the transport level protocol) be any string? Or does it have to be specified in http://www.dns-sd.org/ServiceTypes.html. Also what purpose does this solve apart from service discovery.
When I print out the details of the NSNetService object at the server's side (After setting up the socket and starting the service) the hostname is printed as (null) and there are no addresses being displayed. technically when a service is opened shouldn't these be set to values? If yes could someone please tell me where I am going wrong in my code?
I have attached the client and server code blocks of my project where the publishing and browsing of servicing is done. Help is extremely appreciated as I am stuck in my work with this issue.
This is the code snippet at the client side that browses for the services
NSNetServiceBrowser *serviceBrowser;
serviceBrowser = [[NSNetServiceBrowser alloc] init];
[serviceBrowser setDelegate:self];
[serviceBrowser searchForServicesOfType:#"_http._tcp." inDomain:#"local."];
This is the code snippet at the server side that publishes the service
BOOL success;
int fd;
int err;
int junk;
struct sockaddr_in addr;
NSInteger port;
port = 0;
fd = socket(AF_INET, SOCK_STREAM, 0);
success = (fd != -1);
if(success) {
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = INADDR_ANY;
err = bind(fd, (const struct sockaddr*) &addr, sizeof(addr));
success = (err == 0);
}
if (success) {
err = listen(fd, 5);
success = (err == 0);
}
if (success) {
socklen_t addrLen;
addrLen = sizeof(addr);
err = getsockname(fd, (struct sockaddr *) &addr, &addrLen);
success = (err == 0);
if (success) {
assert( addrLen = sizeof(addr));
port = ntohs(addr.sin_port);
}
}
if (success) {
CFSocketContext context = { 0, (__bridge void *) self, NULL, NULL, NULL };
assert(self->_listeningSocket == NULL);
self->_listeningSocket = CFSocketCreateWithNative(NULL, fd, kCFSocketAcceptCallBack, AcceptCallBack, &context);
success = (self->_listeningSocket != NULL);
if (success) {
CFRunLoopSourceRef rls;
fd = -1; //listeningSocket is now responsible for closing the socket
rls = CFSocketCreateRunLoopSource(NULL, self.listeningSocket, 0);
assert(rls != NULL);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
}
}
if (success) {
self.netService = [[NSNetService alloc] initWithDomain:#"local." type:#"_http._tcp." name:#"test" port:port];
success = (self.netService != nil);
}
if (success) {
self.netService.delegate = self;
[self.netService publishWithOptions:NSNetServiceNoAutoRename];
}
if (success) {
assert(port != 0);
[self serverDidStartOnPort:port];
}
else {
[self stopServer:#"Start Failed"];
if (fd != -1) {
junk = close(fd);
assert(junk == 0);
}
}
This is the code snippet that tells that the service is published and prints out the details of my NSSocket object
- (void)netServiceWillPublish:(NSNetService *)sender {
NSLog(#"This function is getting called");
NSLog(#"sender.name %#",sender.name);
NSLog(#"sender.addresses %#",sender.addresses);
NSLog(#"sender.domain %#",sender.domain);
NSLog(#"sender.hostname %#",sender.hostName);
NSLog(#"sender.type %#",sender.type);
NSLog(#"sender.port %d",sender.port);
}
Thanks
Vivek
how can i clear an CFStream buffer?
Everytime i read from socket there is still data from an old request, i mean complete response to an old request not just a fragment of it.
Am i missing something ?
This is a function i use to initialize the connection:
-(void)openSocketConnection:(UInt32)port: (NSString *)host
{
NSString *hoststring = [[NSString alloc] initWithString:host];
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,(__bridge CFStringRef)hoststring ,
port,&_nnet_readStream,&_nnet_writeStream);
CFWriteStreamCanAcceptBytes(_nnet_writeStream);
CFWriteStreamSetProperty(_nnet_writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFReadStreamSetProperty(_nnet_readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
if(!CFWriteStreamOpen(_nnet_writeStream)) {
NSLog(#"Error Opening Write Socket");
}
if(!CFReadStreamOpen(_nnet_readStream)) {
NSLog(#"Error Opening Write Socket");
}
}
This is a function i use to read data from socket:
BOOL done = NO;
NSMutableString* result = [NSMutableString string];
while (!done)
{
if (CFReadStreamHasBytesAvailable(_nnet_readStream))
{
UInt8 buf[1024];
CFIndex bytesRead = CFReadStreamRead(_nnet_readStream, buf, 1024);
if (bytesRead < 0)
{
CFStreamError error = CFReadStreamGetError(_nnet_readStream);
NSLog(#"%#",error);
} else if (bytesRead == 0)
{
if (CFReadStreamGetStatus(_nnet_readStream) == kCFStreamStatusAtEnd)
{
done = YES;
}
} else
{
[result appendString:[[NSString alloc] initWithBytes:buf length:bytesRead encoding:NSUTF8StringEncoding]];
}
} else
{
done = YES;
}
}
You seem to assume that when you get data from the host on the other end it will always be available all in one go.
I don't know what the underlying protocol is, but in general it's entirely possible that the remote end will try and send 1600 bytes and that you'll get 1500 bytes but then have to wait a few milliseconds (or even seconds) for the next 100 bytes. Meanwhile CFReadStreamHasBytesAvailable would return false and so your code would set your done flag even though you haven't read all the data the server sent.
I am trying to implement async tcp networking with runloop.
currently I manage to connect, but when I try to send something I get that -1 bytes have been written - but CFWriteStreamCopyError returns null.
code sample below, first function connects, second send a simple message.
any help will be appreciated, including random bug spotting (I am new to objective-c and to iphone development in general).
struct header
{
uint32_t length;
uint32_t type;
} header;
- (void) connect
{
NSLog(#"Attempting to (re)connect to %#:%d", m_host, m_port);
while(TRUE)
{
CFHostRef host = CFHostCreateWithName(kCFAllocatorDefault, (CFStringRef)m_host);
if (!host)
{
NSLog(#"Error resolving host %#", m_host);
[NSThread sleepForTimeInterval:5.0];
continue;
}
CFStreamCreatePairWithSocketToCFHost(kCFAllocatorDefault, host , m_port, &m_in, &m_out);
CFRelease(host);
if (!m_in)
{
NSLog(#"Error");
}
CFStreamClientContext context = {0, self,nil,nil,nil};
if (CFReadStreamSetClient(m_in, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, networkReadEvent, &context))
{
CFReadStreamScheduleWithRunLoop(m_in, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
}
if (CFWriteStreamSetClient(m_out, kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, networkWriteEvent, &context))
{
CFWriteStreamScheduleWithRunLoop(m_out, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
}
BOOL success = CFReadStreamOpen(m_in);
CFErrorRef error = CFReadStreamCopyError(m_in);
if (!success || (error && CFErrorGetCode(error) != 0))
{
NSLog(#"Connect error %s : %d", CFErrorGetDomain(error), CFErrorGetCode(error));
[NSThread sleepForTimeInterval:5.0];
}
else
{
NSLog(#"Connected");
break;
}
}
[self startSession];
}
- (void) startSession
{
struct header hh;
hh.type = RTR_CREATE_SESSION;
hh.length = 0;
CFIndex res = CFWriteStreamWrite(self.m_out, (const UInt8*)&hh, sizeof(hh));
NSLog(#"Written %d", res);
CFErrorRef error = CFWriteStreamCopyError(self.m_out);
if (error)
{
NSLog(#"Read error %s : %d", CFErrorGetDomain(error), CFErrorGetCode(error));
CFRelease(error);
}
}
figured it out, I forgot to open the write stream as well:
CFWriteStreamOpen(m_out);
I have a problem with my socket program.
I create the client program (my code is below)
I have a problem when i close the socket with the disconnect method.
CFSocketRef s;
-(void)CreaConnessione
{
CFSocketError errore;
struct sockaddr_in sin;
CFDataRef address;
CFRunLoopSourceRef source;
CFSocketContext context = { 0, self, NULL, NULL, NULL };
s = CFSocketCreate(
NULL,
PF_INET,
SOCK_STREAM,
IPPROTO_TCP,
kCFSocketDataCallBack,
AcceptDataCallback,
&context);
memset(&sin, 0, sizeof(sin));
int port = [fieldPorta.text intValue];
NSString *tempIp = fieldIndirizzo.text;
const char *ip = [tempIp UTF8String];
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = (long)inet_addr(ip);
address = CFDataCreate(NULL, (UInt8 *)&sin, sizeof(sin));
errore = CFSocketConnectToAddress(s, address, 0);
if(errore == 0){
buttonInvioMess.enabled = TRUE;
fieldMessaggioInvio.enabled = TRUE;
labelTemp.text = [NSString stringWithFormat:#"Connesso al Server"];
CFRelease(address);
source = CFSocketCreateRunLoopSource(NULL, s, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
source,
kCFRunLoopDefaultMode);
CFRelease(source);
CFRunLoopRun();
}
else{
labelTemp.text = [NSString stringWithFormat:#"Errore di connessione. Verificare Ip e Porta"];
switchConnection.on = FALSE;
}
}
//the socket doesn't disconnect
-(void)Disconnetti{
CFSocketInvalidate(s);
CFRelease(s);
}
-(IBAction)Connetti
{
if(switchConnection.on)
[self CreaConnessione];
else
[self Disconnetti];
}
If you're trying to reuse the socket which you have invalidated. I guess you cant use the same socket object again.
because once i remove invalidating socket from my code. the code works pretty good on every single time i try to make connection after disconnecting..