How to handle client disconnect in Socket Programming? - c#

I have written a server side code using Sockets, Its working fine but have one problem I don't know how to handle this scenario: if client just closes application without sending Disconnect request, my server side program crashes. What do I need to do to avoid this? Please guide me I am new to Socket programming.
private void OnReceive(IAsyncResult result)
{
try
{
Socket clientSocket = (Socket)result.AsyncState;
clientSocket.EndReceive(result);
command = responseMessage = string.Empty;
command = ByteToString(receviedData);
receviedData = new byte[30];
if (command=="Connect")
{
ClientInfo clientInfo = new ClientInfo();
clientInfo.socket = clientSocket;
clientInfo.IP = clientSocket.RemoteEndPoint.ToString();
connectedClients.Add(clientInfo);
responseMessage = "Connection established...";
}
else if (command=="Disconnect")
{
for (int i = 0; i < connectedClients.Count; i++)
{
if (connectedClients[i].socket == clientSocket)
{
connectedClients.RemoveAt(i);
break;
}
}
clientSocket.Close();
}
else
{
responseMessage = "Error";
}
byte[] responseStatus = StringToByte(responseMessage);
for (int i = 0; i < connectedClients.Count; i++)
{
if (connectedClients[i].socket==clientSocket)
{
connectedClients[i].socket.BeginSend(responseStatus, 0, responseStatus.Length,SocketFlags.None, new AsyncCallback(OnSend), connectedClients[i].socket);
break;
}
}
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}
}

Your application crashes, because you throw an exception in the catch block of your method.
If you don't want your application to crash, you need to remove the throw new Exception(ex.Message); line from the catch block.
Replace it with code that handles the error and gracefully restores your application to a safe state. From reading your code, this should be done by removing the clientSocket from connectedClients
Secondly, it is better to just use throw; instead of throw new Exception(ex.Message);. throw; will re-throw the original exception object and thus preserve the stack trace and other vital information that helps in debugging your software.
Using new Exception("Message") will create a completely new exception object with the current stack trace.
private void OnReceive(IAsyncResult result)
{
try
{
Socket clientSocket = (Socket)result.AsyncState;
clientSocket.EndReceive(result);
command = responseMessage = string.Empty;
command = ByteToString(receviedData);
receviedData = new byte[30];
if (command=="Connect")
{
ClientInfo clientInfo = new ClientInfo() {
socket = clientSocket,
IP = clientSocket.RemoteEndPoint.ToString(),
};
connectedClients.Add(clientInfo);
responseMessage = "Connection established...";
}
else if (command=="Disconnect")
{
removeClientInfo(clientSocket);
clientSocket.Close();
}
else
{
responseMessage = "Error";
}
byte[] responseStatus = StringToByte(responseMessage);
for (int i = 0; i < connectedClients.Count; i++)
{
if (connectedClients[i].socket==clientSocket)
{
connectedClients[i].socket.BeginSend(responseStatus, 0, responseStatus.Length,SocketFlags.None, new AsyncCallback(OnSend), connectedClients[i].socket);
break;
}
}
}
catch(Exception ex)
{
// add error handling and gracefully recover
// caution: The way done here, might work, but it smells :-/
removeClientInfo((Socket)result.AsyncState);
((Socket)result.AsyncState).Close();
}
}
/// removes the client info from the connectedClients enumerable
private void removeClientInfo(Socket socket)
{
for (int i = 0; i < connectedClients.Count; i++)
{
if (connectedClients[i].socket == socket)
{
connectedClients.RemoveAt(i);
break;
}
}
}

You are throwing a new exception inside the catch block, which doesn't make much sense unless you're doing some logging or similar. Change the catch block like:
catch(SocketException)
{
}
Also, you should check the number of bytes read returned from EndReceive. If you receive zero bytes, that means the client has shutdown the connection:
int numReceived = clientSocket.EndReceive(result);
if(numReceived == 0)
{
//client has shutdown the connection
}

Related

StreamReader seemingly advancing after ReadLine() even if there's nothing to read

