NSInputStream send NSStreamEventEndEncountered after Facebook connexion - ios

I use SocketIO project on my iOS app to connect to my node.js server and everything works great until I choose to connect to facebook. When I connect to Facebook I send some data to my server and he answer with some data like "user already connected, user created in database" etc. And after that, my NSInputStream seems to be at 0 and so my connexion is closed. I don't know what to do, I spent 14 hours on that and still get this issue...
Can you help me with ?
Edit: Looks like the problem is in SRWebSocket.m line 1462
For more details I added some log :
case NSStreamEventHasBytesAvailable: {
SRFastLog(#"NSStreamEventHasBytesAvailable %#", aStream);
const int bufferSize = 2048;
uint8_t buffer[bufferSize];
while (_inputStream.hasBytesAvailable) {
int bytes_read = [_inputStream read:buffer maxLength:bufferSize];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"BYTES_READ = %i", bytes_read);
});
if (bytes_read > 0) {
[_readBuffer appendBytes:buffer length:bytes_read];
} else if (bytes_read < 0) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"ERROR = %#", _inputStream.streamError);
});
[self _failWithError:_inputStream.streamError];
}
if (bytes_read != bufferSize) {
break;
}
};
[self _pumpScanner];
break;
}
And the result :
onData 5:::{"name":"initalLogin","args":[{"guid":"af78bdf0-f17d-ede2-7dd6-22708d1330f7","usedWithFaceBook":true}]}
start/reset timeout
CURRRENT SIZE = 0
READ BUFFER LENTGH = 373
event
CURRRENT SIZE = 0
BYTES_READ = 0
NSStreamEventEndEncountered <__NSCFInputStream: 0xab21660>
Reason = Stream end encountered
onDisconnect()
DECONNEXION = The operation couldn’t be completed. (SocketIOError error -4.)

Related

Pthread semaphore TCP

I wanna create 2 threads in client, each thread will send a message to server and get rebounce or download a file from server, using TCP protocole. This program run very well when I didn't add pthread in it. After I created 2 threads in client, It doesn't communicate with server. Normally, thread will tell server which operation it wants, then server respond, but when one thread send the message, there is no respond from server, and this thread exit immediately, next thread occupy the semaphore but exit without chose the operation.
Code
void semaphore()
{
int nThread = 2;
int nSemaphore = 1;
int nRet = -1;
pthread_t threadIDs[nThread];
nRet = sem_init(&sem, 0, nSemaphore);
if(nRet == -1)
{
perror("Semaphore intialization failed!!!\n");
exit(EXIT_FAILURE);
}
int i;
for (i = 0; i < nThread; ++i)
{
nRet = pthread_create(&threadIDs[i], NULL, thread, NULL);
if(nRet != 0)
{
perror("pthreas_create failed!!!\n");
exit(EXIT_FAILURE);
}
}
for(i = 0; i < nThread; ++i)
{
nRet = pthread_join(threadIDs[i], NULL);
if(nRet != 0)
{
printf("Threan %d join failed!!!\n", i);
exit(EXIT_FAILURE);
}
}
sem_destroy(&sem);
}
enter code here
void *thread(void* p)
{
pthread_t id = pthread_self();
sem_wait(&sem);
pthread_mutex_lock(&mutex);
int operation ;
//向服务器(特定的IP和端口)发起请求
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1234); //端口
printf("\nThis is client %ld", id);
printf("\nWhich operation do you want:\n");
printf("1:Sending message to server and get rebound\n");
printf("2:Downlown a file from server\n");
scanf("Your chose:%d", &operation);
switch(operation)
{
case 1:
{
sendMessage(serv_addr);
break;
}
case 2:
{
download(serv_addr);
break;
}
default:
break;
}
sem_post(&sem);
pthread_mutex_unlock(&mutex);
}
From terminal
This is client 139671599236864
Which operation do you want:
1:Sending message to server and get rebound
2:Downlown a file from server
Your choise:1
This is client 139671590844160
Which operation do you want:
1:Sending message to server and get rebound
2:Downlown a file from server
Can someone tell me where is the problem?

