C# TCP server sometimes send string containing parts of last message - c#

I am working on my project in which i need arduinos to comunicate with my TCP server app. Eveyrthing was going fine as of now. But i just noticed that the server sometimes send invalid string to the client. (the string simply contains parts of the last string sent) Those the arduino doesnt know what to do with it.
How it should work:
Server receives: "CNN:1" (connection request from arduino #1)
Server sends: "CNN:1:CFM" (confirming that the request was recieved.
Server Recieves: "PING"
Server Sends: "PONG"
Server Recieves: "CHGTM:1:B" (arduino #1 sends request to change team to Blue)
Server Sends: "CHGTM:1:B:CFM" or "CHGTM:1:GMPAUSED" (depends on wheter the game is running or is paused)
EXAMPLE OF THE BAD BEHAVIOUR:
Server Recieves: "CNN:1"
sends: "CNN:1:CFM"
Recieves: "PING"
Sends: "PONG1:CFM" (This message contains part of the last message those is corrupted -- It doesnt happen everytime only sometimes)
Do anybody know how to fix this? I tried nulling the string variables used to contain the text before sending but no luck. I also tried flushing the networkstream after writing to it but also no luck (as i understand it, this method is just placeholder and doesnt do anything at the moment)
Here is the code that i am using for the communication:
class Comunication
{
TcpListener server = null;
public List<Majak> activeClients;
public MainWindow mw { get; set; }
public bool isConOk { get; set; }
public Comunication(string ip, int port, MainWindow _mw)
{
mw = _mw;
activeClients = new List<Majak>();
IPAddress localAddr = IPAddress.Parse(ip);
server = new TcpListener(localAddr, port);
server.Start();
StartConnectionCheck();
StartListener();
//StartConnectionCheck();
}
public void StartListener()
{
try
{
while (true)
{
TcpClient client = server.AcceptTcpClient();
if(client != null)
{
Majak m = new Majak(client);
Thread t = new Thread(new ParameterizedThreadStart(HandleDevice));
t.Start(m);
activeClients.Add(m);
}
}
}
catch(SocketException e)
{
MessageBox.Show("Nelze spustit server", e.ToString());
server.Stop();
}
}
public void HandleDevice(Object obj)
{
Majak majak = (Majak)obj;
if (majak != null && majak.isConnected)
{
var stream = majak.Client.GetStream();
string imei = String.Empty;
string data = null;
Byte[] bytes = new Byte[256];
int i;
try
{
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
string hex = BitConverter.ToString(bytes);
data = Encoding.ASCII.GetString(bytes, 0, i); //text revieved from arduino needs evaluation
DataEvaluation(data, majak);
}
}
catch (Exception e) //if there is an error than the client is closed and removed from list of connected clients and this thread is stopped
{
//MessageBox.Show(e.ToString(), "Chyba při komunikaci s majákem");
App.Current.Dispatcher.Invoke((Action)delegate // <--- HERE
{
activeClients.Remove(majak);
});
majak.isConnected = false;
majak.Client.Close();
return;
}
}
}
public void DataEvaluation(string d, Majak m)
{
//split data by ":"
string[] datasplt = d.Split(':');
switch(datasplt[0]){
case "CNN":
m.ID = int.Parse(datasplt[1]);
SendResponse(1, m);
break;
case "PONG":
isConOk = true;
break;
case "PING":
SendResponse(3, m);
break;
case "CHGTM":
m.Color = datasplt[2];
if(mw.SS.ChangeTeam(int.Parse(datasplt[1]), datasplt[2]))
{
SendResponse(2, m); //if the game is running than sends confirmation to the arduino
}
else
{
SendResponse(4, m); //if the game is paused sends to arduino string saying so
}
break;
}
}
public void SendResponse(int i, Majak m)
{
string response = null;
Byte[] response_byte = null;
switch (i)
{
case 1: //connection cofirmation
//connection validation
response = null;
response = string.Format("CNN:{0}:CFM", m.ID);
response_byte = null;
response_byte = System.Text.Encoding.ASCII.GetBytes(response);
try
{
m.Client.GetStream().Write(response_byte, 0, response_byte.Length);
m.Client.GetStream().Flush();
}
catch { }
break;
case 2: //change team confirmation
//confirms change of team
response = null;
response = string.Format("CHGTM:{0}:{1}:CFM", m.ID, m.Color);
response_byte = null;
response_byte = System.Text.Encoding.ASCII.GetBytes(response);
try
{
m.Client.GetStream().Write(response_byte, 0, response_byte.Length);
m.Client.GetStream().Flush();
}
catch { }
break;
case 3: //Sends PONG if PING is recieved
response = null;
response = "PONG";
response_byte = null;
response_byte = System.Text.Encoding.ASCII.GetBytes(response);
try
{
m.Client.GetStream().Write(response_byte, 0, response_byte.Length);
m.Client.GetStream().Flush();
}
catch { }
break;
case 4:
response = null;
response = string.Format("CHGTM:{0}:GMPAUSED", m.ID);
response_byte = null;
response_byte = System.Text.Encoding.ASCII.GetBytes(response);
try
{
m.Client.GetStream().Write(response_byte, 0, response_byte.Length);
m.Client.GetStream().Flush();
}
catch { }
break;
}
}
public void StartConnectionCheck()
{
Thread t = new Thread(new ParameterizedThreadStart(CheckConnectionStatus));
t.Start(mw);
}
public void CheckConnectionStatus(Object __mw) //every 30 seconds sends PING to the arduino in order to evaluate if the connection still exists. If the PONG is not recieved in 30 seconds than closes connection and remove client from connected clients
{
while (true)
{
string pingmsg = "PING";
Byte[] pingmsg_byte = System.Text.Encoding.ASCII.GetBytes(pingmsg);
Stopwatch s = new Stopwatch();
foreach (Majak m in activeClients.ToList<Majak>())
{
isConOk = false;
var stream = m.Client.GetStream();
Byte[] bytes = new Byte[256];
try
{
stream.Write(pingmsg_byte, 0, pingmsg_byte.Length);
s.Restart();
while (s.Elapsed < TimeSpan.FromSeconds(30) && !isConOk)
{
}
if (!isConOk)
{
m.isConnected = false;
activeClients.Remove(m);
m.Client.GetStream().Close();
m.Client.Close();
}
}
catch (Exception e)
{
m.isConnected = false;
activeClients.Remove(m);
m.Client.Close();
}
}
Thread.Sleep(30000);
}
}
}

Related

Bluetooth connection getting read failed, socket might closed or timeout, read ret: -1

I am creating an app in Xamarin(Android app), that allows user to send data on his phone via bluetooth connection to another phone. When I click the button it should run the bluetooth getAllPairedDevices and then openConnection, but when it tries to connect it goes into throw exception.
This string sets data variable:
private string data = null;
This is my call button, that checks if any devices are paired:
Button btConnect = FindViewById<Button>(Resource.Id.connect);
btConnect.Click += (sender, e) =>
{
BluetoothManager manager = new BluetoothManager();
if (manager.getAllPairedDevices() != false)
{
System.Threading.Thread thread = new System.Threading.Thread(() =>
{
while (true)
{
data = manager.getDataFromDevice();
}
});
thread.IsBackground = true;
thread.Start();
}
};
And then this is my bluetooth class:
public class BluetoothManager
{
// Unique ID for connecting
private const string UuidUniverseProfile = "00001101-0000-1000-8000-00805f9b34fb";
// Incoming bluetooth data from UART
private BluetoothDevice result;
// Input/Output stream of this communication
private BluetoothSocket mSocket;
// Convert byte[] to strings
private BufferedReader reader;
private System.IO.Stream mStream;
private InputStreamReader mReader;
public BluetoothManager()
{
reader = null;
}
private UUID getUUIDfromString()
{
return UUID.FromString(UuidUniverseProfile);
}
private void close(IDisposable connectedObject)
{
if (connectedObject == null) return;
try
{
connectedObject.Dispose();
}
catch (Exception ex)
{
throw ex;
}
connectedObject = null;
}
private void openDeviceConnection(BluetoothDevice btDevice)
{
try
{
// Getting socket from specific device
mSocket = btDevice.CreateRfcommSocketToServiceRecord(getUUIDfromString());
mSocket.Connect();
// Input stream
mStream = mSocket.InputStream;
// Output stream
//mStream.OutputStream;
mReader = new InputStreamReader(mStream);
reader = new BufferedReader(mReader);
System.Threading.Thread.Sleep(1000);
mSocket.Close();
}
catch (IOException ex)
{
close(mSocket);
close(mStream);
close(mReader);
throw ex;
}
}
public String getDataFromDevice()
{
return reader.ReadLine();
}
public bool getAllPairedDevices()
{
// Default android phone bluetooth
BluetoothAdapter btAdapter = BluetoothAdapter.DefaultAdapter;
var devices = btAdapter.BondedDevices;
if (devices != null && devices.Count > 0)
{
// All paired devices
foreach (BluetoothDevice mDevice in devices)
{
openDeviceConnection(mDevice);
}
return true;
}
else
{
return false;
}
}
}
Since I am new to Bluetooth communication I am not exactly sure where the problem is, I already checked multiple answers, and android.library but it is so complicated, so no luck.
Also a subquestion, how would you send a simple string via this setup?

A very simple TCP server/client in C# occasionally drops packets. What can I do to prevent this?

I've got an intra-PC communication server / client set up to send and receive data from one program to another - in this case, a custom server that is listening to text commands and Unity3D.
For the most part, it works, however every once in awhile, it will drop packets, and Unity will not get them without multiple attempts. The packets seem to be sent but lost, as I do see the "Sent message" console log. The following is the code for the server and client:
SERVER:
class TCPGameServer
{
public event EventHandler Error;
public Action<Data> ADelegate;
TcpListener TCPListener;
TcpClient TCPClient;
Client ActiveClient;
NetworkStream networkStream;
StreamWriter returnWriter;
StreamReader streamReader;
Timer SystemTimer = new Timer();
Timer PingTimer = new Timer();
int Port = 8637;
public TCPGameServer()
{
TCPListener = new TcpListener(IPAddress.Loopback, Port);
SystemTimer.Elapsed += StreamTimer_Tick;
SystemTimer.AutoReset = true;
SystemTimer.Interval = 2000;
PingTimer.Elapsed += PingTimer_Tick;
PingTimer.AutoReset = true;
PingTimer.Interval = 30000;
}
public void OpenListener()
{
TCPListener.Start();
TCPListener.BeginAcceptTcpClient(AcceptTCPCallBack, null);
Console.WriteLine("Network Open.");
}
public void GameLogout()
{
SystemTimer.AutoReset = false;
SystemTimer.Stop();
PingTimer.AutoReset = false;
PingTimer.Stop();
ActiveClient = null;
returnWriter.Dispose();
streamReader.Dispose();
Console.WriteLine("The client has logged out successfully.");
}
private void AcceptTCPCallBack(IAsyncResult asyncResult)
{
TCPClient = null;
ActiveClient = null;
returnWriter = null;
streamReader = null;
try
{
TCPClient = TCPListener.EndAcceptTcpClient(asyncResult);
TCPListener.BeginAcceptTcpClient(AcceptTCPCallBack, null);
ActiveClient = new Client(TCPClient);
networkStream = ActiveClient.NetworkStream;
returnWriter = new StreamWriter(TCPClient.GetStream());
streamReader = new StreamReader(TCPClient.GetStream());
Console.WriteLine("Client Connected Successfully.");
Data Packet = new Data();
Packet.cmdCommand = Command.Login;
Packet.strName = "Server";
Packet.strMessage = "LOGGEDIN";
SendMessage(Packet);
SystemTimer.AutoReset = true;
SystemTimer.Enabled = true;
SystemTimer.Start();
Ping();
PingTimer.AutoReset = true;
PingTimer.Enabled = true;
PingTimer.Start();
} catch (Exception ex)
{
OnError(TCPListener, ex);
return;
}
}
private void StreamTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
{
CheckStream();
}
private void PingTimer_Tick(object source, System.Timers.ElapsedEventArgs e)
{
Ping();
}
private void Ping()
{
if (TCPClient.Connected)
{
Data Packet = new Data();
Packet.cmdCommand = Command.Ping;
Packet.strName = "Server";
Packet.strMessage = "PING";
SendMessage(Packet);
}
}
public void CheckStream()
{
try
{
if (TCPClient.Available > 0 || streamReader.Peek() >= 0)
{
string PacketString = streamReader.ReadLine();
Data packet = JsonConvert.DeserializeObject<Data>(PacketString);
switch (packet.cmdCommand)
{
case Command.Logout:
GameLogout();
break;
case Command.Message:
if (ADelegate != null)
{
ADelegate(packet);
}
break;
case Command.Ping:
Console.WriteLine("PONG!");
break;
}
}
} catch (IOException e)
{
Console.WriteLine(e.Message);
} catch (NullReferenceException e)
{
Console.WriteLine(e.Message);
}
}
public void SendMessage(Data packet)
{
if (ActiveClient != null)
{
string packetMessage = JsonConvert.SerializeObject(packet);
returnWriter.WriteLine(packetMessage);
returnWriter.Flush();
}
}
public void OnError(object sender, Exception ex)
{
EventHandler handler = Error;
if (handler != null)
{
ErrorEventArgs e = new ErrorEventArgs(ex);
handler(sender, e);
}
}
public void RegisterActionDelegate(Action<Data> RegisterDelegate)
{
ADelegate += RegisterDelegate;
}
public void UnRegisterActionDelegate(Action<Data> UnregisterDelegate)
{
ADelegate -= UnregisterDelegate;
}
}
CLIENT:
public class TCPNetworkClient
{
public Action<Data> PacketDelegate;
public TcpClient TCPClient;
int Port = 8637;
StreamReader streamReader;
StreamWriter streamWriter;
Timer StreamTimer = new Timer();
public bool LoggedIn = false;
public void Start()
{
if (LoggedIn == false)
{
TCPClient = null;
StreamTimer.AutoReset = true;
StreamTimer.Interval = 2000;
StreamTimer.Elapsed += StreamTimer_Tick;
try
{
TCPClient = new TcpClient("127.0.0.1", Port);
streamReader = new StreamReader(TCPClient.GetStream());
streamWriter = new StreamWriter(TCPClient.GetStream());
StreamTimer.Enabled = true;
StreamTimer.Start();
}
catch (Exception ex)
{
Debug.Log(ex.Message);
}
}
}
private void StreamTimer_Tick(System.Object source, System.Timers.ElapsedEventArgs e)
{
if (TCPClient.Available > 0 || streamReader.Peek() >= 0)
{
string PacketString = streamReader.ReadLine();
Data packet = JsonConvert.DeserializeObject<Data>(PacketString);
PacketDelegate(packet);
}
}
public void Logout()
{
Data Packet = new Data();
Packet.cmdCommand = Command.Logout;
Packet.strMessage = "LOGOUT";
Packet.strName = "Game";
SendMessage(Packet);
if (streamReader != null && streamWriter != null)
{
streamReader.Dispose();
streamWriter.Dispose();
TCPClient.Close();
TCPClient = null;
streamReader = null;
streamWriter = null;
}
StreamTimer.Stop();
}
public void SendMessage(Data packet)
{
string packetMessage = JsonConvert.SerializeObject(packet);
try
{
streamWriter.WriteLine(packetMessage);
streamWriter.Flush();
} catch (Exception e)
{
}
}
public void RegisterActionDelegate(Action<Data> RegisterDelegate)
{
PacketDelegate += RegisterDelegate;
}
public void UnRegisterActionDelegate(Action<Data> UnregisterDelegate)
{
PacketDelegate -= UnregisterDelegate;
}
}
I'm not really sure what's going on, or if there are any more additional checks that I need to add into the system. Note: It's TCP so that "when" this fully works, I can drop the client into other programs that I might write that may not fully rely or use Unity.
new TcpClient("127.0.0.1", Port) is not appropriate for the client. Just use TcpClient(). There is no need to specify IP and port, both of which will end up being wrong.
TCPClient.Available is almost always a bug. You seem to assume that TCP is packet based. You can't test whether a full message is incoming or not. TCP only offers a boundaryless stream of bytes. Therefore, this Available check does not tell you if a whole line is available. Also, there could be multiple lines. The correct way to read is to have a reading loop always running and simply reading lines without checking. Any line that arrives will be processed that way. No need for timers etc.
The server has the same problems.
Issue (2) might have caused the appearance of lost packets somehow. You need to fix this in any case.

How many times should C# .BeginReceive() be called to receive 3 chunks sent be 3 calls to Linux C write() across TCP?

Do 3 chunks sent by 3 calls to Linux 'C' write(), through TCP, get received as the same, 3 chunks by Windows C# .BeginReceive(), or a single, contiguous chunk, or however many have been received when .BeginReceived is called?
A 'C' app on Linux sends a message by 3 calls to write(), through TCP connection, to a Windows C# app, which receives using BeginReceive().
Does BeginReceive() need to be called three times, to receive each of the three chunks sent by write()? Or is the size received by BeginReceive() equal to the size of what Windows has received when BeginReceive() is called? Which could be all bytes sent by the 3 writes(), or a partial amount, so .BeginReceive() should be called UNTIL all are received?
The Linux C app is running on an embedded TI ARM, and inside the same box the Windows C# app is running a Single Board Computer. The ARM has a direct Ethernet connection to the SBC.
The communication between the ARM and SBC sometimes fails to start at boot time, and I'm reverse engineering the source code to check for bad design.
ARM side is TCP listener, and Windows client initiates the TCP connection.
Using MontaVista(R) Linux(R) Professional Edition 5.0.0 (0702774)
and Windows-7 Visual-Studio 2010 Visual-C#.
Here is the ARM sending software, and the Windows receiving software........................
LINX 'C'
char msgBuffer[64];
sprintf(msgBuffer, START_MSG_ENVELOPE, msgId++, ack);
write(connection->fd, msgBuffer, strlen(msgBuffer));
write(connection->fd, msg, strlen(msg));
write(connection->fd, END_MSG_ENVELOPE, strlen(END_MSG_ENVELOPE));
HERE IS THE WINDOWS C# SIDE OF IT.............................................
private static void makeConnection(Socket clientSocket, int iPortNo)
{
TimeoutObject.Reset();
socketexception = null;
IPAddress ip;
//create the end point
IPEndPoint ipEndPoint;
ip = IPAddress.Parse(ipAddress);
try
{
ipEndPoint = new IPEndPoint(ip, iPortNo);
//connect to the remote host...
clientSocket.BeginConnect(ip, iPortNo, new AsyncCallback(CallBackMethod), clientSocket);
if (TimeoutObject.WaitOne(5 * 1000, false)) //5 secs connection timeout
{
if (!IsConnectionSuccessful)
{
string msg = VNResourceManager.Instance.GetString(VNMessages.DAM_NOT_FOUND);
if (socketexception != null)
msg += ": " + socketexception.Message;
throw new Exception(msg);
}
}
else
{
clientSocket.Close();
throw new TimeoutException(VNResourceManager.Instance.GetString(VNMessages.CONNECTION_TO_DAM_TIMED_OUT));
}
//watch for data ( asynchronously )...
WaitForData();
}
catch (SocketException e)
{
socketexception = e;
throw;
}
}
private static void CallBackMethod(IAsyncResult asyncresult)
{
try
{
IsConnectionSuccessful = false;
Socket socket = asyncresult.AsyncState as Socket;
if (socket.Connected)
{
socket.EndConnect(asyncresult);
IsConnectionSuccessful = true;
}
}
catch (Exception ex)
{
IsConnectionSuccessful = false;
socketexception = ex;
}
finally
{
TimeoutObject.Set();
}
}
public static void WaitForData()
{
try
{
if (asyncCallBack == null)
{
asyncCallBack = new AsyncCallback(OnDataReceived);
}
CSocketPacket theSocPkt = new CSocketPacket();
theSocPkt.thisSocket = clientSocket;
asyncResult = clientSocket.BeginReceive(theSocPkt.dataBuffer, 0, theSocPkt.dataBuffer.Length, SocketFlags.None, asyncCallBack, theSocPkt);
}
catch (SocketException se)
{
notifyErrorEventSubscribers(se);
}
}
public static void send(string message)
{
try
{
byte[] byData = System.Text.Encoding.ASCII.GetBytes(message);
clientSocket.Send(byData);
}
catch (SocketException se)
{
notifyErrorEventSubscribers(se);
throw;
}
}
//[MethodImpl(MethodImplOptions.Synchronized)]
public static void OnDataReceived(IAsyncResult result)
{
try
{
CSocketPacket theSockId = (CSocketPacket)result.AsyncState;
//end receive...
int messageSize = 0;
messageSize = theSockId.thisSocket.EndReceive(result);
Console.WriteLine(">>>>>>>>> messageSize = " + messageSize); // !!!
char[] chars = new char[messageSize + 1];
System.Text.Decoder d = System.Text.Encoding.ASCII.GetDecoder();
int charLen = d.GetChars(theSockId.dataBuffer, 0, messageSize, chars, 0);
string replyMessage = new System.String(chars);
lock (syncLock) //LastIndexOf function accesses the current culture info and we clear it in WM_TIMECHANGE handler (protecting from that race condition here)
{
if (replyMessage.LastIndexOf("\0") > 0)
replyMessage = replyMessage.Remove(replyMessage.LastIndexOf("\0"), 1);
if (replyMessage.LastIndexOf(Terminator) > 0)
replyMessage = replyMessage.Remove(replyMessage.LastIndexOf(Terminator), 1);
}
// Continue the waiting for data on the Socket
WaitForData();
receivedMsg += replyMessage;
// only serialize when we feel we have a message or we have reached the message line limit
if (((receivedMsg.Contains("message") && receivedMsg.Contains("/>")) || receivedMsg.Contains("</message>")) /* || (mRecvdMsgLineCount == Message.kMaxLines) */ )
{
List<XmlMessage> msgList = new List<XmlMessage>();
int index = -1;
do
{
index = receivedMsg.IndexOf("</message>");
if (index != -1)
{
XmlMessage message;
string strMessage = receivedMsg.Substring(0, index + "</message>".Length);
//MessageBox.Show(strMessage);
strMessage = strMessage.TrimStart(new char[] { '\r', '\n' });
receivedMsg = receivedMsg.Remove(0, index + "</message>".Length);
try
{
message = (XmlMessage)XmlMessage.GetXmlSerializer().Deserialize(XmlReader.Create(new StringReader(strMessage)));
}
catch (InvalidOperationException error)
{
string strErrorMessage = error.Message;
if (error.InnerException != null)
strErrorMessage += "\r\n" + error.InnerException.Message;
notifyErrorEventSubscribers(new Exception(strErrorMessage + "\r\n-----------------------------------------------------------------\r\n" + strMessage));
return;
}
msgList.Add(message);
}
} while (index != -1);
StringWriter sw = new StringWriter();
string serializedXml = string.Empty;
string strXmlMessage = string.Empty;
foreach (XmlMessage message in msgList)
{
if (message.ack_required && (message.update == null))
{
XmlMessage messageAcknowledge = new XmlMessage();
messageAcknowledge.ack_required = false;
messageAcknowledge.ack = new ack();
messageAcknowledge.ack.success = true;
messageAcknowledge.ack.id = message.id;
try
{
sendMessage(messageAcknowledge);
}
catch(Exception ex)
{
Logger.Log(EnumLoggingLevel.Error, "SocketCommunicationXMl.OnDataReceived", ex.Message);
}
}
if (dataReceivedEvent != null)
{
dataReceivedEvent(new object(), new DataReceivedEventArgs(message));
}
if ((ackRequiredMsg != null) && (message.ack != null))
{
if ((message.ack.id == ackRequiredMsg.id) && message.ack.success)
{
eventWaitForAck.Set();
}
}
}
}
}
catch (ObjectDisposedException objectDisposedException)
{
// Dispatcher.dispatchDebug(Debug.Level_3,"Socket has been closed", this);
notifyErrorEventSubscribers(objectDisposedException);
}
catch (SocketException se)
{
if (se.ErrorCode == 10054)
{
/*
for (int i = 0; i < 50; i++)
{
Thread.Sleep(1000);
}
try
{
SocketCommunicationDaq.Reconnect();
}
catch(Exception ex)
{
VNMsgBox.Show(ex.Message, MsgButtonType.OkOnly, MsgIconType.Error);
return;
}
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
for (int i = 0; i < 3; i++)
{
try
{
connect();
break;
}
catch (Exception ex)
{
System.Threading.Thread.Sleep(5000);
}
}
*/
Logger.Log(EnumLoggingLevel.Error, "OnDataReceived: ", se.ToString());
}
else
{
notifyErrorEventSubscribers(se);
}
}
}
As others have mentioned, TCP is a streaming protocol, so you never can tell how many DataReceived callbacks it will take to get all 100 bytes. Could be 1, could be 100.
The receive code is fairly complex and performance could be improved (too many string operations). Hard to tell if there are control-flow issues. I would suggest breaking the DataReceived method up to simplify. Here's a reasonable skeleton:
public static void OnDataReceived(IAsyncResult result)
{
//1) copy all data received into a buffer of some sort, like MemoryStream
//2) Dispatch any/all full messages that have been received
// to a queue or handler method (maybe handle on another thread)
//(hold onto any leftover bytes received that are part of the next message)
//Call BeginReceive() again
}
Also, it can help simplify Framing if you use a Length-Prefixed message format.
As #nos already stated, number of receives does not equal the number of sends, regardless of the client application was written in.
See also When does TcpClient's NetworkStream finish one read operation?

Android bluetooth in a service stops sending data after 561K

I wrote a service to send sensor data over bluetooth on android. Although I got no errors my C# client stops getting data after I sent exactly 561K data. At this moment it seems like my android continues to send data but my client doesn't get any. After a while, android also stops sending data. I tried different configurations. My program always stops sending data after "Service->Server". I don't get any errors but it stops sending. Here is android program.
#Override
public synchronized int onStartCommand(Intent intent, int flags, int startId) {
Log.i(EXTRA_MESSAGE,"onStartCommand");
if(isRunning)
Log.e(EXTRA_MESSAGE, "I am already running");
else
{
isRunning = true;
BluetoothDevice selectedDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRA_MESSAGE);
if(selectedDevice == null)
{Log.i(EXTRA_MESSAGE,"null it is "); return -1;}
connect = new ConnectThread(selectedDevice);
connect.start();
mHandler =new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case SUCCESS_CONNECT:
connected = new ConnectedThread((BluetoothSocket) msg.obj);
Toast.makeText(getApplicationContext(), "Connected", 0).show();
//connected.write(("Connected").getBytes());
Log.i(EXTRA_MESSAGE, "we are connected");
isConnected = true;
break;
case MESSAGE_READ:
Toast.makeText(getApplicationContext(), ((byte[]) msg.obj).toString(), 0).show();
break;
}
}
};
mSensor = (SensorManager) getSystemService(SENSOR_SERVICE);
sSensor = mSensor.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
// sSensor = mSensor.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
sSensor = mSensor.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
}
return super.onStartCommand(intent, flags, startId);
}
#Override
#Override
public void onSensorChanged(SensorEvent event) {
Log.i(EXTRA_MESSAGE, "Sensor data arrived");
if(isConnected )
{
String toSend = Integer.toString(event.sensor.getType())+ ":" + Long.toString(event.timestamp)+ ":";
for(float f : event.values){
toSend = toSend + Float.toString(f)+":";
}
//
connected.write(toSend.getBytes());
}
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.i(EXTRA_MESSAGE,"connectThread started successfully");
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
Log.i(EXTRA_MESSAGE,"connectThread connect successfully");
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
Log.i(EXTRA_MESSAGE,"connectThread connect exception");
try {
mmSocket.close();
} catch (IOException closeException) { }
}
// Do work to manage the connection (in a separate thread)
mHandler.obtainMessage(SUCCESS_CONNECT, mmSocket).sendToTarget();
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public boolean shouldContinue = true;
int nBytes =0;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
Log.i(EXTRA_MESSAGE,"connectedThread sockets");
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while(shouldContinue) {
try {
// Read from the InputStream
buffer = new byte[1024];
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
Log.e(EXTRA_MESSAGE, " We read");
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
Log.i(EXTRA_MESSAGE,"Service->Server");
mmOutStream.write(bytes);
nBytes += bytes.length;
Log.i(EXTRA_MESSAGE,"ok" + String.valueOf(nBytes ));
} catch (IOException e) {
Log.i(EXTRA_MESSAGE,"exception");
}
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
shouldContinue = false;
mmSocket.close();
} catch (IOException e) { }
}
}
Also my c# thread is as follows
public void ServerConnectThread()
{
serverStarted = true;
int counter = 0;
updateUI("Server started, waiting for clients");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
BluetoothClient conn = blueListener.AcceptBluetoothClient();
updateUI("Client has connected");
Stream mStream = conn.GetStream();
while (true)
{
try
{
//handle server connection
byte[] received = new byte[1024];
mStream.Read(received, 0, received.Length);
counter += Encoding.ASCII.GetString(received).Length;
String[] fields = Encoding.ASCII.GetString(received).Split(':');
double[] data = new double[3];
for (int i = 2; i < 5; i++) data[i-2] = double.Parse(fields[i]);
//mSource.notifyObserver(Int16.Parse(fields[0]), data);
updateUI(counter.ToString() + " "+ fields[2]+ ":" + fields[3] + ":" + fields[4]);
byte[] sent = Encoding.ASCII.GetBytes("Hello World");
mStream.Write(sent, 0, sent.Length);
}
catch (IOException exception)`enter code here`
{
updateUI("Client has disconnected!!!!");
}
}
}
One final thing is I've found thousands of 561K android program which sounded a little interesting.
Ok I found my own problem. Basically I m sending a reply from my client when I got the sensor data and It is not handled by android app.

