我可以对C#中的基本异步套接字编程有一些建议吗?

时间:2011-11-03 02:39:04

标签: c# asyncsocket

我正在开发(或尝试,无论如何)一个使用异步套接字的程序,据说可以随时在服务器和客户端之间传递字符串。

此程序只需要将一个客户端连接到服务器。我尝试过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有问题?

2 个答案:

答案 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值来计算出你需要使用多少缓冲区。

如果您从套接字收到的数据是全额,或者如果您需要阅读更多数据来补充来自另一方的当前消息,则需要确定。