I will try to be as detailed as I can. I am trying to connect to an acquisition unit from my iPhone in my app. We are using IP4 and the acquisition unit doesn't support DHCP so its always scanning for device with a specific static IP and port no.
Before I tested the connection between the unit and my iPhone, I created an adhoc network using my desktop and try it out with my iPhone. This is part of my code.
CFSocketContext CTX = { 0, description, NULL, NULL, NULL };
/* Create the server socket as a TCP IPv4 socket and set a callback */
/* for calls to the socket's lower-level accept() function */
TCPServer = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP,
kCFSocketAcceptCallBack
, (CFSocketCallBack)WiFiCallBack, &CTX);
/* Set the port and address we want to listen on */
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
CFDataRef addressData = CFDataCreate( NULL, (UInt8*)(&addr), sizeof( struct sockaddr_in ) );
CFSocketSetAddress(TCPServer, addressData);
It works and I can do data transfer between my desktop and iPhone if I feed in the IP that was assigned to iPhone to the PC app on my desktop. However if I set a static IP for iPhone and try to get the PC app to connect to any device with that IP it doesn't work.
Same goes with my acquisition unit. The call back function is not called at all.
I am in desperate need of help so any form of help is welcomed. Thanks.
I'm sorry, but your post is not very clear.
Are you are trying to establish a server socket on the iPhone, and connect to it from elsewhere?
This is going to be problematic for many reasons.
First is that your ip is not going to be the same. When connected to WIFI, you will have an ip that is routable at least on the current network.
But when connected to 3g (or lte, etc), you will likely not be able to route to the ip given at all.
Even if you did have a fully routable ip address on some interface that existed long enough, iOS is not designed for this. Your application will not be able to run efficiently in the background and listen to a server socket. You can simulate this with persistent sockets and voip background mode. However that requires a separate server component.
You could also try polling from the iPhone, that may satisfy your requirements.
Related
could you help me?
So problem is QOS On IPV6 socket when it connect to IPV4 server
I can't set transport options on package
self.socket = socket(PF_INET6, SOCK_DGRAM, 0); // Create UDP Socket
int on = 1;
setsockopt(self.socket, SOL_SOCKET, SO_NOSIGPIPE, (void *)&on, sizeof(int));
int off = 0;
setsockopt(self.socket, IPPROTO_IPV6, IPV6_V6ONLY, (void *)(&off), sizeof(off)); // Disable IPV6 Only
int option = 46;
// set QOS option
setsockopt(self.socket, IPPROTO_IP, IP_TOS, (void *)(&option),sizeof(option)); // IPV4
setsockopt(self.socket, IPPROTO_IPV6, IPV6_TCLASS, (void *)(&option),sizeof(option)); //IPV6
But when i see packages to IPV4 DSCP is 0
Server send me correct DSCP
It seems like your kernel doesn't implement the mapping of IPv6 TCLASS to IPv4 DSCP.
The V6ONLY=0 option is mostly a hack to allow servers to accept both IPv4 and IPv6 connections on a single socket. I'm not surprised that only the bare minimum of options is implemented.
Your best option is probably to contact the kernel developers to see if they want to implement the mapping...
I'll start by saying that I definitely want to disable Nagle's Algorithm. The application that I am testing for is a real time P2P app in which packets are small and extremely time sensitive. This test also serves to compare UDP and TCP for possible networking solutions.
I am able to open the TCP socket and send messages back and forth, but I have not been able to disable Nagle's Algorithm. I have tried:
static const int yes = 1;
if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes))) {
fprintf(stderr, "Error setting tcp nodelay\n");
return -2;
}
on both the listening and connecting sockets. This does not fail. I also elevated the priority of the receiving thread to DISPATCH_QUEUE_PRIORITY_HIGH.
The test I am running starts with a packet that contains "start", followed by numerous packets that contain "data" and one final packet containing "stop". These packets are almost always combined into one or few packets, such as:
Received Peer Message: startdatadatadatadatadatadatastop
or
Received Peer Message: startdatadatadata
Received Peer Message: datadatadatastop
Is there a difference in the way that TCP_NODELAY is set on iOS? I was able to set it on Linux with the above code successfully.
I'm attempting to receive a UDP Broadcast under Mono for Android and I am seeing no data coming in. This is somewhat perplexing because it works fine on the Galaxy Tab 7 and Galaxy Tab 10 (Android v 3.2) I have, but fails on an HTC G2 (Android v2.3.4).
The code is straightforward:
public void BeginDiscover()
{
var packet = new DiscoverPacket();
lock (m_syncRoot)
{
var localEndpoint = new IPEndPoint(m_local, 0);
using (var udp = new UdpClient(localEndpoint))
{
var remoteEndpoint = new IPEndPoint(IPAddress.Broadcast, DiscoverPort);
udp.Send(packet.Data, packet.Data.Length, remoteEndpoint);
Thread.Sleep(100);
}
}
}
I have verified that the manifest includes this line:
<uses-permission android:name="android.permission.INTERNET" />
Though this is happening in Debug, so that should be implicitly set anyway.
Other very strange observations:
Again, this is working just fine on another type of device
The handler listening for UDP broadcasts (which list listening for the response) does see this outbound packet. The code for this listener is also straightforward:
[listener code]
private void Start()
{
m_discoverListener = new UdpClient(DiscoverPort);
m_discoverListener.BeginReceive(DiscoverCallback, m_discoverListener);
}
private void DiscoverCallback(IAsyncResult result)
{
try
{
var ep = new IPEndPoint(IPAddress.Any, DiscoverPort);
var data = m_discoverListener.EndReceive(result, ref ep);
// filter out what we send
var add = AddressWithoutPort(ep.Address);
if (add == m_local.ToString()) return;
// parse discover response
// [clipped for clarity]
}
finally
{
m_discoverListener.BeginReceive(DiscoverCallback, m_discoverListener);
}
}
Wireshark running on a separate PC on the same network does see the discover request packet (from above)
The "discovered" device is also seeing it, because Wireshark is also seeing the reply
The Android device UDP listener is not receiving the response packet
The only major differences between devices that I can think of (other than different OEMs implementing the platform) is that the G2 has a cellular radio built in and the Galaxy Tab does not. In my specific test case, I have no SIM card in the phone, though, so no cellular connection is being made. Note that the code above is explicitly using the local endpoint that is on the WiFi network.
Is there a known issue with UDP on the G2 specifically or generally on older implementations of the Android platform?
It took a bit of work as the UDP response in question is coming from a microcontroller on the device and I wanted to make absolutely certain that it wasn't an issue on the micro end (though I suspected it wasn't). I created a PC-based simulator for the microcontroller device that handles my Android UDP request and that sends back the exact same UDP response that the microcontroller does, then verified all of the traffic looks fine with Wireshark.
The net result is that I see he exact same behavior with the simulator. The Galaxy Tab 7 and 10 devices receive the UDP response no problem. The HTC G2 never does. This leads me to conclude that one of the following is true:
a) The HTC G2 specifically has an implementation bug preventing it from receiving (or at least passing along) UDP broadcasts on the network
or
b) The older Android build has his bug.
Until I find different hardware with the same Android version as the G2 (v2.3) I can't tell which is the case. In either event, it's a bug that makes this (and potentially other) hardware unusable for my specific solution.
I have a couple of applications on the market based on UDP communication.
I have problems with HTC phones not receiving the UDP broadcast packets sent from another device... if sent from the same device, the packets arrive.
so, I think the problem is in HTC, and I found a possible solutions online (even though I have not tried it):
http://www.flattermann.net/2010/09/fix-udp-broadcasts-on-htc-phones-running-stock-firmware/
I'm trying to add some simple peer-to-peer connection functionality to an iOS library. Coding for outgoing connections was simple enough; a call to CFStreamCreatePairWithSocketToHost connects to a remote host and sets up streams for reading/writing from/to it. Simple enough.
However, I couldn't find an equivalently easy way to set up a socket to listen for/accept incoming connections. So I went back to basics and used socket(), bind(), listen(), and accept() to implement low-level connection handling, patterned largely after the example here:
http://www.pcs.cnu.edu/~dgame/sockets/server.c
That's all fine, but now that I'm able to accept incoming connections I'm wondering how to go about creating CFReadStream and CFWriteStream instances to manage them. Is there a straightforward way of doing so?
As an aside, I'm aware that a CocoaAsyncSocket library exists that supports asynchronous server sockets, but I'm really not interested in an async solution.
Okay, turns out that the answer was actually fairly simple. You can use:
void CFStreamCreatePairWithSocket (
CFAllocatorRef alloc,
CFSocketNativeHandle sock,
CFReadStreamRef *readStream,
CFWriteStreamRef *writeStream
);
...to bind a CFReadStream and CFWriteStream to an already connected socket. That flow seems a little backwards to me (i.e. what if bytes have already been read from the socket, etc., and why isn't it possible to just bind something to the listening/server socket such that every time a new connection is accepted corresponding CFReadStream and CFWriteStream instances are automatically set up?), but whatever.
The code goes like:
int connectedSocketId = accept(socketId, (struct sockaddr *)&clientSocket, &addrlen);
if (connectedSocketId != -1) {
//successful connection
CFReadStreamRef clientInput = NULL;
CFWriteStreamRef clientOutput = NULL;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, connectedSocketId, &clientInput, &clientOutput);
if (clientInput && clientOutput) {
CFReadStreamSetProperty(clientInput, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(clientOutput, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
if (! CFReadStreamOpen(clientInput) || ! CFWriteStreamOpen(clientOutput)) {
NSLog(#"Could not initialize streams!");
}
else {
//use the streams
}
}
}
So the important things to realize were:
It's not necessary to bind anything to the socket you are listening on; instead it's possible to bind CFReadStream and CFWriteStream instances arbitrarily to any already connected socket, even if you've already read/written data from/to it.
For all its fancy verboseness, a CFSocketNativeHandle is just an int. Oh how I loathe unnecessary aliasing of primitive types to things that sound like they aren't primitive types.
When I do a non blocking connect to the loopback address from the iOS Simulator (which probably uses the same TCP stack as the Mac and FreeBSD), I see that connect always succeeds, even when the server process isn't running.
I detect that connect succeeded by using select() with zero timeout. So, as long as select() returns 0, I assume that the connection is in progress, if it returns -1 I fail with an error and if it returns 1, the socket is ready for reading (as the server must have responded), and I begin reading after reporting that the connect succeeded.
This works well for all addresses except the loopback. On the loopback select() always returns 1, even when there is no server running. So, I begin reading, which fails and I handle it. But I should have detected this through select()!
You are getting the error before that, right on connect().
Before going to proceed with select(), check that errno is EINPROGRESS and not something else. In *BSD, connections to a non-listening port on a localhost error out (or may error out) immediately.
I just ran a very simple test like this (headers skipped):
int
main(void)
{
int fd;
int r;
struct sockaddr_in remote;
struct hostent *he;
he = gethostbyname("localhost");
if (he == NULL)
return -1;
memcpy(&remote.sin_addr, he->h_addr, sizeof(remote.sin_addr));
remote.sin_port = htons(9671);
remote.sin_family = AF_INET;
fd = socket(PF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
r = connect(fd, (struct sockaddr *)&remote, sizeof remote);
if (r < 0) {
perror("connect");
}
return 0;
}
With nothing listening on port 9671, I got:
on Linux: connect: Operation now in progress
on FreeBSD: connect: Connection refused
Of course, it is always a good idea to check the error codes of all syscalls (something the example above does not do for simplicity's sake - it's just an illustration, after all).
The problem was that I was relying on select() to tell me if the connect was successful. Select only tells you if something changed on that fd. I should've actually called connect() again on the socket and verify that the if it fails, errno is either EINPROGRESS, ECONN or EALREADY. Except for ECONN, all the other values mean that we should retry; ECONN means it's already connected. Any other errno value means we failed to connect.