In my app I'm using GCDAsyncSocket and I write and read in this way:
NSData *bufferWriteData = [NSData dataWithBytesNoCopy:bufferWrite length:17 freeWhenDone:YES];
[self.socket writeData:bufferWriteData withTimeout:-1 tag:1];
[self.socket readDataWithTimeout:-1 tag:1];
after I read in the delegate method the data:
- (void) socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
NSLog(#"did read data");
if (tag == 1){
//analyze my data...
[self.socket readDataWithTimeout:-1 tag:1];
}
inside this delegate method I recall the "readDataWithTimeout" so in this way I read all data.
The problem id when I do a new call, if I do:
[self.socket writeData:bufferWriteData withTimeout:-1 tag:2];
[self.socket readDataWithTimeout:-1 tag:2];
I start a new write and a new read with tag = 2;
When the delegate method "didReadData" is called my code enter inside the block of tag = 1; it seems that it don't recognize the new tag.
Why it happen?
There are several ways to handle this. In your case, the best way probably just used a terminator to indicate the end of that data segment. You can read up there: TCP is a stream.
NSData *MyCommandTerminator(void)
{
return [NSData dataWithBytes:"\x0D\x0A\x0B\x0A" length:4];
}
//callback from Asyncsocket for incoming data
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag //sender and receiver
{
NSLog(#"GCDAsyncSocket didreaddata");
// do what you wish with data and read again using a terminator
[sock readDataToData:MyCommandTerminator() withTimeout:-1 tag:1]; //tag is not being used here
}
// your method to send data
-(void)sendData:(NSData *)data
{
// NSData *bufferWriteData = [NSData dataWithBytesNoCopy:bufferWrite length:17 freeWhenDone:YES];
NSLog(#"Sendata wehere _peerSocket is your instance of GCDAsyncSocket");
NSMutableData *myData = [[NSMutableData alloc] initWithData:data];
[myData appendData:MyCommandTerminator()];
[_peerSocket writeData:myData withTimeout:-1 tag:2]; // tag is not being used here
}
Related
I am trying to retrieve a Facebook profile picture, however I am having trouble being able to check when the image has been downloaded?
First I create a variable.
#property (strong, nonatomic) NSMutableData *imageData;
Than I start the connection.
-(void)getUserPicture {
//Grab user profile picture
imageData = [[NSMutableData alloc] init]; // the image will be loaded in here
NSString *urlString = [NSString stringWithFormat:#"http://graph.facebook.com/%#/picture?type=large", userId];
NSMutableURLRequest *urlRequest =
[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest
delegate:self];
if (!urlConnection) NSLog(#"Failed to download picture");
}
After that I try to check when it is done so I can upload the file to my backend, however my problem is connectionDidFinishLoading calls almost instantly before the image has downloaded.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
imageData = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[imageData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
userPicture = [UIImage imageWithData:imageData];
NSLog(#"%#",userPicture); //this returns null :(
}
The weird thing is if I call this method twice, the NSLog doesn't return null, it actually returns the photo. So why is connectionDidFinishedLoading calling before the image has downloaded from Facebook?
The problem is almost certainly neither NSURLConnection nor the Facebook API, but rather how you're calling it. But, your question doesn't include enough information for us to diagnose it.
So, first, expand your methods to include more diagnostic information, for example:
// check the response header when we receive the response
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
imageData = [NSMutableData data];
// if `statusCode` is not 200, show us what it was ...
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
int statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode != 200) {
NSLog(#"Status code was %ld, but should be 200.", (long)statusCode);
NSLog(#"response = %#", response);
}
}
}
// make sure to detect and report any errors
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"didFailWithError: %#", error);
}
// when you're done, if we fail to create `UIImage`, then it obviously
// wasn't an image, so let's see what it was.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
userPicture = [UIImage imageWithData:imageData];
// if not an image, then ...
if (!userPicture) {
NSString *responseString = [[NSString alloc] initWithData:imageData encoding:NSUTF8StringEncoding];
if (responseString) {
// if it was a string, show it to us ...
NSLog(#"did not receive image: responseString = %#", responseString);
} else {
// if neither a string nor image data, then what was it ...
NSLog(#"did not receive image: imageData = %#", imageData);
}
}
// By the way, I'd expect to see you do something here to update the UI with the image;
// all of these delegate methods were called asynchronously, so you have
// to do something here that triggers the update of the UI, e.g.
//
// self.imageView.image = userPicture
}
By the way, I typed the above without the benefit of Xcode's syntax checking and the like, so don't be surprised if there are some errors there. But worry less about the actual code and focus on the the three diagnostic pillars this illustrates: 1. Look at the response headers and make sure they're ok, not reporting some non-200 status code; 2. Implement delegate that will report networking errors; and 3. If image conversion failed, then you obviously didn't receive an image, so stop and figure out what you actually received. (Often if the server had trouble fulfilling your request, the response is actually HTML or something like that which tells you why it had problems. If you don't look at it, you're flying blind.)
Second, you can watch the network connection by using Charles (or something like that). Run the app on the simulator and then watch the network connection as the app runs.
Third, if you're still having problems, create a MCVE. Namely, we don't want to see all of your code, but you should instead create the simplest possible example that manifests the problem you describe. Don't ask us to pour through tons of code, but rather make it as absolutely bare-bones as possible.
So I'm not sure why connectionDidFinishLoading is getting called instantly after you set the connection, but I may be able to help you work around the issue.
Try this:
-(UIImage *) getImageFromURL:(NSString *)fileURL {
UIImage * result;
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
result = [UIImage imageWithData:data];
return result;
}
Where fileURL is the a string with the url.
If you want to perform an action after the request is sent try this instead:
-(UIImage *) getImageFromURL:(NSString *)fileURL {
UIImage * result;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]];
result = [UIImage imageWithData:data];
return result;
dispatch_async(dispatch_get_main_queue(), ^{
//code for operation after download
});
});
}
Let me know how it goes
I'm trying to send a message to a server and receive a response using my iPhone. I can connect to the server using:
telnet 123.123.123.1 6000
Trying 123.123.123.1...
Connected to 123.123.123.1.
Escape character is '^]'.
?VERSION
OK
VERSION=PROTOCOL: 1.1.0
?VERSION is my question
OK states it received and understood the question
VERSION= is the response from the server
so I'm trying to do the same thing but with xcode
So I have this in my viewDidLoad
dispatch_queue_t mainQueue = dispatch_get_main_queue();
asyncSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:mainQueue];
asyncSocket.delegate = self;
NSString *host = #"123.123.123.1";
uint16_t port = 6000;
NSLog(#"Connecting to \"%#\" on port %hu...", host, port);
NSError *error = nil;
if (![asyncSocket connectToHost:host onPort:port withTimeout:5.0 error:&error])
{
NSLog(#"Error connecting: %#", error);
}
else
{
NSLog(#"Connecting...");
}
And I have the following code showing it connected
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:
(UInt16)port
{
NSLog(#"socket:%p didConnectToHost:%# port:%hu", sock, host, port);
// We're just going to send a test string to the server.
NSString *myStr = #"?VERSION";
NSData *myData2 = [myStr dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:myData2 withTimeout:-1 tag:0];
}
And the following to show it wrote
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
NSLog(#"WRITING");
[asyncSocket readDataToData:[GCDAsyncSocket LFData] withTimeout:-1 tag:0];
}
Sadly this never gets called
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *tempString = [[NSString alloc]initWithData:data
encoding: NSUTF8StringEncoding];
}
I'm lost and really need some assistance
It is because you say write AND read at the same time. First call [asyncSocket writeData:myData2 withTimeout:-1 tag:0]; and in didWriteDataWithTag call [asyncSocket readDataToData:[GCDAsyncSocket LFData] withTimeout:-1 tag:0];. You are on one thread - dispatch_get_main_queue() - it can't do two things at one time.
I'm trying to get data from a website to display it inside a table view
My code:
-(void)loadTutorials {
NSURL *url = [NSURL URLWithString:[#"http://www.example.com/search?q=" stringByAppendingString:self.searchString]];
NSURLRequest *UrlString = [[NSURLRequest alloc] initWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:UrlString
delegate:self];
[connection start];
NSLog(#"Started");
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
TFHpple *tutorialsParser = [TFHpple hppleWithHTMLData:data];
NSString *tutorialsXpathQueryString = #"//div[#id='header']/div[#class='window']/div[#class='item']/div[#class='title']/a";
NSArray *tutorialsNodes = [tutorialsParser searchWithXPathQuery:tutorialsXpathQueryString];
NSMutableArray *newTutorials = [[NSMutableArray alloc] init];
for (TFHppleElement *element in tutorialsNodes) {
Data *tutorial = [[Data alloc] initWithTitle: [[element firstChild] content]
Url: [#"http://www.example.com" stringByAppendingString: [element objectForKey:#"href"]]
];
[newTutorials addObject:tutorial];
}
_objects = newTutorials;
[self.tableView reloadData];
}
but the data is not showing up, is the data not finished loading?
I got it to working without NSURLConnection but then it will stop the program until the data is recieved
According to NSURLConnectionDataDelegate
connection:didReceiveData:
is called in a incrementally manner.
The newly available data. The delegate should concatenate the contents
of each data object delivered to build up the complete data for a URL
load.
So this means you should append new data within this method.
Then, in
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
you should manipulate your data.
So, for example
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
// Create space for containing incoming data
// This method may be called more than once if you're getting a multi-part mime
// message and will be called once there's enough date to create the response object
// Hence do a check if _responseData already there
_responseData = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Append the new data
[_responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Parse the stuff in your instance variable now
}
Obviously you should also implement the delegate responsible for error handling.
A simple note is the following. If data is too big and you need to do some computations stuff (e.g. parsing), you could block the UI. So, you could move the parsing in a different thread (GCD is your friend). Then when finished you could reload the table in the main thread.
Take a look also here for further info: NSURLConnectionDataDelegate order of functions.
I tried to connect to the server and send some information to the server(like username, password..), and the server send me back the ID (string type). The problem is I can not get the ID. Could anyone help me? I am beginner in IOS coding. Thanks.
Here is the codes:
After I click the button, it will call my own function to get serverIP which is a string and Port which is a int.
Then that function will call this function to connect the server:
(void)logInCheck {
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
uint16_t port = serverPort;
if (![asyncSocket connectToHost:serverIP onPort:port error:&error])
{
DDLogError(#"Unable to connect to due to invalid configuration: %#", error);
}
else
{
DDLogVerbose(#"Connecting...");
[self passDataToServer];
}
}
//DataPassToServer is a NSString that hold my data
(void)passDataToServer
{
NSData *requestData = [DataPassToServer dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:requestData withTimeout:-1.0 tag:0];
[asyncSocket readDataWithTimeout:-1 tag:0];
}
//this function call successfully
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
DDLogVerbose(#"socket:didConnectToHost:%# port:%hu", host, port);
}
//this function call successfully
(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
DDLogVerbose(#"socket:didWriteDataWithTag:");
}
//This function does not run !!! Nothing print out.
(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
DDLogVerbose(#"socket:didReadData:withTag:");
NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"ID = %#",response);
[asyncSocket readDataWithTimeout:-1 tag:0];
}
I don't know about your server implementation but, most implementations would read up to the first newline character before processing the request.
So make sure that your [DataPassToServer dataUsingEncoding:NSUTF8StringEncoding] includes a newline character ("\n") at the end.
Your code looks fine and works for me.
I really need some help with my project...
I need to exchange data with my server written in Java. I tried using GCDAsyncSocket, and I can send message to server, read it on server, but when server sends response to client, I can't (don't know how to) read it on client. Here is part of my code:
- (void) someMethod{
NSError *err = nil;
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
if(![asyncSocket connectToHost:#"localhost" onPort:7777 error:&err]){
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(#"I goofed: %#", err);
}
NSString *requestStr = #"<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><root><service>1</service><type>1</type><userProperties><username>ivo</username></userProperties></root>";
NSData *requestData = [requestStr dataUsingEncoding:NSUTF8StringEncoding];
[asyncSocket writeData:requestData withTimeout:-1.0 tag:0];
[asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:1.0 tag:0];
[asyncSocket disconnectAfterWriting];
}
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{
if (tag == 0)
NSLog(#"First request sent");
else if (tag == 2)
NSLog(#"Second request sent");
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",str);
}
Please help, if there is another way I am willing to try as I am getting desperate...
I see that you're sending XML, with no particular terminator at the end of your request data, yet you're expecting the server to send a response terminated by a \r\n?
What does the protocol specify?
Sending and receiving data over tcp is a common cause of confusion because tcp is stream based. It has no concept of individual reads/writes. It treats all data as conceptually a never ending stream. The protocol dictates message boundaries. For a better explanation, see the "Common Pitfalls" article from GCDAsyncSocket's wiki:
https://github.com/robbiehanson/CocoaAsyncSocket/wiki/CommonPitfalls
I think it will help explain a lot.