C#异步套接字服务器-回调中的BeginReceive [英] C# Async Socket Server - BeginReceive in Callback

查看:42
本文介绍了C#异步套接字服务器-回调中的BeginReceive的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用C#编写的简单异步套接字服务器(几乎是Microsoft的示例),但是此示例的问题在于它仅接受来自客户端的一条消息,然后关闭.我希望此服务器在收到任何消息后仍保持活动状态.

I have a simple asynchronous socket server written in C# (pretty much Microsoft's example), however the issue with this example is that it accepts only one message from a client and then shuts down. I want this server to stay alive after receiving any message.

此处和答案&注释解释说,解决此问题的方法只是在 SendCallback 函数中调用 handler.beginReceive ,但是要做到这一点,需要传递一个 state 变量.对于异步编程,这是我不确定的事情,因为我还很陌生.

This question has been asked before here, and the answer & comments explain that the resolution to this is simply to call handler.beginReceive within the SendCallback function, however to do this requires passing in a state variable. This is something I am unsure of doing with Async programming as I'm pretty new to it.

在下面的示例中,如何将我的 state 对象从 Send 函数传送到 SendCallback 函数?

With the example below, how can I carry my state object from the Send function to the SendCallback function?

服务器代码:

// Asynchronous Server Socket Example
// http://msdn.microsoft.com/en-us/library/fx6588te.aspx

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

// State object for reading client data asynchronously
public class StateObject
{
    // Client  socket.
    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 1024;
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
    // Received data string.
    public StringBuilder sb = new StringBuilder();
}

public class GetState
{

}

public class AsynchronousSocketListener
{
    // Thread signal.
    public static ManualResetEvent allDone = new ManualResetEvent(false);

    public AsynchronousSocketListener()
    {
    }

    public static void StartListening()
    {
        // Data buffer for incoming data.
        byte[] bytes = new Byte[1024];

        // Establish the local endpoint for the socket.
        // The DNS name of the computer
        // running the listener is "host.contoso.com".
        IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
        IPAddress ipAddress = ipHostInfo.AddressList[0];
        IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

        // Create a TCP/IP socket.
        Socket listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        // Bind the socket to the local endpoint and listen for incoming connections.
        try
        {
            listener.Bind(localEndPoint);
            listener.Listen(100);

            while (true)
            {
                // Set the event to nonsignaled state.
                allDone.Reset();

                // Start an asynchronous socket to listen for connections.
                Console.WriteLine("Waiting for a connection...");
                listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

                // Wait until a connection is made before continuing.
                allDone.WaitOne();
            }

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }

        Console.WriteLine("\nPress ENTER to continue...");
        Console.Read();

    }

    public static void AcceptCallback(IAsyncResult ar)
    {
        // Signal the main thread to continue.
        allDone.Set();

        // Get the socket that handles the client request.
        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        // Create the state object.
        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
    }

    public static void ReadCallback(IAsyncResult ar)
    {
        String content = String.Empty;

        // Retrieve the state object and the handler socket
        // from the asynchronous state object.
        StateObject state = (StateObject)ar.AsyncState;
        Socket handler = state.workSocket;

        // Read data from the client socket. 
        int bytesRead = handler.EndReceive(ar);

        if (bytesRead > 0)
        {
            // There  might be more data, so store the data received so far.
            state.sb.Append(Encoding.ASCII.GetString(
                state.buffer, 0, bytesRead));

            // Check for end-of-file tag. If it is not there, read 
            // more data.
            content = state.sb.ToString();
            if (content != null)
            {
                // All the data has been read from the 
                // client. Display it on the console.
                Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
                    content.Length, content);
                // Echo the data back to the client.
                Send(handler, content, state);
            }
            else
            {
                // Not all data received. Get more.
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
            }
        }
    }

    private static void Send(Socket handler, String data, StateObject state)
    {
        // Convert the string data to byte data using ASCII encoding.
        byte[] byteData = Encoding.ASCII.GetBytes(data);

        // Begin sending the data to the remote device.
        handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
    }

    private static void SendCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the socket from the state object.
            Socket handler = (Socket)ar.AsyncState;

            // Complete sending the data to the remote device.
            int bytesSent = handler.EndSend(ar);
            Console.WriteLine("Sent {0} bytes to client.", bytesSent);

            /// ******* WIP ******** //
            //handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
            //handler.Shutdown(SocketShutdown.Both);
            //handler.Close();

        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }


    public static int Main(String[] args)
    {
        StartListening();
        return 0;
    }
}

推荐答案

这就是我要这样做的方式:

public class AsynchronousSocketServer
{
    private Socket listener;
    private byte[] buffer = new byte[8192]; // Buffer to store data from clients.

    public void StartListening()
    {
        listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        listener.Bind(new IPEndPoint(localIPAddress, listeningPort));
        listener.Listen(20);
        listener.BeginAccept(OnSocketAccepted, null);
    }

    private void OnSocketAccepted(IAsyncResult result)
    {
        // This is the client socket, where you send/receive data from after accepting. Keep it in a List<Socket> collection if you need to.
        Socket client = listener.EndAccept(result);

        // Pass in the client socket as the state object, so you can access it in the callback.
        client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnDataReceived, client); // Start receiving data from this client.
        listener.BeginAccept(OnSocketAccepted, null); // Start a new async accept operation to accept incoming connections from other clients.
    }

    private void OnDataReceived(IAsyncResult result)
    {
        // This is the client that sent you data. AsyncState is exactly what you passed into the state parameter in BeginReceive
        Socket client = result.AsyncState as Socket;
        int received = client.EndReceive(result);

        // Handle received data in buffer, send reply to client etc...

        // Start a new async receive on the client to receive more data.
        client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, OnDataReceived, client);
    }
}

如您所见,它有很多递归回调.基本想法是,您调用 BeginAccept 开始接受第一个连接,然后在触发回调后,您就使用 EndAccept 接受连接并启动一个新的 BeginAccept 开始接受更多连接.当您开始与客户端通信时,相同的逻辑适用于 BeginReceive EndReceive .通过这种逻辑,服务器将继续接受传入的连接,客户端也将能够连续向服务器发送数据.

As you can see it's pretty much a lot of recursive callbacks. The basic idea is that you call BeginAccept to start accepting the first connection, then once your callback fires you accept the connection with EndAccept and start a new BeginAccept to start accepting more connections. This same logic applies to BeginReceive and EndReceive when you start communicating with the clients. With this logic the server will continuously accept incoming connections and clients will also be able to continously send data to server.

在您的示例中,您从AsyncState获得了 listener ,但是您没有再次调用BeginAccept来接受更多的传入连接,这可能解释了为什么您的服务器仅接受1个连接并关闭.

In your example you get your listener from the AsyncState but you didn't call BeginAccept again to accept more incoming connections, which probably explains why your server only accepts 1 connection and shuts down.

对于 BeginSend 中的 state 参数,您实际上可以将 BeginReceive 放在 Send()避免麻烦的方法:

For the state parameter in BeginSend, you can actually just put your BeginReceive right after your Send() method to save yourself the hassle:

Send(handler, content, state);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);

这篇关于C#异步套接字服务器-回调中的BeginReceive的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