3

I am using the MSDN server example found here

http://msdn.microsoft.com/en-us/library/fx6588te(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1

and my problem is that this code only continuously accepts new clients. I want it to continuously receive from all the clients that it accepts as well. This code accepts a client then receives one message from them and then sends one message and that's it. The only thing I can think to do is stick the async receive method in a while(true) loop, but that doesn't sound right.

I changed the example a little but it still has the same basic functionality.

public class AServer
{
    // Thread signal.
    public static ManualResetEvent allDone = new ManualResetEvent(false); 
    Socket listener;
    ArrayList clients;

    public AServer(int port)
    {
        // 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.IPv6Any, port);

        clients = new ArrayList();

        // Create a TCP/IP socket.
        listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
        listener.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, 0);
        listener.Bind(localEndPoint);
        listener.Listen(100);
    }

    public void ServerLoop(){
        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();
        }

    }

    public 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.
        ClientData state = new ClientData();
        state.workSocket = handler;
        clients.Add(state);

        handler.BeginReceive(state.buffer, 0, ClientData.BufferSize, 0,new AsyncCallback(ReadCallback), state);
        Console.WriteLine("we passed handler.BeginReceive");
    }

    public void ReadCallback(IAsyncResult ar)
    {
        // Retrieve the state object and the handler socket
        // from the asynchronous state object.
        ClientData data = (ClientData)ar.AsyncState;
        String buff = String.Empty;
        Socket handler = data.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.
            buff = Encoding.ASCII.GetString(data.buffer, 0, bytesRead);
            ParseBuffer(buff);
            Send(handler, "1");
        }
        if (bytesRead == 0) {
            CloseConnection(handler);
        }
    }

    private bool ParseBuffer(String buff) {
        Console.WriteLine(buff);
        switch (buff[0]) { 
            case '0':

                break;
            case '1':
                break;
        }

        return true;
    }

    private static void Send(Socket handler, String data)
    {
        // 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)
    {
        // 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);
    }

    private static void CloseConnection(Socket handler)
    {
        handler.Shutdown(SocketShutdown.Both);
        handler.Close();
    }
}

Of course my main method simply calls the constructor then the loop sequentially. The arraylist is a remnant of some attempts of mine to figure this out.

edit

I was looking for the same sort of functionality that Select offers me in C, but apparently C# can do the same thing with events.

1 Answer 1

2

In your ReadCallback you need to post again an async Receive:

public void ReadCallback(IAsyncResult ar)
{
    // Retrieve the state object and the handler socket
    // from the asynchronous state object.
    ClientData data = (ClientData)ar.AsyncState;
    String buff = String.Empty;
    Socket handler = data.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.
        buff = Encoding.ASCII.GetString(data.buffer, 0, bytesRead);
        ParseBuffer(buff);
        Send(handler, "1");

        // Here, you need to Receive again
        handler.BeginReceive(state.buffer, 0, ClientData.BufferSize, 0,new AsyncCallback(ReadCallback), state);
    }
    if (bytesRead == 0) {
        CloseConnection(handler);
    }
}   

This way you will receive, send, receive, send, receive, send etc on each socket. You need error handling and a proper server would need to handle incomplete messages, but that is the general idea.

BTW, similarly you must post another async Accept in your accept callback:

public 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.
    ClientData state = new ClientData();
    state.workSocket = handler;
    clients.Add(state);

    handler.BeginReceive(state.buffer, 0, ClientData.BufferSize, 0,new AsyncCallback(ReadCallback), state);
    Console.WriteLine("we passed handler.BeginReceive");

    // Here, you must start a new accept:
    listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
}
Sign up to request clarification or add additional context in comments.

1 Comment

That sounds right. I am at home right now so I don't have the code, but I think you got it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.