我正在开发(或尝试,无论如何)一个使用异步套接字的程序,据说可以随时在服务器和客户端之间传递字符串。
此程序只需要将一个客户端连接到服务器。我尝试过Socket编程,但我发现它阻止了程序,直到任何一个人收到了什么。
由于我对异步套接字编程只有基本的了解,所以我只选择了我能找到的最简单的一个,或者至少是我能理解的最简单的一个。
这是我的服务器代码:
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
}
}
现在,客户端可以正常连接到服务器。客户端甚至可以将包含用户名称的字符串发送到服务器,然后该服务器将显示在服务器上。然后,服务器将其用户的名称发送给客户端,客户端接收并显示该用户的名称。无论发送什么都存储在Label(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函数中放置一个消息框),只会覆盖部分Label(lblBuffer)。所以,如果我的名字是“Anon E. Moose”,并且服务器在客户端连接时发送,并且我尝试发送,例如,“0.0”(通过按下按钮),则客户端上的标签将显示为“0.0 n E. Moose“。
我做错了什么?我可以帮忙吗?
也许我的net_Data_Receive和net_Data_Send有问题?
答案 0 :(得分:1)
BeginReceive不只是在新数据包(你的案例中的字符串到达)时调用它的回调。事实上。 BeginReceive或任何原始套接字方法在基于流的fasion中工作,而不是基于数据包。有关示例,请参阅http://msdn.microsoft.com/en-us/library/bew39x2a.aspx。
你需要做的是,你的'net_As_Receive'回调方法(命名是可怕的imo),你需要先调用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让您的生活更轻松
答案 1 :(得分:1)
我认为您需要再次在您的客户端上调用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值来计算出你需要使用多少缓冲区。
如果您从套接字收到的数据是全额,或者如果您需要阅读更多数据来补充来自另一方的当前消息,则需要确定。