I have a client/server app.
On my client app there is a loop to handle client communication. There's a StreamReader with the ReadLine command. The command should stay locked until it receives something to read. However, in my case, after sending 1 request, on the next cycle the program is advancing after ReadLine is reached even if there's no message sent.
This is throwing an exception in the next line, where I attempt to deserealize the message I received in the line above, since it's trying to desearilize something that is null
private void ClientCommunication(object client)
{
TcpClient tcClient = (TcpClient)client;
StreamReader reader = new StreamReader(tcCliente.GetStream());
StreamWriter serverStreamWriter = new StreamWriter(tcClient.GetStream());
while (serverIsOn)
{
try
{
//This line should be blocked until there's actually something to read.
var message= reader.ReadLine();
//But after handling one request, it moves to this line on its own even if there's no message yet
SocketMessage<object> requestReceived= JsonConvert.DeserializeObject<SocketMessage<object>>(message);//And this is where the exception gets thrown from, because message is eventually null.
if (!connectedClientsDict.ContainsKey(requestReceived.Entity.ToString())){
connectedClientsDict.Add(requestReceived.Entity.ToString(), tcClient);
SelectMethod(requestReceived.Method, message, ref serverStreamWriter);
} else
{
SelectMethod(requestReceived.Metodo, message, ref serverStreamWriter);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error in ClientCommunication", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
}
tcClient.Close();
}
Here's the SelectMethod method
public void SelectMethod(string pMethod, string pMessage, ref StreamWriter serverStreamWriter)
{
switch (pMethod)
{
case "RegisterEmployee":
try {
semaphore = new Semaphore(1, 1);
semaphore.WaitOne();
SocketMessage<Employee> employeeToRegister = JsonConvert.DeserializeObject<SocketMessage<Employee>>(pMessage);
data.RegisterEmployee(employeeToRegister.Entity);
SocketMessage<string> serverRsp = new SocketMessage<string> { Entity= "SERVER", Method= "SUCCESS" };
serverStreamWriter.WriteLine(JsonConvert.SerializeObject(serverRsp));
serverStreamWriter.Flush();
semaphore.Release();
break;
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
SocketMessage<string> serverRsp= new SocketMessage<string> { Entity = ex.Message.ToString(),
Method = "EMPLOYEE ALREADY EXISTS" };
serverStreamWriter.WriteLine(JsonConvert.SerializeObject(serverRsp));
serverStreamWriter.Flush();
semaphore.Release();
break;
}
default:
break;
}
}
This is the method being sent from the client, in case it helps.
public SocketMessage<string> RequestEmployeeRegistration(Employee emp)
{
try
{
serverIp = IPAddress.Parse("127.0.0.1");
client = new TcpClient();
serverEndPoint = new IPEndPoint(serverIp, 16830);
SocketMessage<string> serverRsp;
string serializedRsp;
client.Connect(serverEndPoint);
SocketMessage<Emp> empToRegister = new SocketMessage<Employee> { Method = "RegisterEmployee", Entidad = emp };
clientStreamReader = new StreamReader(client.GetStream());
clientStreamWriter = new StreamWriter(client.GetStream());
clientStreamWriter.WriteLine(JsonConvert.SerializeObject(empToRegister ));
clientStreamWriter.Flush();
serializedRsp = clientStreamReader.ReadLine();
serverRsp = (JsonConvert.DeserializeObject<SocketMessage<string>>(serializedRsp));
client.Close();
return serverRsp;
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
client.Close();
return new SocketMessage<string> { Entity = "ERROR", Method = ex.Message };
}
}

"Cannot access a disposed object.\r\nObject name: 'System.Net.Sockets.NetworkStream'

In the ClientClass, I have two threads; one for TCP-IP connection to a localhost and another to keep receiving messages from the localhost.
In case the connection gets broken, I close the connection using m_DeviceClientSocket.Close() inside the catch block of the method ConnectToDeviceAndMonitorConnection().
PROBLEM: Since I close the socket connection, therefore, the whole m_DeviceClientSocket object is dereferenced. This leads to another error inside the catch block of the method GetMessagesFromDevice() and the error says (as shown in the catch block of the code):
"Cannot access a disposed object.\r\nObject name:
'System.Net.Sockets.NetworkStream'
QUESTION: How should I create the m_DeviceClientSocket object, which can be closed, reconnected and is available to both the threads?
class ClientClass
{
Thread t1_DeviceConnectionMonitor, t2_receiveDeviceMessages;
static readonly object m_IsConnectedToDevice_Locker = new object();
bool m_IsConnectedToDevice = false;
//Device - Communication Variables
string m_DeviceURL = "127.0.0.1";
int m_DevicePort = 23;
TcpClient m_DeviceClientSocket = new TcpClient();
NetworkStream m_DeviceServerStream = default(NetworkStream);
public ClientClass()
{
//Thread for CONNECTION Monitoring
t1_DeviceConnectionMonitor = new Thread(ConnectToDeviceAndMonitorConnection);
t_DeviceConnectionMonitor.Start();
//Thread to RECEIVE messages
t2_receiveDeviceMessages = new Thread(GetMessagesFromDevice);
t2_receiveDeviceMessages.Start();
}
//Connect to Device
void ConnectToDeviceAndMonitorConnection()
{
while (true)
{
if (!m_IsConnectedToDevice)
{
try
{
//Connect to device server
m_DeviceClientSocket.Connect(m_DeviceURL, m_DevicePort);
m_DeviceServerStream = m_DeviceClientSocket.GetStream();
SetDeviceConnectionStatus(true);
}
catch (SocketException se)
{
if (m_DeviceClientSocket.Connected)
m_DeviceClientSocket.Close(); //This Close() statement dereference the complete "m_DeviceClientSocket" object
}
}
}
}
//RECEIVE messages from the device
public void GetMessagesFromDevice()
{
string messageReceivedFromDevice;
while (true)
{
try
{
var buffersize = m_DeviceClientSocket.ReceiveBufferSize;
byte[] instream = new byte[buffersize];
int status = m_DeviceServerStream.Read(instream, 0, buffersize);
if (status == 0)
{
SetDeviceConnectionStatus(false);
}
messageReceivedFromDevice = System.Text.Encoding.ASCII.GetString(instream);
}
catch (Exception e)
{
//I ENETER HERE AND THE EXCEPTIONS SAYS: ""Cannot access a disposed object.\r\nObject name: 'System.Net.Sockets.NetworkStream'"
}
}
}
//Thread Safety is needed to set the device connection status
public void SetDeviceConnectionStatus(bool status)
{
lock (m_IsConnectedToDevice_Locker)
{
m_IsConnectedToDevice = status;
}
}
}

Error Handling in TcpClient Socket

I am new to Sockets this is my Code to send and receive data from a server.
This Code Works fine as long as the Client is able to receive data from the server.
In Case the Server does not sends the answer back in the given time then the application needs to send "No Answer Received".
How can i know if the recvBuffer is empty or NULL.
Currently the if Condition for the recvBuffer does not work and the application tries to send empty buffer which results in "System.IndexOutOfRangeException error".
class GetSocket
{
public string SocketSendReceive(string server, int port, string cmd)
{
byte[] recvBuffer = new byte[1024];
TcpClient tcpClient = new TcpClient();
tcpClient.Client.ReceiveTimeout = 200;
try
{
tcpClient.Connect(server, 6100);
}
catch (SocketException e)
{
MessageBox.Show(e.Message);
}
if (tcpClient != null && tcpClient.Connected)
{
try
{
tcpClient.Client.Send(Encoding.UTF8.GetBytes(cmd));
tcpClient.Client.Receive(recvBuffer);
}
catch (SocketException e)
{
MessageBox.Show(e.ErrorCode.ToString());
}
tcpClient.GetStream().Close();
tcpClient.Client.Close();
tcpClient.Client.Dispose();
tcpClient = null;
string tmp = Encoding.ASCII.GetString(recvBuffer, 0, recvBuffer.Length);
if (recvBuffer != null && recvBuffer.Length > 0)
{
string[] words = tmp.Split(null);
return words[1];
}
else
{
return ("No Answer Received");
}
}
return null;
}
}
The Following code works fine after making changes as suggested.
class GetSocket
{
public string SocketSendReceive(string server, int port, string cmd)
{
byte[] recvBuffer = new byte[1024];
TcpClient tcpClient = new TcpClient();
tcpClient.Client.ReceiveTimeout = 200;
string tmp;
try
{
tcpClient.Connect(server, 6100);
}
catch (SocketException e)
{
MessageBox.Show(e.Message);
}
if (tcpClient != null && tcpClient.Connected)
{
try
{
tcpClient.Client.Send(Encoding.UTF8.GetBytes(cmd));
tcpClient.Client.Receive(recvBuffer);
tmp = Encoding.ASCII.GetString(recvBuffer, 0, recvBuffer.Length);
string[] words = tmp.Split(null);
return words[1];
}
catch (SocketException e)
{
return ("No Answer Received");
}
}
return null;
}
}

Read data from StreamSocketListener on RFComm bluetooth connected device C#

I'm developing an universal application Win8.1 / WP8.1
I'm able to discover and connect to the paired bluetooth devices (Stick readers - Rfid)
This is how I'm connecting
Variables
private IAsyncOperation<RfcommDeviceService> connectService;
private IAsyncAction connectAction;
private RfcommDeviceService rfcommService;
private RfcommServiceProvider rfcommProvider;
private StreamSocketListener listener;
private DataReader reader;
private DataWriter writer;
//Connection
public async Task ConnectToServiceAsync(string name)
{
DeviceInformation serviceInfo = null;
foreach (var device in devices)
{
if(device.Name == name)
{
serviceInfo = device;
break;
}
}
if (serviceInfo != null)
{
this.State = BluetoothConnectionState.Connecting;
try
{
// Initialize the target Bluetooth RFCOMM device service
connectService = RfcommDeviceService.FromIdAsync(serviceInfo.Id);
rfcommService = await connectService;
if (rfcommService != null)
{
rfcommProvider = await RfcommServiceProvider.CreateAsync(rfcommService.ServiceId);
// Create a socket and connect to the target
listener = new StreamSocketListener();
listener.ConnectionReceived += Listener_ConnectionReceived;
connectAction = listener.BindServiceNameAsync(rfcommService.ServiceId.AsString(), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
await connectAction;//to make it cancellable
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
this.State = BluetoothConnectionState.Connected;
}
else
OnExceptionOccuredEvent(this, new Exception("Unable to create service.\nMake sure that the 'bluetooth.rfcomm' capability is declared with a function of type 'name:serialPort' in Package.appxmanifest."));
}
catch (TaskCanceledException)
{
this.State = BluetoothConnectionState.Disconnected;
}
catch (Exception ex)
{
this.State = BluetoothConnectionState.Disconnected;
OnExceptionOccuredEvent(this, ex);
}
}
}
//Then wait for a connection over the listener
private async void Listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
DataReader inputreader = new DataReader(args.Socket.InputStream);
while (true)
{
try
{
inputreader.InputStreamOptions = InputStreamOptions.Partial;
// Read first byte (length of the subsequent message, 255 or less).
uint sizeFieldCount = await inputreader.LoadAsync(1);
if (sizeFieldCount != 1)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message.
uint messageLength = inputreader.ReadByte();
uint actualMessageLength = await inputreader.LoadAsync(messageLength);
if (messageLength != actualMessageLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the message and process it.
string message = inputreader.ReadString(actualMessageLength);
OnMessageReceivedEvent(this, message);
}
catch (Exception ex)
{
if (inputreader != null)
OnExceptionOccuredEvent(this, ex);
}
}
}
The problem is that the Bluetooth Stick Reader never send a connection request, it just read the rfid device ID and sends it over the serial port.
So, I'm able to connect to the device but I don't know how to actively listen or read the incoming data.
Any help will be appreciated.
RFCOMM communication is Client/Server model. You need to define a server to broadcast the service and a client connected to the service.
As I saw from the code you post, it only includes the server side code which provides the service but there was no client connecting to service. Only when the client connected to the service, the ConnectionReceived event will be fired.
I have written a sample which host the RFCOMM service in a console application and consume the service in Windows Runtime App before (Code Sample).
The client code is as following:
rfcommServiceInfoCollection = await DeviceInformation.FindAllAsync(
RfcommDeviceService.GetDeviceSelector(RfcommServiceId.ObexObjectPush));
var count = rfcommServiceInfoCollection.Count;
Debug.WriteLine("Count of RFCOMM Service: " + count);
if(count > 0)
{
lock (this)
{
streamSocket = new StreamSocket();
}
var defaultSvcInfo = rfcommServiceInfoCollection.FirstOrDefault();
rfcommDeviceService = await RfcommDeviceService.FromIdAsync(defaultSvcInfo.Id);
if(rfcommDeviceService == null)
{
Debug.WriteLine("Rfcomm Device Service is NULL, ID = {0}", defaultSvcInfo.Id);
return;
}
Debug.WriteLine("ConnectionHostName: {0}, ConnectionServiceName: {1}", rfcommDeviceService.ConnectionHostName, rfcommDeviceService.ConnectionServiceName);
await streamSocket.ConnectAsync(rfcommDeviceService.ConnectionHostName, rfcommDeviceService.ConnectionServiceName);
By the way, do not forget to add the RFCOMM capabilities in your appxmanifest.
For example:
<m2:DeviceCapability Name="bluetooth.rfcomm">
<m2:Device Id="any">
<m2:Function Type="name:obexObjectPush" />
</m2:Device>
</m2:DeviceCapability
As Jeffrey said, this is a client/Server model, but in my case the client doesn't have any logic, and is not capable to request any connection.
Thank you for your samples, It helped a lot.
After trying several things I got some code working, and a "server" running and listening in the rfcomm service:
public async Task ConnectToServiceAsync(string name)
{
lock(this.interlock)
{
readBuffer = String.Empty;
}
DeviceInformation serviceInfo = null;
foreach (var device in devices)
{
if(device.Name == name)
{
serviceInfo = device;
break;
}
}
if (serviceInfo != null)
{
DeviceName = serviceInfo.Name;
this.State = BluetoothConnectionState.Connecting;
try
{
// Initialize the target Bluetooth RFCOMM device service
connectService = RfcommDeviceService.FromIdAsync(serviceInfo.Id);
rfcommService = await connectService;
if (rfcommService != null)
{
// Create a socket and connect to the target
socket = new StreamSocket();
connectAction = socket.ConnectAsync(rfcommService.ConnectionHostName, rfcommService.ConnectionServiceName, SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
await connectAction;//to make it cancellable
writer = new DataWriter(socket.OutputStream);
reader = new DataReader(socket.InputStream);
State = BluetoothConnectionState.Connected;
Task taskReceive = Task.Run(async () => { ListenForMessagesAsync(socket); });
taskReceive.ConfigureAwait(false);
}
else
OnExceptionOccuredEvent(this, new Exception("Unable to create service.\nMake sure that the 'bluetooth.rfcomm' capability is declared with a function of type 'name:serialPort' in Package.appxmanifest."));
}
catch (TaskCanceledException)
{
this.State = BluetoothConnectionState.Disconnected;
}
catch (Exception ex)
{
this.State = BluetoothConnectionState.Disconnected;
OnExceptionOccuredEvent(this, ex);
}
}
}
And the listener in
private async Task ListenForMessagesAsync(StreamSocket localsocket)
{
while (socket != null)
{
try
{
string message = String.Empty;
DataReader dataReader = new DataReader(localsocket.InputStream);
dataReader.InputStreamOptions = InputStreamOptions.Partial;
// Read the message and process it.
lock (this.interlock)
{
if (!message.Contains("\r\n"))
readBuffer = readBuffer + message;
else
{
var data = message.Split('\r');
readBuffer = readBuffer + data[0];
}
if (readBuffer.Length == 15)
{
readBuffer = readBuffer.Replace("\r\n", "");
OnMessageReceivedEvent(this, readBuffer);
readBuffer = String.Empty;
}
if (readBuffer.Length > 15 || (readBuffer.Length < 15 && readBuffer.Contains("\r\n")))
readBuffer = String.Empty;
}
}
catch (Exception ex)
{
if (socket != null)
OnExceptionOccuredEvent(this, ex);
}
}
}

Handling exceptions in Socket programming

public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
String text = textBox1.Text;
UdpClient udpc = new UdpClient(text,8899);
IPEndPoint ep = null;
while (true)
{
MessageBox.Show("Name: ");
string name = "Connected";
if (name == "") break;
byte[] sdata = Encoding.ASCII.GetBytes(name);
udpc.Send(sdata, sdata.Length);
if (udpc.Receive(ref ep)==null)
{
MessageBox.Show("Host not found");
}
else
{
byte[] rdata = udpc.Receive(ref ep);
string job = Encoding.ASCII.GetString(rdata);
MessageBox.Show(job);
}
}
}
I'm getting an error message when my remote machine is not connected . with this code im getting the errror.
if (udpc.Receive(ref ep)==null)
error message is socket exception was unhanded ( An existing connection was forcibly closed by the remote host)
Wrap the code in a
try
{
}
catch(SocketException se)
{
}
catch(Exception ex)
{
}
And start here for documentation on SocketException
http://msdn.microsoft.com/en-us/library/system.net.sockets.socketexception.aspx
If the exception is unhandled, so handle the exception:
while (true)
{
MessageBox.Show("Name: ");
string name = "Connected";
if (name == "") break;
byte[] sdata = Encoding.ASCII.GetBytes(name);
try{
udpc.Send(sdata, sdata.Length);
byte[] rdata = udpc.Receive(ref ep);
string job = Encoding.ASCII.GetString(rdata);
MessageBox.Show(job);
}
catch(Exception ex)
{
MessaageBox.show(ex.toString());
}
}
The try...catch block is always recommended when the program accesses external resources (dbs, queues, file systems, http connections, udp sockets etc.)
You can wrap your infinite loop in a try catch block.
try
{
while(true)
{
//your code
}
}
catch(Exception exception)
{
//show exception.Message;
}
finally{ //clean up}
Look at how it is done.

Categories