【发布时间】:2014-03-02 17:12:54
【问题描述】:
有人可以帮我解决这个问题吗...我整天都在苦苦挣扎。
所以我正在尝试学习异步套接字,这给我带来了麻烦。
问题基本上是我用加入聊天室名称的人更新列表框的方式:
基本上我所做的是让每个客户端在加入服务器时发送 "!!addlist [nickname]"。
这并不理想,因为它不检查重复项等,但现在我只想知道为什么它不起作用。 每当有人添加他们以前从未见过的名字时,他们也会发送 "!!addlist [nick]"
这样,每次有人加入时,都应该为每个人更新列表。 问题似乎是所有客户端同时开始通信并干扰缓冲区。
我尝试为每个客户端使用单独的缓冲区,所以这不是问题。 我试过使用 lock() 但这似乎也不起作用。
基本上发生的事情是缓冲区似乎被截断;在同一个缓冲区中有来自两个不同人的数据。
请告诉我我在缓冲区或客户端做错了什么:
请注意,异步套接字使用的是 Send 而不是 BeginSend。 我已经尝试了这两种方法,但它们遇到了同样的问题......所以它可能是客户端?
public partial class Login : Form
{
private ChatWindow cw;
private Socket serverSocket;
private List<Socket> socketList;
private byte[] buffer;
private bool isHost;
private bool isClosing;
public void startListening()
{
try
{
this.isHost = true; //We're hosting this server
cw.callingForm = this; //Give ChatForm the login form (this) [that acts as the server]
cw.Show(); //Show ChatForm
cw.isHost = true; //Tell ChatForm it is the host (for display purposes)
this.Hide(); //And hide the login form
serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text))); //Bind to our local address
serverSocket.Listen(1); //And start listening
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //When someone connects, begin the async callback
cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text); //And have ChatForm connect to the server
}
catch (Exception) { /*MessageBox.Show("Error:\n\n" + e.ToString());*/ } //Let us know if we ran into any errors
}
public void AcceptCallback(IAsyncResult AR)
{
try
{
Socket s = serverSocket.EndAccept(AR); //When someone connects, accept the new socket
socketList.Add(s); //Add it to our list of clients
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s); //Begin the async receive method using our buffer
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //And start accepting new connections
}
catch (Exception) {}
}
public void ReceiveCallback(IAsyncResult AR) //When a message from a client is received
{
try
{
if (isClosing)
return;
Socket s = (Socket)AR.AsyncState; //Get the socket from our IAsyncResult
int received = s.EndReceive(AR); //Read the number of bytes received (*need to add locking code here*)
byte[] dbuf = new byte[received]; //Create a temporary buffer to store just what was received so we don't have extra data
Array.Copy(buffer, dbuf, received); //Copy the received data from our buffer to our temporary buffer
foreach (Socket client in socketList) //For each client that is connected
{
try
{
if (client != (Socket)AR.AsyncState) //If this isn't the same client that just sent a message (*client handles displaying these*)
client.Send(dbuf); //Send the message to the client
}
catch (Exception) { }
} //Start receiving new data again
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
}
catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
}
public void SendCallback(IAsyncResult AR)
{
try
{
Socket s = (Socket)AR.AsyncState;
s.EndSend(AR);
}
catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
}
这是客户端:
public void getData()
{
try
{
byte[] buf = new byte[1024];
string message = "";
while(isConnected)
{
Array.Clear(buf, 0, buf.Length);
message = "";
clientSocket.Receive(buf, buf.Length, SocketFlags.None);
message = Encoding.ASCII.GetString(buf);
if (message.StartsWith("!!addlist"))
{
message = message.Replace("!!addlist", "");
string userNick = message.Trim();
if (!namesBox.Items.Contains(userNick))
{
addNick(userNick.Trim());
}
continue;
}
else if (message.StartsWith("!!removelist"))
{
message = message.Replace("!!removelist", "");
string userNick = message.Trim();
removeNick(userNick);
output("Someone left the room: " + userNick);
continue;
}
else if (!namesBox.Items.Contains(message.Substring(0, message.IndexOf(":"))))
{
addNick(message.Substring(0, message.IndexOf(":")).Trim()); //So they at least get added when they send a message
}
output(message);
}
}
catch (Exception)
{
output("\n\nConnection to the server lost.");
isConnected = false;
}
}
这是我的 addNick 函数,似乎可以解决一些问题?
public void addNick(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
output("Someone new joined the room: " + n);
sendRaw("!!addlist " + nickName);
}
}
我认为问题在于某些数据包被跳过了?
在 Receive 之后再次调用之前,客户端中可能有太多代码?
我应该为每条消息创建一个单独的线程,以便接收不断运行吗? (哑巴)
我是否应该让我的客户也使用异步接收和发送?
我有一种感觉就是答案^
通过我所做的所有检查,我设法清理了重复名称问题......但我经常收到来自其他客户端的带有空格和部分消息的消息。
【问题讨论】:
-
getData 是否在自己的线程中运行?如果是这样,那可能是您的问题。向您的列表框添加内容的 CrossThread 异常。
-
有趣;我没有想到这一点。是的。我应该在编辑时锁定它吗?我经常有几个线程在不使用委托的情况下写入消息框,而且它似乎从来没有随机添加额外的文本
-
@user1274820 这就是未定义行为的美妙之处。您应该使用
Control.Invoke()在 UI 线程上执行访问,而不是锁定控件。 -
将 .Add 加入到 Invoke/BeginInvoke 调用中,看看它是否变得更稳定。这就是这类 CrossThread 问题的问题,有时它会起作用......它可能是零星的,很难追踪。
-
实际上,您可能需要将大部分方法添加到 Invoke 调用中。作为 Winforms 中的一般规则,您永远不能在单独的线程上触摸 UI 控件。即使访问 .Items 也会导致问题。
标签: c# multithreading sockets asynchronous chat