TCP Socket Programming in iOS. Server Client Response - ios

I'm almost done with this task but i'm stuck a point due to which i'm getting partial result.
I have server(linux or windows) and client(iOS) between which TCP IP socket connection exist. I have used form load in my iphone simulator where the connection between server and iphone happens automatically as the application opens. Server send the data back what ever I send on simulator and print it in log. But i'm not able to exactly receive the whole response. For "Innovations" I receive maybe just "in" or "Innova"etc.. Below are the code snippets.
void TCPClient()
{
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, &readStream, &writeStream);
[NSThread sleepForTimeInterval:2]; //Delay
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
if(!CFWriteStreamOpen(writeStream))
{
NSLog(#"Error Opening Socket");
}
else
{
UInt8 buf[] = "Innovations";
int bytesWritten = CFWriteStreamWrite(writeStream, buf, strlen((char*)buf));
NSLog(#"Written: %d", bytesWritten);
}
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
if(!CFReadStreamOpen(readStream))
{
NSLog(#"Error reading");
}
else
{
UInt8 bufr[15];
int bytesRead = CFReadStreamRead(readStream, bufr,strlen((char*)bufr));
NSLog(#"Read: %d", bytesRead);
NSLog(#"buffer: %s", bufr);
}
}
Notice in the read I did change the array size. But I still get the error. Same in the case of IBAction of a button. Even in that for every click i'm sending a data and i'm not getting the response of the same data.
Can valuable suggestion???

One error is that
int bytesRead = CFReadStreamRead(readStream, bufr,strlen((char*)bufr));
should be
int bytesRead = CFReadStreamRead(readStream, bufr, sizeof(bufr));
The last parameter of CFReadStreamRead is the capacity of the read buffer and determines the maximum number of bytes read. strlen((char*)bufr) is the length of the string currently in the buffer. You should also NULL-terminat the string in bufr before printing it.
With this modification, your program might work with short strings. But there will be problems as soon as you try to send/receive larger amounts of data.
A socket write can write less bytes than you asked it to, and a socket read can return less bytes than you requested.
Have a look at the Stream Programming Guide which describes how to register the socket streams with the runloop and handle stream events asynchronously.

Related

CFSocket data callbacks

In an iPhone app, I create a CFSocket object from an existing native UDP socket and set up a data callback whenever the socket receives some data. I then add that to my main program loop:
//Set socket descriptor field
cbData.s = udpSocket.getSocketDescriptor();
CFSocketContext udpSocketContext;
memset(&udpSocketContext, 0, sizeof(udpSocketContext));
udpSocketContext.info = &cbData;
cbData.socketRef = CFSocketCreateWithNative(NULL, cbData.s, kCFSocketDataCallBack, &getSocketDataCallBack, &udpSocketContext);
cbData.runLoopSourceRef = CFSocketCreateRunLoopSource( NULL, cbData.socketRef, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), cbData.runLoopSourceRef, kCFRunLoopCommonModes);
I send 1024-byte datagrams over WiFi from a separate Mac server app every 5 mS, and receive them on my iPhone in my getSocketDataCallBack routine.
I expect getSocketDataCallBack to be called every 5 mS (to match the period of the datagrams being sent from the Mac), which happens the majority of times. BUT, the calls often get delayed by 10s or 100s of mS. Thereafter, I get a rapid sequence of callbacks (fractions of a mS) to retrieve the multiple datagrams that have piled up over that delay.
As iOS obviously keeps the delayed datagrams around,
is there any way to grab all the delayed datagrams from the system at
once instead of getSocketDataCallBack being called over and over
in quick succession?
[I do query how many bytes are available in the callback ala:
CFDataRef dataRef = (CFDataRef)data;
numBytesReceived = CFDataGetLength(dataRef);
but 'numBytesReceived' is always reported as 1024.]
Alternatively, is there any way to improve/lessen the socket callback
timing variability through other means?
I'm using socket call back for Inter Process Communication (actually, inter thread communication) with UNIX socket. How we use socket is identical to the TCP/UDP.
The code below is written in c/obj-c and using posix thread. To translate it to Swift/NSThread should not be difficult.
Note the program below works as a server side, which means the program creates socket where the clients connect to. Once the client connected to the socket, the system automatically accepts the connection and allocates
another file descriptor to read/write. The socket call back reflects this two stage operation. Initially we create the socket, we then add as run-loop source so the system can call the call back when the client attempted to connect. The system accepts, then allocates and tells the call back
a file descriptor to read/write with the client. We then create another run-loop source from the read/write fd and add to run-loop. This second call back is called when rx/tx data is ready.
MAIN THREAD:
The main thread creates UNIX socket and worker thread. The socket fd is passed as argument of the worker thread.
#import <stdio.h>
#import <string.h>
#import <stdlib.h>
#import <unistd.h>
#import <pthread.h>
#import <sys/socket.h>
#import <sys/un.h>
#import <sys/stat.h>
#import <sys/types.h>
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
int setup(const char *ipcNode) {
int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd == -1) {
return -1;
}
struct sockaddr_un sa = {0};
sa.sun_len = sizeof(sa);
sa.sun_family = AF_UNIX;
strcpy(sa.sun_path, ipcNode);
remove(sa.sun_path);
if (bind(sockfd, (struct sockaddr*)&sa, sizeof(struct sockaddr_un)) == -1) {
close(sockfd);
return -1;
}
// start up worker thread
pthread_attr_t at;
pthread_attr_init(&at);
pthread_attr_setdetachstate(&at, PTHREAD_CREATE_DETACHED);
pthread_t th;
pthread_create(&th, &at, workerThread, (void *)(long)(sockfd));
return 1;
}
WORKER THREAD:
The program works as a server. So, it waits to get connected by client (via connect()). Once it's connected, the system automatically calls accept() and allocates read/write fd to communicate with the client. This fd is passed to accept-call back routine socketDataCallback(). Then we create another call back clientDataCallback() with the read/write fd.
// worker thread
//
void *workerThread(void *tprm) {
int sockfd = (int)tprm;
int retval = listen(sockfd, 1); // mark as "server" side. here, accept only 1 connection request at a time
if (retval != 0) {
return NULL;
}
// create CFSocket and register it as data source.
CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, sockfd, kCFSocketAcceptCallBack, socketDataCallback, nil);
// don't close native fd on CFSocketInvalidate
CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
// create run loop source
CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
// add to run loop
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
CFRelease(socketRunLoop);
CFRelease(socket);
CFRunLoopRun();
// not return here untill run loop stops
close(sockfd);
return NULL;
}
// socket connection w/ client side. create another data source and add to run-loop
//
void socketDataCallback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
CFSocketContext socketContext;
memset(&socketContext, 0, sizeof(CFSocketContext));
int clientfd = *((int *)data); // get file descriptor (fd)
socketContext.info = (void *)((long)clientfd); // set fd at info of socketContext
// create CFSocket for tx/rx w/ connected client
CFSocketRef socket = CFSocketCreateWithNative(kCFAllocatorDefault, clientfd, kCFSocketReadCallBack | kCFSocketWriteCallBack, clientDataCallback, &socketContext);
CFSocketDisableCallBacks(socket, kCFSocketWriteCallBack);
CFRunLoopSourceRef socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), socketRunLoop, kCFRunLoopCommonModes);
CFRelease(socket);
CFRelease(socketRunLoop);
}
// data to/from client
//
void clientDataCallback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info) {
if (callbackType & kCFSocketWriteCallBack) {
// your own tx data prcess here
// txDataCallback(s, callbackType, address, data, info);
}
if (!(callbackType & kCFSocketReadCallBack)) return;
// extract fd
int fd = (int)((long)info);
// read data, and do some work
uint8_t rxdata[1024];
size_t nr = read(fd, rxdata, 1024);
if (!nr) {
// socket closed
handleSocketClosed(s);
return;
}
// your own rx process here
}
// socket closed
//
void handleSocketClosed(CFSocketRef s) {
// any clean up process here, then
CFSocketInvalidate(s);
// stop run loop if necessary
// CFRunLoopStop(CFRunLoopGetCurrent());
}
If you are working at client side, things get a bit easier. You get a read/write fd with connect() call. Then you create CFSockeRef and add to run-loop by using the fd.
Hope this helps.
EDIT: How to wait with POSIX select(). To wait with POSIX select() at worker thread is simpler than socket call back. If you are on client side, then:
int sockfd = socket(...);
bind(sockfd, ...)
connect(sockfd, ...);
while (1) {
int nfds = sockfd+1;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
int retval = select(nfds, &rfds, NULL, NULL, NULL);
if (retval == -1) break;
if (retval > 0) {
uint8_t rxdata[1024];
size_t nr = read(sockfd, rxdata, 1024);
if (!nr) {
// socket closed.
break;
}
// do your rx process here
}
}
Run the code above at your worker thread.

