能给我一些关于C#中基本异步套接字编程的建议吗?
我正在开发(或尝试开发)一个程序,它使用异步套接字在任何时候向服务器和客户端传递字符串 此程序要求连接到服务器的客户端不超过一个。我尝试过Socket编程,但我发现它会阻止程序,直到其中一个收到某些东西 因为我对异步套接字编程只有基本的了解,所以我只选择了我能找到的最简单的一个,或者至少是我能理解的最简单的一个 以下是我的服务器代码:能给我一些关于C#中基本异步套接字编程的建议吗?,c#,asyncsocket,C#,Asyncsocket,我正在开发(或尝试开发)一个程序,它使用异步套接字在任何时候向服务器和客户端传递字符串 此程序要求连接到服务器的客户端不超过一个。我尝试过Socket编程,但我发现它会阻止程序,直到其中一个收到某些东西 因为我对异步套接字编程只有基本的了解,所以我只选择了我能找到的最简单的一个,或者至少是我能理解的最简单的一个 以下是我的服务器代码: public Socket g_server_conn; public byte[] g_bmsg; public bool check
public Socket g_server_conn;
public byte[] g_bmsg;
public bool check = false;
private void net_As_Accept(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
g_server_conn = server_conn.EndAccept(iar);
g_bmsg = new byte[1024];
check = true;
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_As_Send(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
try
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
public void net_Data_Send(string msg2snd) // Function for sending through socket
{
MessageBox.Show(msg2snd);
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_server_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_server_conn);
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do something
}
}
这是我的客户代码:
private void net_As_Connect(IAsyncResult iar)
{
try
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndConnect(iar);
g_bmsg = new byte[1024];
check = true;
string toSendData = "&" + net_Name;
net_Data_Send(toSendData);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
private void net_As_Send(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
public void net_Data_Send(string msg2snd)
{
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_client_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_client_conn);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do Something
}
else if (txt.StartsWith("$"))
{
// Do something Else
}
}
现在,客户端可以很好地连接到服务器。客户端甚至可以向服务器发送一个包含用户名的字符串,该字符串将显示在服务器上。然后,服务器向客户端发送其用户的名称,客户端接收并显示该名称。发送的内容都存储在标签(lblBuffer)中
但之后,假设我有以下代码:
private void btnSendData_Click(object sender, EventArgs e)
{
string posMov = "Stuff to send";
net_Data_Send(posMov);
}
客户没有收到任何东西。在net_Data_Send(msg2snd)函数中放置一个消息框表明服务器实际上发送了消息。事实上,将消息框放在该函数中可以使它工作(客户端接收它),原因我不知道。由于我没有尝试将消息从客户端发送到服务器(客户端连接时的名称除外),因此我假设客户端在发送到服务器时也会遇到同样的问题
此外,当它确实发送第二条消息时(通过在net_Data_send函数中放置一个消息框),只有部分标签(lblBuffer)被覆盖。因此,如果我的名字是“Anon E.Moose”,服务器在客户端连接时发送,我尝试发送,比如说,“0.0”(通过按键),客户端上的标签将显示为“0.0n E.Moose”
我做错了什么?请帮我一下好吗
也许我在网络数据接收和网络数据发送方面有问题?BeginReceive不只是在新数据包到达时调用它的回调(在您的例子中是字符串)。事实上BeginReceive或任何原始套接字方法以基于流的方式工作,而不是基于数据包。有关示例,请参见 您需要做的是,在“net_As_Receive”回调方法中(命名非常糟糕),您需要首先调用socket.EndRecieve(IAsyncResult),它反过来返回当前可用的总字节数。之后,您必须决定是否接收更多数据 例如:
private StringBuilder packetBuilder;
{
if (packetBuilder == null)
packetBuilder = new StringBuilder();
// finalyze the receive
int length = g_server_conn.EndReceive(iar);
if (length != 0)
{
// get the total bytes received. Note that the length property is of that of the number of bytes received rather than that of the buffer
packetBuilder.Append(Encoding.ASCII.GetString(g_bmsg, 0, length));
net_Data_Receive(packetBuilder.ToString());
check = false;
}
// receive the next part
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
注意,这个例子并不关心包。如果你幸运的话,它会起作用,但是有一个很好的变化,要么显示一个字符串的一部分,要么组合两个不同的字符串。一个好的实现将查找字符串结束,并仅显示该部分,同时缓冲其余部分,直到找到新的字符串结束。您还可以使用StreamReader使您的生活更加轻松我认为您需要在客户端上再次调用BeginReceive,看起来您只调用了它一次,因此在它收到服务器名称后,它不再侦听来自服务器的任何数据
private void net_As_Receive(IAsyncResult iar)
{
var bytesRead = g_client_conn.EndReceive(iar);
if (bytesRead != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, bytesRead));
check = false;
}
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
另外,正如我在评论中提到的,使用bytesRead值来计算需要使用多少缓冲区
您需要确定从套接字接收到的数据是否为全部数据,或者是否需要读取更多数据以从另一端组成当前消息。覆盖的问题是,您正在将缓冲区的内容从ascii字节转换为字符串,g_bmsg.Length将返回字节数组的长度,而不是接收调用放入其中的数据的当前长度。您需要存储实际读取的字节数,并使用它,谢谢。它现在可以接收消息了。虽然我还在做标签覆盖的事情。顺便说一下,我的C#中没有“var”。我想只有C#3.0版本才有。将其更改为“int”会弄糟什么吗?不,使用int:)对不起,现在习惯使用var了,这是第二天性!