How to determine if the tcp is connected or not?

I have tcpclient object and i want to determine if it's connected or not.
i use connected property of tcpclient but it returns the state of last operation. so its not useful.
then i use this code :
bool flag;
flag = (tcp.Client.Poll(10000, SelectMode.SelectWrite));
and
if( tcp.Client.Poll( 0, SelectMode.SelectRead ) )
{
byte[] buff = new byte[1];
if( tcp.Client.Receive( buff, SocketFlags.Peek ) == 0 )
{
flag = false;
}
}
but it does not work properly.
Any idea?
this is my code in server side :
private ArrayList _ClientList = new ArrayList();
public ClsServer(int port)
{
_TCPListener = new TcpListener(IPAddress.Any, port);
_TCPListener.Start();
Thread ListenThread = new Thread(new ThreadStart(ListenForClients));
ListenThread.IsBackground = true;
ListenThread.Start();
}
private void ListenForClients()
{
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this._TCPListener.AcceptTcpClient();
client.ReceiveTimeout = 0;
//create a thread to handle communication with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.IsBackground = true;
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
try
{
TcpClient tcpClient = (TcpClient)client;
AddObject(tcpclient);
int bytesRead;
string message = "";
byte[] RecievedPack = new byte[1024 * 1000];
NetworkStream clientStream = tcpClient.GetStream();
while (true)
{
bytesRead = 0;
try
{
////blocks until a client sends a message
bytesRead = clientStream.Read(RecievedPack, 0, RecievedPack.Length);
int Len = BitConverter.ToInt32(RecievedPack, 0);
message = UTF8Encoding.UTF8.GetString(RecievedPack, 0, Len);
}
catch (Exception er)
{
//When Client is disconnected
if (er.GetType() == typeof(IOException))
{
RemoveObject(client);
break;
}
}
//message has successfully been received
// do something
}
RemoveObject(client);
}
catch(Exception e)
{
// RemoveObject(client);
}
}
private void AddObject(object obj)
{
int totalcount, index;
totalcount = _ClientList.Count;
index = 0;
while (index < totalcount)
{
TcpClient alcobj = (TcpClient)_ClientList[index];
try
{
if (IPAddress.Equals(((IPEndPoint)alcobj.Client.RemoteEndPoint).Address,
((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address))
{
_ClientList.Remove(alcobj);
break;
}
index++;
}
catch (Exception er)
{
if (er.GetType() == typeof(ObjectDisposedException))
RemoveObject(alcobj);
}
finally
{
totalcount = _ClientList.Count;
}
}
_ClientList.Add(obj);
}
private void RemoveObject(object obj)
{
if (_ClientList.IndexOf(obj) > -1)
{
_ClientList.Remove(obj);
SendClientState(IP, false);
}
}
and this is the client side :
public bool IsConnected
{
try
{
if (_TcpClient != null && _TcpClient.Client != null && _TcpClient.Client.Connected)
{
// Detect if client disconnected
if (_TcpClient.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_TcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
private void clsClient()
{
if(!IsConnected())
{
Connecttoserver()
}
}
private void ConnectToServer()
{
try
{
NetworkStream _NetworkStream = _TcpClient.GetStream();
byte[] _RecievedPack = new byte[1024 * 1000];
string _Message = string.Empty;
int _BytesRead;
int _Length;
while (true)
{
_BytesRead = _NetworkStream.Read(_RecievedPack, 0, _RecievedPack.Length);
_Length = BitConverter.ToInt32(_RecievedPack, 0);
_Message = UTF8Encoding.UTF8.GetString(_RecievedPack, 4, _Length);
if (_BytesRead != 0)
{
if (OnReceive != null)
// do something
_NetworkStream.Flush();
}
}
}
catch (Exception exp)
{
// do something
}
}
in client side, IsConnected() always return false and try to connecttoserver, so the server listener always try to add the client in a list
Use this code instead, I have tested it and using it in real production software:
public bool IsConnected
{
get
{
try
{
if (_tcpClient != null && _tcpClient.Client != null && _tcpClient.Client.Connected)
{
/* pear to the documentation on Poll:
* When passing SelectMode.SelectRead as a parameter to the Poll method it will return
* -either- true if Socket.Listen(Int32) has been called and a connection is pending;
* -or- true if data is available for reading;
* -or- true if the connection has been closed, reset, or terminated;
* otherwise, returns false
*/
// Detect if client disconnected
if (_tcpClient.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (_tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
}
Edit: However you can't rely on just checking the connection and if true proceed, because it returning the status of connection at time this property executed, for instance after you check IsConnected and it returns true, and while you are in the middle of communication, the connection maybe lost there! we just use it in the first place to reduce the probability of failure, So you have to wrap the whole communication in a try/catch and expect the connection to be lost at any time!
I suggest not relaying on such methods.
For my opinion, the best way is to implement some kind of keep-alive mechanism.
Every X seconds, send a small message and wait for an answer.
You're probably disconnected when:
1. You catch an exception when trying to send a keep-alive message (if you're the client side).
2. You don't get a keep-alive message/response for some period of time.
I also suggest not to count on the built-in TCP keep-alive, I found it very not-reliable.
Updated: Found a nice post for this matter: Post
The only way to know whether or not the other end of a socket connection is still connected is by catching the result of a read or write operation or possibly catching an exception.
For more information please refer to this StackOverflow question:
Instantly detect client disconnection from server socket
Here is a small snippet of code that is simply using a Socket in non-blocking mode and connected to a server.
try
{
bytesRead = nambSok.Receive(message, 4096, SocketFlags.None);
}
catch (SocketException e)
{
//a socket error has occured
switch (e.SocketErrorCode)
{
case System.Net.Sockets.SocketError.TimedOut:
case System.Net.Sockets.SocketError.WouldBlock:
if (doDisconnect == false)
{
continue;
}
break;
case System.Net.Sockets.SocketError.ConnectionReset:
case System.Net.Sockets.SocketError.ConnectionAborted:
isConnected = false;
break;
}
}
if (bytesRead > 0)
{
/* do something with data */
}
The "keep-alive" method Lior Ohana proposed is also a great idea. Force each client to "check in" every X seconds. The client can detect the server is gone if an exception occurs on the write, and the server knows the client has gone away if a "keep-alive" message hasn't been received in X seconds.
I agree with Lior Ohana because I had this problem with remote devices that were used GPRS Tcp connection. when device is turn off or disconnected, it did not alert to the sever. There for I used this code: I send Specific time to the clients
void AnalyzeForHandShaking(Socket socketin, string Message)
{
Socket handler = socketin;
try
{
Message = Message.Trim();
if (!string.IsNullOrEmpty(Message.Trim()))
// if (Message.Equals("~"))
{
// string Serial = getSerialFromSocket(socketin).Trim();
DateTime dt = DateTime.Now;
if (handler!=null)
//if there is serial in hastable
if (!arrIPTimeHandShaking.ContainsKey(handler))
{
arrIPTimeHandShaking.Add(handler, dt);
}
else
{
arrIPTimeHandShaking[handler] = dt;
}
}
}
catch
{
}
}

Categories

Resources