I created a simple persistent socket connection for our game using TcpClient and NetworkStream. There's no problem connecting, sending messages, and disconnecting normally (quitting app/server shuts down/etc).
However, I'm having some problems where, in certain cases, the client isn't detecting a disconnection from the server. The easiest way I have of testing this is to pull out the network cable on the wifi box, or set the phone to airplane mode, but it's happened in the middle of a game on what should otherwise be a stable wifi.
Going through the docs for NetworkStream etc, it says that the only way to detect a disconnection is to try to write to the socket. Fair enough, except, when I try, the write passes as if nothing is wrong. I can write multiple messages like this, and everything seems fine. It's only when I plug the cable back in that it sees that it's disconnected (all messages are buffered?).
The TcpClient is set to NoDelay, and there's a Flush() called after every write anyway.
What I've tried:
Writing a message to the NetworkStream - no joy
CanWrite, Connected, etc all return true
TcpClient.Client.Poll( 1000, SelectMode.SelectWrite ); - returns true
TcpClient.Client.Poll( 1000, SelectMode.SelectRead ) && TcpClient.Client.Available == 0 - returns true
TcpClient.Client.Receive(buffer, SocketFlags.Peek) == 0 - when connected, blocks for about 10-20s, then returns true. When no server, blocks forever(?)
NetworkStream.Write() - doesn't throw an error
NetworkStream.BeginWrite() - doesn't throw an error (not even when calling EndWrite())
Setting a WriteTimeout - had no effect
Having a specific time where we haven't received a message from the server (normally there's a keep-alive) - I had this, but removed it, as we were getting a lot of false-positives due to lag etc (some clients would see between 10-20s of lag)
So am I doing something wrong here? Is there any way to get the NetworkStream to throw an error (like it should) when writing to a socket that should be disconnected?
I've no problem with a keep-alive (the default case is the server will notify the client that it hasn't received anything in a while, and the client will send a heartbeat), but at the minute, according to the NetworkStream everything's hunky-dory.
It's for a game, so ideally the detection should be quick enough (as the user can still move through the game until they need to make a server call, some of which will block the UI, so the game seems broken).
I'm using Unity, so it's .Net 2.0
is to pull out the network cable on the wifi box
That's a good test. If you do that the remote party is not notified. How could it possibly find out? It can't.
when I try, the write passes as if nothing is wrong
Writes can (and are) buffered. They eventually enter a block hole... No reply comes back. The only way to detect this is a timeout.
So am I doing something wrong here?
You have tried a lot of things but fundamentally you cannot find out about disconnects if no reply comes back telling you that. Use a timeout.
Related
I would want to send a message from the server actively, such as using UDP/TCPIP to a client using an arduino. It is known that this is possible if the user has port forward the specific port to the device on local network. However I wouldn't want to have the user to port forward manually, perhaps using another protocol, will this be possible?
1 Arduino Side
I think the closest you can get to this is opening a connection to the server from the arduino, then use available to wait for the server to stream some data to the arduino. Your code will be polling the open connection, but you are avoiding all the back and forth communications to open and close the connection, passing headers back and forth etc.
2 Server Side
This means the bulk of the work will be on the server side, where you will need to manage open connections so you can instantly write to them when a user triggers some event which requires a message to be pushed to the arduino. How to do this varies a bit depending on what type of server application you are running.
2.1 Node.js "walk-through" of main issues
In Node.js for example, you can res.write() on a connection, without closing it - this should give a similar effect as having an open serial connection to the arduino. That leaves you with the issue of managing the connection - should the server periodically check a database for messages for the arduino? That simply removes one link from the arduino -> server -> database polling link, so we should be able to do better.
We can attach a function triggered by the event of a message being added to the database. Node-orm2 is a database Object Relational Model driver for node.js, and it offers hooks such as afterSave and afterCreate which you can utilize for this type of thing. Depending on your application, you may be better off not using a database at all and simply using javascript objects.
The only remaining issue then, is: once the hook is activated, how do we get the correct connection into scope so we can write to it? Well you can save all the relevant data you have on the request to some global data structure, maybe a dictionary with an arduino ID as index, and in the triggered function you fetch all the data, i.e. the request context and you write to it!
See this blog post for a great example, including node.js code which manages open connections, closing them properly and clearing from memory on timeout etc.
3 Conclusion
I haven't tested this myself - but I plan to since I already have an existing application using arduino and node.js which is currently implemented using normal polling. Hopefully I will get around to it soon and return here with results.
Typically in long-polling (from what I've read) the connection is closed once data is sent back to the client (arduino), although I don't see why this would be necessary. I plan to try keeping the same connection open for multiple messages, only closing after a fixed time interval to re-establish the connection - and I hope to set this interval fairly high, 5-15 minutes maybe.
We use Pubnub to send notifications to a client web browser so a user can know immediately when they have received a "message" and stuff like that. It works great.
This seems to have the same constraints that you are looking at: No static IP, no port forwarding. User can theoretically just plug the thing in...
It looks like Pubnub has an Arduino library:
https://github.com/pubnub/arduino
The client use ssh login and start up a server on remote machine, then the clinet create a tcp connect to the server.
The server need exit when the client has exit normally or crashed or network is dropped.
So the question is how to detect if the client which the server has connected to is crashed.
The first try is using error() signal, catch QAbsoluteSocket::NetworkError to determine the network has dropped. But I can't receive error() signal at all even if i pull out the network cable.
The second try is using the SocketState, i think whenever SocketState is UnconnectedState,the client may has exit normally and the server should exit too. This way works fine for "normal exit", but I don't know how to deal with "crash" and "dead network".
Help me, thanks!
I'd recommend using TCP keep alive. It is not exposed through the public QTcpSocket interface, but you can use setsockopt with QAbstractSocker::socketDescriptor to activate the SO_KEEPALIVE feature.
EDIT: It appears that keep alive was added to QAbstractSocket at some point. So, simply call QAbstractSocket::setSocketOption with QAbstractSocket::KeepAliveOption.
You can find information about adjusting the timeout of keep alive request here: http://www.gnugk.org/keepalive.html
Most of the time, the only way you will know there is a problem with a socket connection is when you try to read or write with it. There are some exceptions: Windows will change the state of sockets if the network cable is unplugged, Linux (in my experience) will not.
The most reliable way to detect connection problems is to have the client regularly send a small message at an agreed upon interval with the server. If the server does not see this message within a reasonable time, it should consider the client dead and drop the connection. This will also give both sides regular opportunities to detect a problem via reads and writes.
I have a tcpip socket interface to a third party software app. I've implemented this interface for several customer sites with no problem. The latest customer, though... problems. We've turned on logging in the apps on either end, and also installed Wireshark on the PC to log raw tcpip traffic. With that, we've proved that my server app successfully sends the message out, the pc receives the message, but the client app doesn't see it. (This is a totally intermittent problem, which is why it's such a pain to troubleshoot.)
The socket details are as simple as they come: one socket handling two way communications between the server and the pc. The messages are plain ascii text and fairly short (not XML). The server initiates communications by sending the first message, and then the client responds with several messages. The socket is kept open at all times while the apps are running. The client app is designed so that the end user can only process one case at a time, which prevents message collisions from happening. They have some sort of polling set up, their app "hibernates" until it sees the initiating message from the server.
The third party vendor has advised me to add a few second delay before I send them the initiating message. I can't see how that helps. If the client is "sleeping", just polling the socket waiting for a message, how does adding a delay before the first message help? It's not like we send two messages and the second one gets lost. It's losing the first message. So I don't see how it matters if we send that message now or two seconds from now.
I've asked them and they haven't given me details. It could be some proprietary details in their coding that they don't want to disclose to me, and that's fair. So I'm asking here because I'm always learning new things about socket programming. Maybe you guys can shed some light on how polling a tcpip socket can be affected by message timing?
Since its someone else's client and they won't tell you what its doing (other than saying 'insert a delay'), the answer is probably that their client is reading and discarding the message because its not yet in a state to deal with it. The delay will allow the client time to get into a state where it can respond to the message properly.
In other words, the client has a race condition. One easy way this can happen is if they have one thread for reading messages and another for dealing with them.
Short of running strace(1) on the client to see what system calls it is making, its tough to tell what the client is actually doing.
I'd like to know whether it's possible to easily detect (on the server side) when Flex clients disconnect from a BlazeDS destination please? My scenario is simply that I'd like to try and use this to figure out how long each of my clients are connected for each session. I need to be able to differentiate between clients as well (ie so not just counting the number of currently connected clients which I see in ds-console).
Whilst I could program in a "I'm now logging out" process in my clients, I don't know whether this will fire if the client simply navigates away to another web page rather than going though said logout process.
Can anyone suggest if there's an easy way to do this type of monitoring on the server side please.
Many thanks,
Alex
Implement you own adapter extending "extends ServiceAdapter"
Then set the function:
#Override
public boolean handlesSubscriptions() {
return true;
}
So you can handle subscription and unsubscription
Then you can manage those in the manage function:
#Override
public Object manage(CommandMessage commandMessage) {
switch (commandMessage.getOperation()) {
case CommandMessage.SUBSCRIBE_OPERATION:
break;
case CommandMessage.UNSUBSCRIBE_OPERATION:
break;
}
}
You can also catch different commands.
Hope this help
The only way to do it right is to implement the heartbeat mechanism in a way or another. You can use the keep-alive from http coupled with session expire as suggested before but my opinion is to use the messaging mechanism from BlazeDS (send a message at X seconds). You can control the time interval and other aspects (maybe you want to detect if the client is not doing anything for several hours and to invalidate the session even if your client is still connected).
If you want to be notified instantly (chat application) when a client is disconnecting a solution is to have a socket (RTMP) or some emulation (http streaming) which will detect instantly if the client is disconnected, however this disconnection can be temporary (maybe the network was down for one second, and after that is ok, and you should also detect that).
I would assume BlazeDS would provide a callback or event for when a client disconnects, but I haven't worked with Blaze so that would just be a guess. First step would be to check the documentation to see if it does though, as that would be your best bet.
What I've done in cases where there isn't a disconnect event (or it's not reliable) is to add a keepalive message. For instance, the client would be configured to send a keepalive message every 30 seconds, and if the server goes more than, say, 2 minutes without seeing a keepalive then it assumes the client has disconnected. This would let you differentiate between different clients, and you can play around with the rate and check times to get something you're happy with.
I have an HTTP connection, opened by
HttpConnection c = (HttpConnection)Connector.open(url);
where url is one of:
http://foo.bar;deviceside=false
http://foo.bar;deviceside=false;ConnectionType=mds-public
http://foo.bar;deviceside=true;ConnectionUID=xxxxxxx
http://foo.bar;deviceside=true;interface=wifi
Is there any way to cause the request to error out immediately if the connection cannot be established because the device is not connected to a network? As it is, it takes about a minute to timeout in many cases (specifically on the first call to get the information from the network: c.getResponseCode())
Edit: I mean error out. In one case, Wifi, specifically, it will sit around for several minutes if the wifi is not on before timing out, and I want it to stop right away.
I use the RadioInfo class to check if there is a connection and if the radio is turned on before trying to make a connection. Then you can just display a message to the user or turn the radio on (if it's off) before trying to connect, makes for a much better user experience.
Try using:
if (RadioInfo.getState() == RadioInfo.STATE_OFF)
OR
if (RadioInfo.getSignalLevel() == RadioInfo.LEVEL_NO_COVERAGE)
To check connection status before connecting.
I encase my posts in a thread to timeout faster. Make sure your "PostThread" catches all exceptions (and saves them).
public byte[] post(String url, byte[] requestString){
PostThread thread=new PostThread(url, requestString);
synchronized(thread){
try{
thread.start();
thread.wait(TIMEOUT);
}catch(Throwable e){
}//method
}//synch
if (thread.isAlive()){
try{
thread.interrupt();
}catch(Throwable e){
}//method
D.error("Timeout");
}//endif
if (thread.error!=null) D.error(thread.error);
if (thread.output!=null) return thread.output;
throw D.error("No output");
}//method
There is also the ConnectionTimeout parameter, which I have not tested: eg socket://server:80/mywebservice;ConnectionTimeout=2000
Not any way that can be specified programmatically. It can be irritating, but a connection from a mobile device - especially a BlackBerry - generally goes through a few different networks and gateways before reaching the destination server: wireless->Carrier APN->Internet->BES (maybe)->foo.bar server so a large timeout is built-in to account for potential delays at any of those points.
You can control default device connection timeout from your BES/MDS server (or in the JDE, from the MDS\config\rimpublic.property file) - but that probably won't help you.
It would be better to have a Timeout check from a different thread, Because this is gonna happen even when the connection is established, say the network latency is very high, so u dont want the user to wait for so long or such thing.
So, in that case have a check from a different thread, whether the current time minus time entered for initiating the connection is more than your set time, close the connection using connection.close()!