Is it possible to activate TCP keepalive on Apple iOS devices

Apple device === Router === WiFi module
Apple device(iPhone) is connecting to WiFi module port 2000 with TCP connection. I want to activate TCP keepalive packet sending on Apple device to find out when TCP connection to WiFi module is lost (module is switched off).
My stream setup
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)CFBridgingRetain(moduleIPaddress), port2000, &readStream, &writeStream);
outputStream = (NSOutputStream *)CFBridgingRelease(writeStream);
inputStream = (NSInputStream *)CFBridgingRelease(readStream);
[outputStream setDelegate:(id)self];
[inputStream setDelegate:(id)self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream open];
[inputStream open];
I tried to activate keepalive according to David H post Keeping socket connection alive in iOS
- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
switch (streamEvent) {
case NSStreamEventOpenCompleted:
if (theStream == outputStream) {
/*
CFDataRef data = (CFDataRef)CFWriteStreamCopyProperty((__bridge CFWriteStreamRef)theStream, kCFStreamPropertySocketNativeHandle);
if(data) {
CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)CFDataGetBytePtr(data);
CFRelease(data);
NSLog(#"SOCK HANDLE: %x", socket_handle);
//Enabling keep alive
int opt = 1;
if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 )
{
NSLog(#"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno));
}
}
*/
NSData *data = (NSData *)[theStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle];
if(data) {
CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes];
NSLog(#"SOCK HANDLE: %x", socket_handle);
//Enabling keep alive
int opt = 1;
if( setsockopt( socket_handle, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) ) < 0 )
{
NSLog(#"Yikes 2: failed to set keepalive! ERRNO: %s", strerror(errno));
}
}
}
Both options print out SOCK HANDLE: 9 , no error messages. When WiFi module is switched off connection still stays open for 30 minutes or more when I do not send data to outputstream. If I send data to outputstream I get NSStreamEventErrorOccurred - Error Domain=NSPOSIXErrorDomain Code=60 "The operation couldn’t be completed. Operation timed out" after around 60 seconds. I tried this with Apple device. When I tried with iOS Simulator I did not see keepalive packets with Wireshark.
NSStream tcp keepalive in iOS also describes keepalive setup. Martin R example code activates keepalive for inputstream that seems wrong.
Is it possible to activate TCP keepalive on Apple iOS devices like iPhone (should be according to David H)? If it is possible how it should be done (what is missing from my code)?
Thank you rintaro for directing me to right direction.
Stream setup stayed the same as in my question.
I tested different setups and did not find difference between socket handle detection examples described in my question.
Code that activates keepalive with iPod device and iOS 7.1
case NSStreamEventOpenCompleted:
#try {
if (theStream == outputStream)
{
NSData *data = (NSData *)[theStream propertyForKey:(__bridge NSString *)kCFStreamPropertySocketNativeHandle];
if(data)
{
CFSocketNativeHandle socket_handle = *(CFSocketNativeHandle *)[data bytes];
//NSLog(#"SOCK HANDLE: %x", socket_handle);
//SO_KEEPALIVE option to activate
int option = 1;
//TCP_NODELAY option to activate
int option2 = 1;
//Idle time used when SO_KEEPALIVE is enabled. Sets how long connection must be idle before keepalive is sent
int keepaliveIdle = 10;
//Interval between keepalives when there is no reply. Not same as idle time
int keepaliveIntvl = 2;
//Number of keepalives before close (including first keepalive packet)
int keepaliveCount = 4;
//Time after which tcp packet retransmissions will be stopped and the connection will be dropped.Stream is closed
int retransmissionTimeout = 5;
if (setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof (int)) == -1)
{
NSLog(#"setsockopt SO_KEEPALIVE failed: %s", strerror(errno));
}else
{
NSLog(#"setsockopt SO_KEEPALIVE ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPCNT, &keepaliveCount, sizeof(int)) == -1)
{
NSLog(#"setsockopt TCP_KEEPCNT failed: %s", strerror(errno));
}else
{
NSLog(#"setsockopt TCP_KEEPCNT ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &keepaliveIdle, sizeof(int)) == -1)
{
NSLog(#"setsockopt TCP_KEEPALIVE failed: %s", strerror(errno));
}else
{
NSLog(#"setsockopt TCP_KEEPALIVE ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPINTVL, &keepaliveIntvl, sizeof(int)) == -1)
{
NSLog(#"setsockopt TCP_KEEPINTVL failed: %s", strerror(errno));
}else
{
NSLog(#"setsockopt TCP_KEEPINTVL ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_RXT_CONNDROPTIME, &retransmissionTimeout, sizeof(int)) == -1)
{
NSLog(#"setsockopt TCP_RXT_CONNDROPTIME failed: %s", strerror(errno));
}else
{
NSLog(#"setsockopt TCP_RXT_CONNDROPTIME ok");
}
if (setsockopt(socket_handle, IPPROTO_TCP, TCP_NODELAY, &option2, sizeof(int)) == -1)
{
NSLog(#"setsockopt TCP_NODELAY failed: %s", strerror(errno));
}else
{
NSLog(#"setsockopt TCP_NODELAY ok");
}
}
}
When TCP connection is idle, app starts to send keepalive after 10 second intervals. When there is no answer, app starts to send keepalive packets with 2 second interval and closes stream when there is 4 keepalive packets without reply. This means that when WiFi module is turned off after successful keepalive exchange it takes maximum 18 seconds on idle to close stream when connection is lost.
Another parameter is retransmission timeout value. Default seems to be around 6 seconds. This timer starts when there is first packet retransmission. App tries to retransmit packet but if packet retransmission fails during 5 seconds stream is closed.
NB! Different results in device and simulator.
iPod settings activation log
setsockopt SO_KEEPALIVE ok
setsockopt TCP_KEEPCNT ok
setsockopt TCP_KEEPALIVE ok
setsockopt TCP_KEEPINTVL ok
setsockopt TCP_RXT_CONNDROPTIME ok
setsockopt TCP_NODELAY ok
Simulator settings activation log
setsockopt SO_KEEPALIVE ok
setsockopt TCP_KEEPCNT failed: Protocol not available
setsockopt TCP_KEEPALIVE ok
setsockopt TCP_KEEPINTVL failed: Protocol not available
setsockopt TCP_RXT_CONNDROPTIME ok
setsockopt TCP_NODELAY ok
This means that it is not possible to activate and trace with iOS simulator.
I did not find why error message "Protocol not available" is displayed in simulator.
see: http://en.wikipedia.org/wiki/Keepalive#TCP_keepalive
Usually, keepalive time (net.inet.tcp.keepidle) is 7200sec by default.
I don't know that is true in iOS or not, but "no less than 2 hours" is clearly required by RFC 1122.
This interval MUST be
configurable and MUST default to no less than two hours.
So, how could you configure it in your application? try:
#import <sys/types.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet/tcp.h>
int on = 1;
int delay = 120;
setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
setsockopt(socket_handle, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay));
see man tcp.
I confirmed this works on iOS Simulator (iPhone 5s / iOS 8.0).

Can't read custom file from NSInputStream

I am trying to read a custom file from my documents directory via NSInputStream to upload it to a FTP server. I'm using the exact same code as demonstrated in the SimpleFTPSample provided by Apple. It seems to work fine as long as the file is empty, but as soon as it contains data it fails.
Here's my code. This is how I create the input stream, I tried it both ways:
//Approach one: Init with path
self.fileStream = [NSInputStream inputStreamWithFileAtPath:filePath];
//Approach two: Init with data
NSData* fileData = [NSData dataWithContentsOfFile:filePath];
self.fileStream = [NSInputStream inputStreamWithData:fileData];
If I init the stream with data, I get EXC_BAD_ACCESS (code=1, address=0x0) when invoking read on the stream (code snipped below), if I use the path it jumps right to File read error.
filePath is #"/var/mobile/Applications/94292A2A-37FC-4D8E-BDA6-A26963932AE6/Documents/1395576645.cpn", NSData returns properly and has 806 bytes.
That's part of the stream:handleEvent: delegate method:
if (self.bufferOffset == self.bufferLimit) {
NSInteger bytesRead;
bytesRead = [self.fileStream read: self.buffer maxLength: kSendBufferSize];
if (bytesRead == -1) {
if (kPrintsToConsole) NSLog(#"File read error");
} else if (bytesRead == 0) {
[self stopSending];
} else {
self.bufferOffset = 0;
self.bufferLimit = bytesRead;
}
}
I'm kinda stuck. Hope you guys can help me out. Running iOS 7.1 & Xcode 5.1
you need to call [self.fileStream open] before read. For both file and data approaches.

exc_bad_access while writing to output stream with cfnetwork

I am working on an iPhone application. I am trying to send message with one URL with smtp via the gmail server. I use the CFNetwork framework. Sometimes mail is sent without problem , but many times I get an exception exc_bad_access at the line exc_bad_access if
(CFWriteStreamCanAcceptBytes(outputStream))
1 Class : HSK_CFUtilities
CFIndex CFWriteStreamWriteFully(CFWriteStreamRef outputStream, const uint8_t* buffer, CFIndex length)
{
CFIndex bufferOffset = 0;
CFIndex bytesWritten;
while (bufferOffset < length)
{
if (CFWriteStreamCanAcceptBytes(outputStream))
{
bytesWritten = CFWriteStreamWrite(outputStream, &(buffer[bufferOffset]), length - bufferOffset);
if (bytesWritten < 0)
{
// Bail!
return bytesWritten;
}
bufferOffset += bytesWritten;
}
else if (CFWriteStreamGetStatus(outputStream) == kCFStreamStatusError)
{
return -1;
}
else
{
// Pump the runloop
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, true);
}
}
return bufferOffset;
}
2 Class : SKPSMTPMessage in method parseBuffer
case kSKPSMTPWaitingSendSuccess:
{
if ([tmpLine hasPrefix:#"250 "])
{
sendState = kSKPSMTPWaitingQuitReply;
NSString *quitString = #"QUIT\r\n";
DEBUGLOG(#"C: %#", quitString);
if (CFWriteStreamWriteFully((CFWriteStreamRef)outputStream, (const uint8_t *)[quitString UTF8String], [quitString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) < 0)
{
error = [outputStream streamError];
encounteredError = YES;
}
else
{
[self startShortWatchdog];
}
}
I was wondering if you could give me a hint with it? I would appreciate any help . Thank you in advance, Best regards.
Because it was EXC_BAD_ACCESS error so
In my Case when I made strong property of SKPSMTPMessage in needed .h class & used
SKPSMTPMessage object as global for needed class, It worked.
Here's a great link on what causes EXC_BAD_ACCESS and how to track down the root problem:
Lou Franco's Understanding EXC_BAD_ACCESS

Problem with wsarecv while using with IOCP

I am new to IOCP and struggling with this for last few weeks.
I have pasted some core part of my code below related to IOCP.This may not be executed perfectly as I clipped some part to make it easy to understand.
I am struggling while receiving the data.As it comes to wsarecv in worket thread, wsarecv returns WSA_IO_PENDING error code so I call WSAGetOverlappedResult to check operation to be completed.
Twist comes here, rather it proceed and call my local function ProcessTelegramData after WSAGetOverlappedResult , same part of code(wsarecv called again) is executed again by another worker thread which try to call ProcessTelegramData and buffer value is invalid in it.
I am unable to understand
why another thread calling wsarecv again when WSAGetOverlappedResult is called and
why buffer value is getting invalidated?
unsigned LicTCPServer::WorkerThread(LPVOID lpParam)
{
//int nThreadNo = (int)lpParam;
LicTCPServer* pThis = reinterpret_cast<LicTCPServer*>(lpParam);
void *lpContext = NULL;
OVERLAPPED *pOverlapped = NULL;
CClientContext *pClientContext = NULL;
DWORD dwBytesTransfered = 0;
int nBytesRecv = 0;
int nBytesSent = 0;
DWORD dwBytes = 0, dwFlags = 0;
//Worker thread will be around to process requests, until a Shutdown event is not Signaled.
while (WAIT_OBJECT_0 != WaitForSingleObject(g_hShutdownEvent, 0))
{
BOOL bReturn = GetQueuedCompletionStatus(
g_hIOCompletionPort,
&dwBytesTransfered,
(LPDWORD)&lpContext,
&pOverlapped,
INFINITE);
if (NULL == lpContext)
{
//We are shutting down
break;
}
//Get the client context
pClientContext = (CClientContext *)lpContext;
if ((FALSE == bReturn) /*|| ((TRUE == bReturn) && (0 == dwBytesTransfered))*/)
{
//Client connection gone, remove it.
pThis->RemoveFromClientListAndFreeMemory(pClientContext);
continue;
}
WSABUF *p_wbuf = pClientContext->GetWSABUFPtr();
OVERLAPPED *p_ol = pClientContext->GetOVERLAPPEDPtr();
//printf("reached %d\n",pClientContext->GetOpCode());
printf("Get Queued received\n");
switch (pClientContext->GetOpCode())
{
case OP_READ:
{
//Once the data is successfully received, we will print it.
//pClientContext->SetOpCode(OP_WRITE);
pClientContext->ResetWSABUF();
dwFlags = 0;
//int a = recv(pClientContext->GetSocket(), p_wbuf->buf, p_wbuf->len, 0);
//Get the data.
if(WSARecv(pClientContext->GetSocket(), p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
printf("Error occured at WSARecv()\n");
return 0;
}
}
DWORD byteTr = 0;
WSAGetOverlappedResult(pClientContext->GetSocket(),p_ol,&byteTr,TRUE,&dwFlags);
if( byteTr > 0 )
{
//doing some operatin on data received
printf("Process tele called\n");
g_pLicServFunc->ProcessTelegramData(pClientContext->GetSocket(), p_wbuf->buf, byteTr);
}
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
{
//WriteToConsole("\nThread %d: Error occurred while executing WSARecv().", nThreadNo);
//Let's not work with this client
//TBC
//RemoveFromClientListAndFreeMemory(pClientContext);
}
}
break;
case OP_WRITE:
char szBuffer[MAX_BUFFER_LEN];
//Send the message back to the client.
pClientContext->SetOpCode(OP_READ);
pClientContext->SetTotalBytes(dwBytesTransfered);
pClientContext->SetSentBytes(0);
//p_wbuf->len = dwBytesTransfered;
dwFlags = 0;
DWORD temp;
//Overlapped send
printf("reached Going to send\n");
//send(pClientContext->GetSocket(), p_wbuf->buf,p_wbuf->len, 0);
nBytesSent = WSASend(pClientContext->GetSocket(), p_wbuf, 1,
&temp, dwFlags, p_ol, NULL);
if ((SOCKET_ERROR == nBytesSent) && (WSA_IO_PENDING != WSAGetLastError()))
{
//WriteToConsole("\nThread %d: Error occurred while executing WSASend().", nThreadNo);
//Let's not work with this client
//TBC
//RemoveFromClientListAndFreeMemory(pClientContext);
}
break;
default:
printf("reached to default\n");
//We should never be reaching here, under normal circumstances.
break;
} // switch
} // while
return 0;
}
I had a similar issue with WSARecv where it always queued to the io completion queue, even if it succeeded immediately. I had to ignore a return value indicating success and instead handle the queued result that I got from GetQueuedCompletionStatus.

Resources