GCDAsyncSocket - Read stream never completes - iphone

I am playing with GCDAsyncSocket (MRC / iOS5.1) for a while, especially with "large" files (5 - 10 mb). Unfortunately sometimes the read stream is never completed (e.g. it gets stuck) just a few bytes at the end of the stream; the didReadPartialDataOfLength: stops giving me information and the didReadData is not fired at all.
Here's some of my code (for both writing / reading examples the connection between the host and client have been established)
WRITING
#define kHeadTag 0
#define kDataTag 1
typedef struct {
NSUInteger size;
} head_t;
-(void)sendData:(NSData *)__data {
_sendData = [__data retain];
head_t header;
header.size = __data.length;
[_socket writeData:[NSData dataWithBytes:&header
length:sizeof(header)]
withTimeout:-1
tag:kHeadTag];
}
-(void)socket:(GCDAsyncSocket *)sock
didWriteDataWithTag:(long)tag {
if (tag == kHeadTag) {
[sock writeData:_sendData withTimeout:-1 tag:kDataTag];
[_sendData release];
_sendData = nil;
}
}
READING
-(void)socket:(GCDAsyncSocket *)sock
didReadData:(NSData *)data
withTag:(long)tag {
switch (tag) {
// ------------------------------------- HEAD
case kHeadTag:{
head_t head;
[data getBytes:&head length:sizeof(head)];
_readHead = head;
NSLog(#"Received header (total size = %i bytes)", head.size);
[sock readDataToLength:head.size
withTimeout:-1
tag:kDataTag];
}
break;
// ------------------------------------- BODY
case kDataTag: {
NSLog(#"Data received with %i bytes", data.length);
[sock readDataToLength:sizeof(head_t) withTimeout:-1 tag:kHeadTag];
}
break;
}
};
-(void)socket:(GCDAsyncSocket *)sock
didReadPartialDataOfLength:(NSUInteger)partialLength
tag:(long)tag {
if (tag == kDataTag) {
NSLog(#"Data read with %i bytes", partialLength);
}
};
I hope this is enough code to see what I'm doing wrong or maybe this is a bad practice for writing/reading large chunks of data.

I added
[self.partnerSocket writeData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];
at the end of sendData: method, and it works fine for me. You just have to append separator chars at the end of the data.
I can transfer around 48MB of file from one iDevice to other iDevice

Related

Can EADemo send http request

Can someone suggest if it is possible to modify the EADemo to send HTTP request (custom header and body). In the project I am working on, the external accessory will be receiving the data using http request.
The method which is for writing on EADemo is
- (void)writeData:(NSData *)data;
Ok, so I found an example which I will be using but instead of GET I will use POST. Below is the example of GET request:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSLog(#"stream:handleEvent: is invoked...");
switch(eventCode) {
case NSStreamEventHasSpaceAvailable:
{
if (stream == oStream) {
NSString * str = [NSString stringWithFormat:
#"GET / HTTP/1.0\r\n\r\n"];
const uint8_t * rawstring =
(const uint8_t *)[str UTF8String];
[oStream write:rawstring maxLength:strlen(rawstring)];
[oStream close];
}
break;
}
// continued ...
}
}
https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Streams/Articles/NetworkStreams.html

CFStream IOS Socket communication

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.

AsyncSocket just read once?

I make simple client server with no CLRFdata, but i give header of message with lenght message self. this is my code :
This method send data
- (void)sendMessage:(NSString *)message {
unsigned char lendata = [message length];
senddata = [NSMutableData dataWithBytes: &lendata length:sizeof(lendata)] ;
[senddata appendData:[message dataUsingEncoding:NSUTF8StringEncoding]];
[socket writeData:senddata withTimeout:-1 tag:0];
}
This method read data from socket lenght = 1 byte until long of code on header.
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *message = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
if(bheader){
cdata++;
if(cdata==2){
[data getBytes:&idata];
bheader = NO;
cdata=0;
cmessage = [[NSString alloc] initWithFormat:#""];
}
}
else{
if (message){
cmessage = [[NSString alloc]initWithFormat:#"%#%#",cmessage,message] ;
}
else
NSLog(#"Error converting received data into UTF-8 String");
cdata++;
if(cdata==idata) {
msgComplete=YES;
}
}
if (msgComplete) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:cmessage forKey:kNotificationMessage];
[notificationCenter postNotificationName:kNotification object:self userInfo:userInfo];
cmessage=#"";
msgComplete=NO;
bheader=YES;
cdata=0;
[cmessage release];
}
[sock readDataToLength:1 withTimeout:-1 tag:0];
}
and this method
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag {
[sock readDataToLength:1 withTimeout:-1 tag:0];
}
I'm succes send data and recv data from server use this code,
i try make login and server got data and notification, and i recv message with this code, but after that i cant recv message from server when i send message again, but on TCPDUMP i see any message sent to server and server response with a message ?
could any one fix my code where the problem ?
Thanks.

iPhone OS 4.0: NSFileHandleDataAvailableNotification not providing callback at file-end

I'm a bit new to iPhone development, so be gentle! I'm supporting an app which loads a wav file from a URL file-stream, and plays it back through an AudioQueue.
We run a continual loop in another thread, and stop the Queue if we detect that it has no buffers in use, and the input FileStream has reached its end. In turn, we detect if the FileStream has ended within the waitForDataInBackgroundAndNotify callback on the NSFileHandleDataAvailableNotification for the stream, by checking whether the availableData has length 0.
This works under iOS 3.0 - we get a notification of 0 available data at the end of the file - but on iOS 4.0, we don't seem to receive the callback at file end. This happens on an OS 4.0 device, regardless of the target OS version.
Has the API changed between the two versions? How can I detect the end of the file now?
Hopefully-relevant code:
data-available callback:
- (void)readFileData:(NSNotification *)notification
{
#try
{
NSData *data = [[notification object] availableData];
if ([data length] == 0 && self.audioQueueState != AQS_END)
{
/***********************************************************************/
/* We've hit the end of the data but it's possible that more may be */
/* appended to the file (if we're still downloading it) so we need to */
/* wait for the availability of more data. */
/***********************************************************************/
[self setFileStreamerState:FSS_END];
[[notification object] waitForDataInBackgroundAndNotify];
}
else if (self.audioQueueState == AQS_END)
{
TRC_DBG(#"ignore read data as ending");
}
else
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
TRC_DBG(#"Read %d bytes", [data length]);
[self setFileStreamerState:FSS_DATA];
if (discontinuous)
{
TRC_DBG(#"AudioFileStreamParseBytes %d bytes, discontinuous", [data length]);
err = AudioFileStreamParseBytes(audioFileStream, [data length], [data bytes], kAudioFileStreamParseFlag_Discontinuity);
discontinuous = NO;
}
else
{
TRC_DBG(#"AudioFileStreamParseBytes %d bytes, continuous", [data length]);
err = AudioFileStreamParseBytes(audioFileStream, [data length], [data bytes], 0);
}
/***********************************************************************/
/* If error then get out, otherwise wait again for more data. */
/***********************************************************************/
if (err != 0)
{
[self failWithErrorCode:AS_FILE_STREAM_PARSE_BYTES_FAILED];
}
else
{
[[notification object] waitForDataInBackgroundAndNotify];
}
[pool release];
}
}
#catch (NSException *exception)
{
TRC_ERR(#"Exception: %#", exception);
TRC_ERR(#"Exception reason: %#", [exception reason]);
//[self failWithErrorCode:AS_FILE_AVAILABLE_DATA_FAILED];
}
}

Have been cursing over data packets for 36 hours now, please help!

Ok Here is the problem. I am trying to send through a structure or anything through the apple bluetooth send network packet code. The code to send is below
-(void)sendMessagePacket:(GKSession *)session packetID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend {
static unsigned char networkPacket[maxPacketSize];
const unsigned int packetHeaderSize = 2 * sizeof(int); // we have two "ints" for our header
if(length < (maxPacketSize - packetHeaderSize)) { // our networkPacket buffer size minus the size of the header info
int *pIntData = (int *)&networkPacket[0];
// header info
pIntData[0] = 10;
pIntData[1] = packetID;
// copy data in after the header
memcpy( &networkPacket[packetHeaderSize], data, length );
NSData *packet = [NSData dataWithBytes: networkPacket length: (length+8)];
if(howtosend == YES) {
[session sendDataToAllPeers:packet withDataMode:GKSendDataReliable error:nil];
} else {
[session sendDataToAllPeers:packet withDataMode:GKSendDataUnreliable error:nil];
}
}
}
If anyone could go through this with a comb and explain what is going on that would be greatly appreciated. Next, the receive code.
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context {
// Caller whenever data is received from the session
unsigned char *incomingPacket = (unsigned char *)[data bytes];
//EXPECTS THAT WHAT IS IN INCOMING PACKET [0] TO BE OF DATA TYPE INTEGER
int *pIntData = (int *)&incomingPacket[0];
int packetTime = pIntData[0];
int packetID = pIntData[1];
static int lastPacketTime = -1;
switch (packetID) {
case NETWORK_COINTOSS:
//DO SOMETHING
break;
case NETWORK_TEXT_EVENT:
NSString *fewMore = (NSString *)&incomingPacket[8];
fewThings *bitMore = &fewMore[peer];
//NSLog(#"Trace Bit More: %#",fewMore);
break;
default:
break;
}
NSInteger lengthBytes = [data length];
NSLog(#"Length of data : %i", lengthBytes);
}
What i am struggling to understnad being a newcome to objective c is how all this works, how do i access the string that i have send as all efforts to log it cause the program to crash.
Below is the code used to start the send:
NSString *bigWord = #"BigWordBigWord";
NSInteger len = [bigWord length];
[self sendMessagePacket:gameSession packetID:NETWORK_TEXT_EVENT withData:bigWord ofLength:(len) reliable:YES];
Any help would be so appreciated. Thank You.
You really need to change the NSString object into either a UTF8 representation to send, or to archive it as an NSData object you send with the full length - you can't just cast an NSString as a C string (either for sending or receiving!!!).
You can get a C string version of NSString using:
const char *sendString = [bigWord cStringUsingEncoding:NSUTF8StringEncoding]
And then get it out of your incoming NSData object with
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
This guy is sending and receiving the string over network just fine. But, I'm also still getting error on the receiving end.
http://discussions.apple.com/thread.jspa?messageID=10215848
At last got it. GKTank is transferring the data using struct, so I placed my string inside the struct and rest is the history.
typedef struct {
NSString *strInfo;
} myInfo;