【发布时间】:2020-03-15 17:16:25
【问题描述】:
我的套接字有问题。 基本上我有几个监听器监听我电脑上所有可用的端口。当一个套接字连接到其中一个侦听器时,我会打开一个新的 Socket 连接来处理该连接,然后我的侦听器会重新开始侦听。
但是,当客户端断开连接时,我会继续接收空数据。
正如您在下面的代码中看到的,我放置了一个 Console.WriteLine 来检查接收到的消息的长度。
当连接关闭时,它向我显示了超过 100 行 charLen: 0。这意味着它正在接收一些东西,但我不知道它来自哪里。
然后我还会在WaitData-方法中收到System.ObjectDisposedException 错误
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;
}
下面是 WaitForData 方法:
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);
}
}
现在我开始认为我处理OnDataReceived 中数据的方式有问题。我一直在结合我在网上找到的不同教程来获取此代码,它本身是有效的,只有当客户端断开连接时我才会收到此错误。
我希望有人知道原因。
为了完整起见,您将在下面找到我用来组织连接的客户端的 ClientSocketClass。
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));
}
}
}
这个类被用在一个 BindingList 中,它显示在 DataGridView 中以显示连接的客户端。但我只显示了 ID 和 IP 地址,因此是 [Browsable(false)]
【问题讨论】:
-
接收到零字节意味着传输结束。在那之后你不应该尝试接收更多。 OnDataReceived 的代码在哪里?
-
使用 cmd.exe 检查 >Netstat -a 客户端和服务器上的连接状态,以查看是否已断开连接。仅应从客户端(而不是服务器)关闭连接如果客户端和服务器都尝试同时关闭,则当连接可能最终处于半开半闭状态时,存在竞争条件。连接是 TCP,它需要对每条消息(包括打开和关闭)进行确认。如果客户端和服务器同时关闭,则不会发生其中一个 ACK,因为连接的一侧已经关闭。
-
显示的第一个代码是 onDataReceived。而且我只是从客户端关闭。服务器不会断开连接,它只会在之后清理并将客户端从 connectedClients-list 中删除。
-
与此同时,我在 SO (stackoverflow.com/questions/9545527/…) 上发现了一个类似的问题,但我不确定如何在我的代码中实现它。
标签: c# sockets asynchronous