Read "structured/serialized data" from NSStream

I am developing a game app which will take structured data from a server and make responses based on these data.
I have connected the app to the internet through the NSSream. Specifically, I follow the tutorial of Apple's guide. So my code looks something like:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if(!_data) {
_data = [[NSMutableData data] retain];
}
uint8_t buf[1024];
NSInteger len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[_data appendBytes:(const void *)buf length:len];
// bytesRead is an instance variable of type NSNumber.
[bytesRead setIntValue:[bytesRead intValue]+len];
} else {
NSLog(#"no buffer!");
}
break;
}
// continued
My problem is: "How can I convert the _data to the format I want when it is possible". For example, my server will send two types of data. For each data, the first byte is an indicater for the data type (e.g., 1 means data type 1, 2 means data type 2). For the data type 1, an int (4 bytes) is sent as the data itself. For the data type 2, an int (4 bytes) is sent as the size of the following string bytes. For example, if this int is 10, then there will be 10 more bytes sent from the server to form a string for the client.
The code in my Android(Java) app looks like:
// dataIn = new DataInputStream(socket.getInputStream());
private void keepPacketRecving(){ // this will be executed in a separate thread
while(keepRecvThreadRunning) {
try {
byte type;
type = dataIn.readByte();
if(type == 1){
int data = dataIn.readInt();
getTye1Data(data); // callback function for receiving type 1 data (int)
} else if (type ==2) {
int dataSize = dataIn.readInt();
byte[] buf = new byte[dataSize];
// ... loop to read enough byte into buf ...
String data = new String(buf);
getType2Data(data); // callback function for receiving type 2 data (String)
}
}
}
}
I also notice I can't have a while-loop inside the switch statement to ask inputStream to read more data untile it is enougth because it will hang the thread.
I guess the best way would be always appending all the bytes into the _data (like the example) and has another function to parse the _data? Then the problem is how I can peek bytes from the data and just fetch part of it out (e.g., 10 bytes out of a 20-byte _data).
Is there any wrapper in Objective-C like the DataInputStream in Java?

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).

