I'm working on porting a VB program to the PC. It uses serial communication to interact with a physical device. I have a version up and running on the Mac using ORSSerialPort. However, once piece of the VB library that is great is the SerialPort.ReadExisting() function. This essentially reads any messages and discards them.
Has anyone built something similar on the Mac side? I've tried pulling out the ORSSerialPort into a function to directly read values (see below). However, unless I send a message I receive a null response. The readExisting function would be great for a scenario when things get a little out of alignment such as:
I send a message "Message1" to the device and nothing happens (was expecting Response1).
I send a message "Message2" to the device and receive: "Response1" instead of "Response2"
I'd like to detect this, call an equivalent to SerialPort.readExisting() since Response2 is the next to be found if I continue.
My read function:
-(NSString *) directRead
{
// Read Directly
int localPortFD = self.fileDescriptor;
struct timeval timeout;
int result=0;
fd_set localReadFDSet;
FD_ZERO(&localReadFDSet);
FD_SET(localPortFD, &localReadFDSet);
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // Check to see if port closed every 100ms
result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
if (!self.isOpen) return nil; // Port closed while select call was waiting
if (result < 0)
{
NSLog(#"No Data To Read");
}
if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet)) return nil;
// Data is available
char buf[1024];
long lengthRead = read(localPortFD, buf, sizeof(buf));
if (lengthRead>0)
{
NSData *readData = [NSData dataWithBytes:buf length:lengthRead];
if (readData != nil)
return [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding];
}
return nil;
}
You would think just doing:
NSString *result = nil;
do
{
result = [serialPort directRead];
NSLog(#"Past Message is: %#", result);
} while(result != nil);
Would flush out the messages. However, it acts as if there aren't any messages. However, if I call sendData:Message1 again I'd still see Response2 show up (in the above scenario).
Thanks for any and all help.
I'm having a little trouble following exactly what you're trying to do here, but in any case, it's something you'll have to implement at a higher level than ORSSerialPort or the POSIX serial read functions. ORSSerialPort will tell you any time bytes are available, as reported by the underlying standard POSIX file APIs it uses. So, there's nothing to "flush" from its perspective. If you get a response you were not expecting, e.g. a response to an "old" request, there's no way at all for the serial port to know that, it's up to your code to figure it out.
Something like this:
- (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data
{
if (![self dataIsResponseToLatestRequest:data]) return; // Try again next time around
// Process a valid (expected response)
}
This raises a couple other issues. You can't be guaranteed that data will come in "whole packets". The serial hardware, low level serial APIs, and ORSSerialPort have no way of knowing what a whole packet looks like, and therefore just deliver data a bit at a time as it arrives. See this answer for more on that. So, it's up to you to buffer incoming data and assemble it into packets.
If you need to match requests you sent with responses coming in, you have to do that yourself. (sounds like you're really already doing this).
Lastly, if you're only receiving "Response1" after sending "Message2", this leads me to think there's a problem with the device on the other side of the connection not responding to requests properly. It would probably be worth fixing that, if you can (assuming it's your hardware/software).
Related
I have an IOS app using azure storage blobs for jpg photos. My issue is when retrieving the blobs for display.
Most of the time they are retrieved fine. But just occasionally an odd one will not be returned. Instead 749 bytes are returned but with error still = nil.
Now that would be fine, no problem really. However EVERY time after that when I try to retrieve that blob again then the same issue occurs.
All the surrounding blobs are returned fine. The blob in question is fine and can be retrieved using another device.
I have spent lots of time clearing all variables involved and recalling the blob in question and no matter what only ever 749 bytes are returned. Deleting the app from the device and reinstalling it is the only workaround!
So I presume Azure storage or mobile services think the returned data was ok (since it had no error) and keeps sending the same - how can I prevent that and demand a true retry?
The actual retrieving code below was courtesy of github: thank you Ajayi13 it is almost awesome
[request fetchDataWithBlock:^(NSData* data, NSError* error)
{
if(error)
{
if(block)
{
block(nil, error);
}
else if([(NSObject*)_delegate respondsToSelector:#selector(storageClient:didFailRequest:withError:)])
{
[_delegate storageClient:self didFailRequest:request withError:error];
}
return;
}
if(block)
{
block(data, nil);
}
else if([(NSObject*)_delegate respondsToSelector:#selector(storageClient:didGetBlobData:blob:)])
{
[_delegate storageClient:self didGetBlobData:data blob:blob];
}
}];
I have now added the following code based on AdamSorrin's response and a blog post BY DANIEL PASCO: https://blackpixel.com/writing/2012/05/caching-and-nsurlconnection.html
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)[cachedResponse response];
// Look up the cache policy used in our request
if([connection currentRequest].cachePolicy == NSURLRequestUseProtocolCachePolicy) {
NSDictionary *headers = [httpResponse allHeaderFields];
NSString *cacheControl = [headers valueForKey:#"Cache-Control"];
NSString *expires = [headers valueForKey:#"Expires"];
if((cacheControl == nil) && (expires == nil)) {
NSLog(#"server does not provide expiration information and we are using NSURLRequestUseProtocolCachePolicy");
return nil; // don't cache this
}
}
return nil;
}
BUT this has not fixed my issue :o(
I'm not sure exactly what networking library you're using (your request object), so I'm going to assume that it's based off of NSURLSession and NSURLRequest. If not, the details here will be wrong, but the underlying reason still might be correct.
I would guess that your problem is two-fold.
For NSURLSession downloadTaskWithRequest:completionHandler:, the NSError parameter passed into the completion handler block is set only for client-side errors. Retryable service-side errors (throtting, server busy, etc.) aren't detected as such on the client - you need to look at the HTTP response code/message and handle appropriately.
Look at the NSURLSessionConfiguration documentation, specifically requestCachePolicy. My guess is that you're getting stale data from the cache when you try and re-fetch the contents of the blob. You can use this parameter to force the request to re-fetch the data from the service, if this is indeed causing the problem.
I have two functions in my software that cause important latency problems. The software is written in Objective-C. I receive serial data from an usb device and my goal is to encapsulate them and then to send them to another object which will process the data.
This section of the program causes large cpu and latency issues and I don't know how to solve this at all. The device only sends data when its state changes, thus when lots of changes occur well everything becomes laggy..
- (void)getSerialData {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[self getSerialDataLoop];
});
}
- (void)getSerialDataLoop {
readThreadRunning = YES;
char byte_buffer[2]; // buffer for holding incoming data
int numBytes=0; // number of bytes read during read
NSString *text;
// this will loop untilthe serial port closes
while(TRUE) {
// read() blocks until some data is available or the port is closed
numBytes = (int)read(serialFileDescriptor, byte_buffer, 1); // read up to the size of the buffer
if(numBytes>0) {
///text = [NSString stringWithCString:byte_buffer encoding:NSSymbolStringEncoding];
if(![text isEqualToString:#""]){
text = [NSString stringWithUTF8String:byte_buffer];
[self performSelectorOnMainThread:#selector(processNSStringData:) withObject:text waitUntilDone:YES];
}
} else {
break; // Stop the thread if there is an error
}
}
// make sure the serial port is closed
if (serialFileDescriptor != -1) {
close(serialFileDescriptor);
serialFileDescriptor = -1;
}
// mark that the thread has quit
readThreadRunning = FALSE;
}
Do you have any ideas or pointers?
You've basically reinvented NSStream here. I would first recommend that you investigate this already available solution that ties into the run loop.
You also could easily be overwhelming yourself with calls to getSerialData. Nothing in your system prevents multiple calls to this routine, and if you make multiple calls, you'll get dueling concurrent operations. Using NSStream would address that. In any case, though, you shouldn't keep creating new read blocks if one is already running.
You're also reading one byte at time and processing it. This is likely your biggest impact. Calling back to the main thread for every byte is likely quite expensive. If nothing else you're creating a new NSString object for every byte.
Note that your code is very dangerous and could crash. You never initialize byte_buffer, and you only read one byte into it. When you call stringWithUTF8String:, you're assuming that the second byte is \0, which depends on the current state of the stack.
I am attempting to create a torrent scraper in objective-c, I am using CocoaAsyncSocket to send the data packets over UDP. Following the BitTorrent UDP Tracker Protocol. I have verified using Wireshark that packets have been sent, but the tracker does not send anything back. I am assuming I am doing something wrong in putting together the data that is sent, since I have very little experience with data manipulation. Right now I am just trying to successfully complete the connect request of the protocol. Here is the code
-(void)connect {
NSString *host = #"tracker.publicbt.com";
GCDAsyncUdpSocket *socket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[socket connectToHost:host onPort:80 error:nil];
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
uint64_t connection_id = htonl(0x41727101980);
uint32_t action = htonl(0);
uint32_t transaction_id = htonl(122);
NSMutableData *data = [NSMutableData data];
[data appendBytes:&connection_id length:sizeof(connection_id)];
[data appendBytes:&action length:sizeof(action)];
[data appendBytes:&transaction_id length:sizeof(transaction_id)];
[sock sendData:data toAddress:address withTimeout:-1 tag:1];
}
Now I think I know the problem.
Since the connection ID must be initialized to that exact value, you need to make sure that it is intact. You are making a mistake in that htonl() returns a uint32_t, which is not going to be what you want. You need to break the connection id into 2 parts and independently convert them to network byte order.
uint64_t wholeConnectionID = 0x41727101980LLU;
uint32_t connectionID[2];
connectionID[0] = htonl(wholeConnectionID >> 32);
connectionID[1] = htonl(wholeConnectionID & 0xffffffff);
// ...
[data appendBytes:connectionID length:sizeof(connectionID)];
If this doesn't solve your problem, it will at least be a required step to get to that point anyways.
I think you need to call a method to actually get the socket to start receiving:
- (BOOL)beginReceiving:(NSError **)errPtr;
This will ensure that the delegate method is invoked every time a packet comes in.
Ok so I have been at this bug all day, and I think I've got it narrowed down to the fundamental problem.
Background:
I am working on an app that has required me to write my own versions of NSNetService and NSNetServiceBrowser to allow for Bonjour over Bluetooth in iOS 5. It has been a great adventure, as I knew nothing of network programming before I started this project. I have learned a lot from various example projects and from the classic Unix Network Programming textbook. My implementation is based largely on Apple's DNSSDObjects sample project. I have added code to actually make the connection between devices once a service has been resolved. An NSInputStream and an NSOutputStream are attained with CFStreamCreatePairWithSocketToHost( ... ).
Problem:
I am trying to send some data over this connection. The data consists of an integer, a few NSStrings and an NSData object archived with NSKeyedArchiver. The size of the NSData is around 150kb so the size of the whole message is around 160kb. After sending the data over the connection I am getting the following exception when I try to unarchive...
Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive
After further exploration I have noticed that the received data is only about 2kb.. The message is being truncated, thus rendering the archive "incomprehensible."
Potentially relevant code:
The method that sends the data to all connected devices
- (void) sendMessageToPeers:(MyMessage *)msg
{
NSEnumerator *e = [self.peers objectEnumerator];
//MyMessage conforms to NSCoding, messageAsData getter calls encodeWithCoder:
NSData *data = msg.messageAsData;
Peer *peer;
while (peer = [e nextObject]) {
if (![peer sendData:data]) {
NSLog(#"Could not send data to peer..");
}
}
}
The method in the Peer class that actually writes data to the NSOutputStream
- (BOOL) sendData:(NSData *)data
{
if (self.outputStream.hasSpaceAvailable) {
[self.outputStream write:data.bytes maxLength:data.length];
return YES;
}
else {
NSLog(#"PEER DIDN'T HAVE SPACE!!!");
return NO;
}
}
NSStreamDelegate method for handling stream events ("receiving" the data)
The buffer size in this code is 32768 b/c that's what was in whatever example code I learned from.. Is it arbitrary? I tried changing it to 200000, thinking that the problem was just that the buffer was too small, but it didn't change anything.. I don't think I fully understand what's happening.
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch (eventCode) {
case NSStreamEventHasBytesAvailable: {
NSInteger bytesRead;
uint8_t buffer[32768]; // is this the issue?
// uint8_t buffer[200000]; //this didn't change anything
bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];
if (bytesRead == -1) ...;
else if (bytesRead == 0) ...;
else {
NSData *data = [NSData dataWithBytes:buffer length:bytesRead];
[self didReceiveData:data];
}
} break;
/*omitted code for other events*/
}
}
NSStream over a network like that will be using a TCP connection. It can vary, but the maximum packet size is often around 2k. As the message you’re sending is actually 160k, it will be split up into multiple packets.
TCP abstracts this away to just be a stream of data, so you can be sure all these packets will receive in the correct order.
However, the stream:handleEvent: delegate method is probably being called when only the first 2k of data has arrived – there’s no way for it to know that there’s more coming until it does.
Note the method read:maxLength: doesn’t gauruntee you’ll always get that max length – in this case it seems to be only giving you up to 2k.
You should count up the actual bytesReceived, and concatenate all the data together until you receive the total amount you’re waiting for.
How does the receiver know how much data it wants? – you might want to design your protocol so before sending data, you send an integer of defined size indicating the length of the coming data. Alternatively, if you’re only ever sending one message over the socket, you could simply close it when finished, and have the receiver only unarchive after the socket is closed.
You seem to be reading from self.inputStream but the stream passed into your stream:handleEvent: method is called aStream. Are they referencing the same object somehow? Otherwise I'm not sure you're reading the stream that actually has bytes available
I am creating a socket connection with objective C, using asyncsocket. I do this using the "connectToHost" method. I am trying to handle the case where the socket connection fails. "connectToHost" is supposed to return "YES" when it connects sucessfully, and a NO otherwise. For some reason, it is always returning a yes. I even supplied a blank string as the host and it still returns yes. Any thoughts?
Thanks,
Robin
BOOL connectStatus = NO; //used to check if connection attempt succeeded
testSocket = [[AsyncSocket alloc] initWithDelegate: self];
connectStatus = [testSocket connectToHost: #"" onPort: 5000 error: nil];
if(connectStatus == NO)
{
NSLog(#"Failed to connect to socket ");
}
else {
NSLog(#"Connected to socket sucessfully, connectStatus = %d", connectStatus);
}
Per the header file:
// Once one of the accept or connect methods are called, the AsyncSocket instance is locked in
// and the other accept/connect methods can't be called without disconnecting the socket first.
// If the attempt fails or times out, these methods either return NO or
// call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:".
If you check the source – for the love of all things holy, when working with open source software, USE THE SOURCE! –, you will see that the method you are invoking returns NO only when it fails to start the connection process. A return value of YES just says, "Okay, I'm trying to connect:
"If things go wrong, I'll let you know by calling onSocket:willDisconnectWithError: and onSocketDidDisconnect:.
"If all goes well, you'll get a onSocket:didConnectToHost:port: message, mmkay?"
Don't expect synchronous behavior from an asynchronous socket library.