我可以对 C# 中的基本异步套接字编程有一些建议吗?
我正在开发(或者无论如何尝试)一个使用异步套接字的程序,据说可以随时在服务器和客户端之间传递字符串。
该程序要求不超过一个客户端连接到服务器。我尝试了套接字编程,但我发现它会阻塞程序,直到任何一方收到某些内容。
由于我对异步套接字编程只有基本的了解,因此我只是选择我能找到的最简单的一种,或者至少是我能理解的最简单的一种。
这是我的服务器代码:
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.0” n E.驼鹿”。
我做错了什么?我可以帮忙吗?
也许我对 net_Data_Receive 和 net_Data_Send 有问题?
I've developing (or trying to, anyway) a program that uses Asynchronous Socket to, supposedly, pass strings to and fro the server and client, at any time.
This program requires no more than one client be connected to a server. I tried Socket Programming, but I found out it blocks the program until either one receives something.
Since I have only a basic understanding of Asynchronous socket programming, I just went for the simplest one I could find, or at least, the simplest one I could understand.
Here's my code for the Server:
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
}
}
And here's my code for the Client:
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
}
}
Now, the Client can connect to the Server fine. The Client can even send in a string containing the user's name to the Server, which will then be displayed on the Server. The Server then sends out the name of its user to the Client, which the client receives and displays. Whatever is sent is stored in a Label (lblBuffer)
But afterwards, say I have the following code:
private void btnSendData_Click(object sender, EventArgs e)
{
string posMov = "Stuff to send";
net_Data_Send(posMov);
}
The Client receives nothing. Putting a Message Box in net_Data_Send(msg2snd) function reveals that the server does in fact send out the message. In fact, putting in the Message Box in that function makes it work (the Client receives it), for reasons I don't know. Since I haven't tried sending a message from the Client to the Server (other than the name when the Client Connects), I assume the Client will have the same problem sending to the Server.
Also, when it does send the second message (by putting a Message Box in the net_Data_Send function), only parts of the Label (lblBuffer) are overwritten. So if I my name is "Anon E. Moose", and the Server sends that when the Client connects, and I try to send out, say, "0.0" (via button press) the Label on the Client would then read "0.0n E. Moose".
What did I do wrong? Can I have some help on this, please?
Perhaps I have a problem with net_Data_Receive and net_Data_Send?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为您需要再次在客户端上调用 BeginReceive ,看起来您只调用它一次,因此在它收到服务器名称后,它也不再侦听来自服务器的任何数据
,正如我在我的文章中提到的注释,使用 bytesRead 值来计算需要使用多少缓冲区。
您需要确定从套接字接收到的数据是否是全部,或者是否需要读取更多数据来弥补来自另一端的当前消息。
I think you need to call BeginReceive on your client again, it looks like you are only calling it once, so after it has received the server name, it isn't listening for any more data from the server
also, as I mentioned in my comment, use the bytesRead value to work out how much of the buffer you need to use.
You will need to work out if the data you have received from the socket is the full amount, or if you need to read more data to make up the current message from the other side.
BeginReceive 不仅仅在新数据包(在您的情况下为字符串)到达时调用其回调。实际上。 BeginReceive 或任何原始套接字方法以基于流的方式工作,而不是基于数据包。请参阅http://msdn.microsoft.com/en-us/library/bew39x2a。 aspx 为例。
您需要做的是在“net_As_Receive”回调方法中(在我看来,命名很糟糕),您需要首先调用 socket.EndRecieve(IAsyncResult),这又返回当前可用的总字节数。之后,您必须决定是否接收更多数据。
例如:
请注意,此示例不关心包。如果你幸运的话它会起作用,但有一个很好的变化,要么显示字符串的一部分,要么组合 2 个不同的字符串。一个好的实现将查找字符串结尾并仅显示该部分,同时缓冲其余部分,直到找到新的字符串结尾。您还可以使用 StreamReader 让您的生活更轻松
BeginReceive doesn't just call its callback whenever a new packet (string in your case arrives). In fact. BeginReceive or any raw socket method works in a stream based fasion, not packet based. See http://msdn.microsoft.com/en-us/library/bew39x2a.aspx for an example.
What you need to do, is in your 'net_As_Receive' callback method (naming is terrible imo), you need to make a call first to socket.EndRecieve(IAsyncResult), which in turn returns the total bytes currently available. After that, you have to make a decision whether to receive more data or not.
For example:
Note that this example doesnt care about packages. It will work if your lucky but there is a good change either a part of a string will be shown or 2 different strings will be combined. A good implementation will look for a string end and only show that part while buffering the rest untill a new string end is found. You can also use a StreamReader for making your life much easier