RTMP Streaming from ios to server?

Right now, I am using a static librtmp.a library to open a RTMP connection between my iphone and server. When the record button is pressed, the camera starts taking input, and on captureoutput, uses AVAssetWriter's in different threads to encode the videos to h264/AAC. The videos are then saved to a specific URL. I'm trying to take these processed frames and send them over my RTMP client using librtmp.
-(void)writeURL:(NSURL*)segmentURL {
NSLog(#"Utilities are writing...");
NSData *segmentData = [NSData dataWithContentsOfURL:segmentURL];
const char* body = [segmentData bytes];
NSLog(#"%i", [segmentData length]);
NSLog(#"%s", body);
RTMPPacket packet = _rtmp->m_write;
RTMPPacket_Alloc(&packet, [segmentData length]);
packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
packet.m_packetType = RTMP_PACKET_TYPE_VIDEO;
packet.m_body = (char*)body;
RTMPPacket_Dump(&packet);
RTMP_SendPacket(_rtmp, &packet, TRUE);
//RTMP_Write(_rtmp, packet.m_body, packet.m_nBodySize);
}
crashes everytime on the RTMPPacket_alloc call and I'm unsure what to do. Is this the right way to approach sending the data over the network?
EDIT:
sample output
2013-03-31 22:53:16.163 videoAppPrototype[2567:907] Switching encoders
2013-03-31 22:53:16.179 videoAppPrototype[2567:1703] Encoder switch finished
2013-03-31 22:53:16.220 videoAppPrototype[2567:1703] Upload public.mpeg-4
2013-03-31 22:53:16.223 videoAppPrototype[2567:1703] Utilities are writing...
2013-03-31 22:53:16.230 videoAppPrototype[2567:1703] 339171
DEBUG: RTMP PACKET: packet type: 0x09. channel: 0x00. info 1: 0 info 2: 0. Body size: 0. body: 0x00
EDIT 2:
I changed my code to use RTMP_Write() instead of RTMP_SendPacket().
New Method:
-(void)writeURL:(NSURL*)segmentURL {
NSLog(#"Utilities are writing...");
NSData *segmentData = [NSData dataWithContentsOfURL:segmentURL];
NSUInteger len = [segmentData length] / sizeof(unsigned char);
Byte *byteData = (Byte*)malloc(len);
memcpy(byteData, [segmentData bytes], len);
free(byteData);
NSLog(#"%i", [segmentData length]);
NSLog(#"First write attempt...");
RTMP_Write(_rtmp, (char *)byteData, len);
NSLog(#"Successful?");
}
Which now crashes at RTMP_Write, as shown in stack trace:
If someone has any idea or needs anymore information, lemme know!

Hops tracing ttl reciveform on ios

I'm trying to implement simple traceroute for the iOS. Everything seems to work fine, except that somehow when I run my application on simulator or on the device it finds only a few (6-7) first routers on the way when the CLI traceroute finds all 14 routers.
const char *c = "www.gmail.com";
struct hostent *host_entry = gethostbyname(c);
char *ip_addr;
ip_addr = inet_ntoa(*((struct in_addr *)host_entry->h_addr_list[0]));
struct sockaddr_in destination, fromAddr;
int recv_sock;
int send_sock;
// Creting Sockets///
if ((recv_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)) <
0) // using UDP socket.
{
NSLog(#"Could not cretae recv_sock.\n");
}
if ((send_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
NSLog(#"Could not cretae send_sock.\n");
}
memset(&destination, 0, sizeof(destination));
destination.sin_family = AF_INET;
destination.sin_addr.s_addr = inet_addr(ip_addr);
destination.sin_port = htons(80);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
sizeof(struct timeval));
char *cmsg = "GET / HTTP/1.1\r\n\r\n";
int max_ttl = 20;
int num_attempts = 5;
socklen_t n = sizeof(fromAddr);
char buf[100];
for (int ttl = 1; ttl <= max_ttl; ttl++) {
memset(&fromAddr, 0, sizeof(fromAddr));
if (setsockopt(send_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) < 0)
NSLog(#"error in setsockopt\n");
for (int try = 0; try < num_attempts; try ++) {
if (sendto(send_sock, cmsg, sizeof(cmsg), 0,
(struct sockaddr *)&destination,
sizeof(destination)) != sizeof(cmsg))
NSLog(#"error in send to...\n#");
int res = 0;
if ((res = recvfrom(recv_sock, buf, 100, 0, (struct sockaddr *)&fromAddr,
&n)) < 0) {
NSLog(#"an error: %s; recvfrom returned %d\n", strerror(errno), res);
} else {
char display[16] = {0};
inet_ntop(AF_INET, &fromAddr.sin_addr.s_addr, display, sizeof(display));
NSLog(#"Received packet from%s for TTL=%d\n", display, ttl);
break;
}
}
}
I have tried to bind the send socket but have same results and I can't use Sock_raw on iOS. I tried to run it on my mac and got same results. The error I get is "Resource temporarily unavailable;" for the recvfrom(). Why is that? How can I fix it?
The EAGAIN error ( producing "Resource temporarily unavailable;" string) could be raised by the timeout of the receiving socket.
Since you set just 10000 microseconds as read timeout (that's really short IMHO) with this line...
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10000;
setsockopt(recv_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
... it's possibile that the longer the way (i mean the number of router you have to pass through), the more chance you have to incour in this situation.
Try to raise timeout value and let us know if it got better.
EDIT
I tried the source code under linux and i noticed two kind of problems.
As mentioned above: Timeouts
Problem with the 80 port
I just raised the timeout and used a port different than 80 (in my case i sent udp message to 40000 port) and i got back all the hops just like traceroute command.
I'm not sure why this behaviour occour. Maybe some kind of "possible malicious packet alarm" gets triggered by the router that discards it
FURTHER EDIT
Look at this link: man traceroute
In the List Of Available Methods section you can find many ways to achieve what you need. Your method is similar to the default one, stating:
Probe packets are udp datagrams with so-called "unlikely" destination ports. The "unlikely" port of the first probe is 33434, then for each next probe it is incremented by one. Since the ports are expected to be unused, the destination host normally returns "icmp unreach port" as a final response. (Nobody knows what happens when some application listens for such ports, though).
So, if you need to full emulate the behaviour of the common Linux traceroute you have to increase by 1 the destination port, everytime the TTL increase (or everytime you can't get a response IMHO)
MAYBE, sometimes your command doesn't work on certain ports because the router is listening to the latter (as suggested by Linux manual and underlined in bold by me).

Resources