问题
I'm having an issue with my Sockets. Basically I have a few listeners that listen on all the available ports on my pc. When a socket connects to one of the listeners, I open a new Socket-connection to handle that connection and my listener goes back to listening.
However when a client disconnects I keep on receiving empty data.
As you can see in the code below, I have put a Console.WriteLine to check the length of the received message.
When the connection closes, it's showing me well over a 100 lines with charLen: 0
. Meaning it's receiving something but I don't know where it's coming from.
And then I will also receive a System.ObjectDisposedException
error in the WaitData
-method
ClientSocketClass tmpClient = (ClientSocketClass)asyn.AsyncState;
try
{
// END THE BeginReceive() ASYNCHRONOUS CALL BY CALLING THE EndReceive() METHOD FOR THAT SOCKET
// THIS WILL RETURN THE NUMBER OF CHARACTER WHICH HAS BEEN RECEIVED BY THE CLIENT
int byteMessage = tmpClient.Socket.EndReceive(asyn);
char[] chars = new char[byteMessage + 1];
// EXTRACT THE CHARACTERS INTO A BUFFER
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(tmpClient.BufferSize, 0, byteMessage, chars, 0);
Console.WriteLine("charlen: " + charLen.ToString());
// START WAITING AGAIN FOR NEW DATA FROM THE CLIENT
WaitForData(tmpClient);
Array.Resize(ref chars, charLen);
// PROCESS THE CURRENT MESSAGE
string tempData = new string(chars).Replace("\0", string.Empty);
// LOG THE RECEPTION OF NEW DATA
string log = string.Format("{0}{1}Received: {2}", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", tempData);
Log(LoggingType.Data, LoggingLevel.Debug, log);
// ADD THE MESSAGE TO THE MESSAGE QUEUE
if (MessageQueue != null)
{
if (tempData != null && tempData != string.Empty)
{
MessageQueue.Add(tempData);
}
}
}
catch (ObjectDisposedException)
{
// THIS CODE WILL BE EXECUTED IF THE SOCKET WAS DISCONNECTED
if (tmpClient != null)
{
// GET THE ID OF THE CLIENT
int clientId = tmpClient.Id;
// REMOVE THE CLIENT FROM THE CONNECTED CLIENTS LISTS
removeClient(clientId);
string log = string.Format("{0}{1}Client {2} disconnected", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", clientId);
Log(LoggingType.Status, LoggingLevel.Notification, log);
}
return;
}
Below you see the WaitForData-method:
public void WaitForData(ClientSocketClass selectedClient)
{
try
{
if (pfnWorkerCallBack == null)
{
// SPECIFY THE CALL BACK FUNCTION WHICH SHOULD BE RUN
// WHEN DATA IS RECEIVED FROM THE CLIENT
pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
}
// START RECEIVING THE MESSAGE INTO THE DATA BUFFER
selectedClient.Socket.BeginReceive(selectedClient.BufferSize, 0, selectedClient.BufferSize.Length, SocketFlags.None, pfnWorkerCallBack, selectedClient);
}
catch (SocketException ex)
{
string log = string.Format("{0}\t<WaitForData>\tAn error occured while waiting for data: {1}{2}", DateTime.Now.ToString("HH:mm:ss.fff"), ex.Message, Environment.NewLine);
Log(LoggingType.Error, LoggingLevel.Debug, log);
}
}
Now I'm beginning to think that something is wrong in the way I'm handling the data in the OnDataReceived
. I have been combining different tutorials I found online to get to this code, which in itself is working, only when the client disconnects I'm getting this error.
I'm hoping somebody knows why.
To be complete, below you will find the ClientSocketClass which I'm using to organise my connected clients.
public class ClientSocketClass
{
private int tmpId;
private string tmpIp;
private byte[] tmpBuffer;
public event PropertyChangedEventHandler PropertyChanged;
public int Id
{
get { return tmpId; }
set
{
tmpId = value;
this.NotifyPropertyChanged("Id");
}
}
public string Ip
{
get { return tmpIp; }
set
{
tmpIp = value;
this.NotifyPropertyChanged("Ip");
}
}
[Browsable(false)]
public Socket Socket { get; set; }
[Browsable(false)]
public byte[] BufferSize
{
get { return tmpBuffer; }
set
{
tmpBuffer = value;
this.NotifyPropertyChanged("BufferSize");
}
}
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
This class is being used in a BindingList which is displayed in a DataGridView to show the connected clients. But I'm only showing the Id and the IP Address, hence the [Browsable(false)]
回答1:
I was able to find the answer here on StackOverflow (https://stackoverflow.com/a/9546493/4425684)
What I didn't realize was that WaitForData actually generated a loop and I needed to escape the loop when I was receiving NULL.
So by just adding the below code, the problem was solved.
if (byteMessage <= 0)
{
// LEAVE THE LOOP
return;
}
After some cleanup, the new code looks like this:
public void OnDataReceived(IAsyncResult asyn)
{
ClientSocketClass tmpClient = (ClientSocketClass)asyn.AsyncState;
try
{
// END THE BeginReceive() ASYNCHRONOUS CALL BY CALLING THE EndReceive() METHOD FOR THAT SOCKET
// THIS WILL RETURN THE NUMBER OF CHARACTER WHICH HAS BEEN RECEIVED BY THE CLIENT
int byteMessage = tmpClient.Socket.EndReceive(asyn);
// READ THE CONTENT
string tmpContent = Encoding.UTF8.GetString(tmpClient.BufferSize, 0, byteMessage);
// CHECK IF WE HAVE REACHED THE END OF THE RECEIVED MESSAGE
if (byteMessage <= 0)
{
// LEAVE THE LOOP
return;
}
// START WAITING AGAIN FOR NEW DATA FROM THE CLIENT
WaitForData(tmpClient);
// PROCESS THE CURRENT MESSAGE
string tempData = tmpContent.Replace("\0", string.Empty);
// LOG THE RECEPTION OF NEW DATA
string log = string.Format("{0}{1}Received: {2}", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", tempData);
Log(LoggingType.Data, LoggingLevel.Debug, log);
// ADD THE MESSAGE TO THE MESSAGE QUEUE
if (MessageQueue != null)
{
if (tempData != null && tempData != string.Empty)
{
MessageQueue.Add(tempData);
}
}
}
catch (ObjectDisposedException)
{
// THIS CODE WILL BE EXECUTED IF THE SOCKET WAS DISCONNECTED
if (tmpClient != null)
{
// GET THE ID OF THE CLIENT
int clientId = tmpClient.Id;
// REMOVE THE CLIENT FROM THE CONNECTED CLIENTS LISTS
removeClient(clientId);
string log = string.Format("{0}{1}Client {2} disconnected", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", clientId);
Log(LoggingType.Status, LoggingLevel.Notification, log);
}
return;
}
}
I do think that there's still an issue with this code, because I think that when a message is received which is larger than my buffer, the message will not be completely parsed since I'm only processing the message after the WaitForData
, but I'm not completely sure.
For now the problem is solved because I have made my buffer very large, but if someone wants to correct it, please feel free.
来源:https://stackoverflow.com/questions/60695596/why-am-i-receiving-data-when-my-socket-has-disconnected