【问题标题】:c# asynchronous socket client/server hangs on server responsec#异步套接字客户端/服务器挂起服务器响应
【发布时间】:2015-06-21 10:51:21
【问题描述】:

我一直在使用我在 MSDN 上找到的一些 c# 套接字代码(原始 server codeclient code),我遇到了一个我不明白的问题。首先,这是我的套接字服务器代码:

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

namespace AsyncSocketServerTest
{
    class Program
    {
        public class StateObject
        {
            public Socket socket = null;
            public const int BufferSize = 1024;
            public byte[] buffer = new byte[BufferSize];
            public List<byte> bytes = new List<byte>();
        }

        public static ManualResetEvent allDone = new ManualResetEvent(false);

        private const string ipAdd = "127.0.0.1";

        public static void StartListening()
        {
            byte[] bytes = new byte[1024];

            IPAddress ipAddress = IPAddress.Parse(ipAdd);
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 25981);

            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(100);

                while (true)
                {
                    allDone.Reset();
                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                    allDone.WaitOne();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

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

        public static void AcceptCallback(IAsyncResult ar)
        {
            allDone.Set();

            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);

            StateObject state = new StateObject();
            state.socket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
        }

        public static void ReadCallback(IAsyncResult ar)
        {
            Console.WriteLine("Inside ReadCallback()...");

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

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

            if (bytesRead > 0)
            {
                // there might be more data, so store the data received so far
                for (int bufferIndex = 0; bufferIndex < bytesRead; bufferIndex++)
                {
                    state.bytes.Add(state.buffer[bufferIndex]);
                }

                socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
            }
            else
            {
                if (state.bytes.Count > 0)
                {
                    // All the data has been read from the client; display it on the console.
                    byte[] bytesReceived = state.bytes.ToArray();

                    Console.WriteLine("Received {0} bytes from client...", bytesReceived.Length.ToString());
                }

                // generate a 50 byte response to send back to the client
                Random r = new Random();
                byte[] responseToSend = new byte[50];
                r.NextBytes(responseToSend);

                // *** THIS APPEARS TO BE CAUSING A PROBLEM ***
                // send the response back to client
                SendBytes(socket, responseToSend);
                // ********************************************

                // edit - commented out; the socket shouldn't be closed before the response is sent back to the client asynchronously
                //socket.Close();
            }
        }

        private static void SendBytes(Socket client, byte[] bytesToSend)
        {
            client.BeginSend(bytesToSend, 0, bytesToSend.Length, 0, new AsyncCallback(SendCallback), client);
        }

        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;

                int bytesSent = handler.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to client.", bytesSent);

                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        static void Main(string[] args)
        {
            StartListening();
            Console.ReadKey();
        }
    }
}

现在是客户端代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace AsyncSocketClientTest
{
    class Program
    {
        public class StateObject
        {
            public Socket socket = null;
            public const int BufferSize = 1024;
            public byte[] buffer = new byte[BufferSize];
            public List<byte> bytes = new List<byte>();
        }

        private const string ipAdd = "127.0.0.1";

        // ManualResetEvent instances signal completion
        private static ManualResetEvent connectDone = new ManualResetEvent(false);
        private static ManualResetEvent sendDone = new ManualResetEvent(false);
        private static ManualResetEvent receiveDone = new ManualResetEvent(false);

        private static void StartClient()
        {
            try
            {
                IPAddress ipAddress = IPAddress.Parse(ipAdd);
                IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, 25981);

                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                client.BeginConnect(remoteEndPoint, new AsyncCallback(ConnectCallback), client);
                connectDone.WaitOne();

                // generate 100 random bytes to send to the server
                Random r = new Random();
                byte[] buffer = new byte[100];
                r.NextBytes(buffer);

                // send data to the server
                SendBytes(client, buffer);
                sendDone.WaitOne();

                // *** THIS APPEARS TO BE CAUSING A PROBLEM ***
                // receive the response from the remote host
                ReceiveBytes(client);
                receiveDone.WaitOne();
                // ********************************************

                // release the socket
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                // retrieve the socket from the state object
                Socket client = (Socket)ar.AsyncState;

                // complete the connection
                client.EndConnect(ar);

                Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());

                // signal that the connection has been made
                connectDone.Set();
            }
            catch (SocketException sockEx)
            {
                // if the server isn't running, we're going to get a socket exception here...
                Console.WriteLine(sockEx.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ReceiveBytes(Socket client)
        {
            Console.WriteLine("Inside ReceiveBytes()...");

            try
            {
                // create the state object
                StateObject state = new StateObject();
                state.socket = client;

                // begin receiving data from the remote device
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            Console.WriteLine("Inside ReceiveCallback()...");

            try
            {
                // Retrieve the state object and the client socket from the asynchronous state object
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.socket;

                // Read data from the remote host
                int bytesRead = client.EndReceive(ar);

                if (bytesRead > 0)
                {
                    // there might be more data, so store the data received so far
                    for (int bufferIndex = 0; bufferIndex < bytesRead; bufferIndex++)
                    {
                        state.bytes.Add(state.buffer[bufferIndex]);
                    }

                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                }
                else
                {
                    if (state.bytes.Count > 0)
                    {
                        // All the data has been read from the client; display it on the console.
                        byte[] bytesReceived = state.bytes.ToArray();

                        Console.WriteLine("Read {0} bytes from socket...", bytesReceived.Length.ToString());
                    }

                    // Signal that all bytes have been received
                    receiveDone.Set();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void SendBytes(Socket client, byte[] bytesToSend)
        {
            // Begin sending the data to the remote device
            client.BeginSend(bytesToSend, 0, bytesToSend.Length, 0, new AsyncCallback(SendCallback), client);
        }

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

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

                // signal that all bytes have been sent
                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        static void Main(string[] args)
        {
            StartClient();
        }
    }
}

如果我注释掉客户端中从服务器接收响应的代码以及服务器中尝试向客户端发送响应的代码,那么一切似乎都如您所愿(即,客户端连接服务器,发送数据,服务器正常接收数据)。但是,当我取消注释这些代码部分时,我看到了一些我不理解的行为。在这种情况下,我看到客户端连接到服务器并向其发送数据。在服务器端,代码似乎挂在 ReadCallback() 中。为了更好地说明这一点,当我之前提到的代码部分被注释掉时,我看到了:

Client output:

Socket connected to 127.0.0.1:25981
Sent 100 bytes to server.


Server output:

Waiting for a connection...
Waiting for a connection...
Inside ReadCallback()...
Inside ReadCallback()...
Received 100 bytes from client...

从这个输出中可以看出,当服务器接收到 100 字节的客户端数据时,我看到了两次对 ReadCallback() 的调用。所以现在我取消注释上述代码并再次运行它。这一次,我明白了:

Client output:

Socket connected to 127.0.0.1:25981
Sent 100 bytes to server.
Inside ReceiveBytes()...


Server output:

Waiting for a connection...
Waiting for a connection...
Inside ReadCallback()...

这一次,我的客户端向服务器发送了 100 个字节的数据,设置了 sendDone ManualResetEvent,然后进入 ReceiveBytes()。在服务器端,我看到一个对 ReadCallback() 的调用,没有别的。这使我相信服务器没有正确完成从客户端读取数据,尽管我不确定为什么。我错过了什么?

【问题讨论】:

  • 你的服务器如果收到 0 个字节就关闭连接
  • @Ewan - 你是对的。我在服务器上的 ReadCallback() 中注释掉了对 socket.Close() 的调用,但是当我有代码将服务器的响应发送回未注释的客户端时,行为并没有改变。

标签: c# sockets asynchronous client server


【解决方案1】:

这并不能真正回答您的确切问题,但我可以建议另一种方法来解决这个问题吗?对我来说,线程更容易理解,代码看起来更简洁:

服务器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2 {
class Program {
  static void Main(string[] args) {
     ServerWorkThread objThread = new ServerWorkThread();
     while(true) {
        objThread.HandleConnection(objThread.mySocket.Accept());
     }
  }
}

public class ServerWorkThread {
     public Socket mySocket;
     public ServerWorkThread() {
        IPEndPoint objEnpoint = new IPEndPoint(IPAddress.Parse("***.***.***.***"), 8888);
        mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        mySocket.Bind(objEnpoint);
        mySocket.Listen(100);
     }

     public void HandleConnection(Socket iIncomingSocket) {
        Thread worker = new Thread(this.RecieveAndSend);
        worker.Start(iIncomingSocket);
        worker.Join();
     }

     public void RecieveAndSend(object iIncoming) {
        Socket objSocket = (Socket)iIncoming;
        byte[] bytes = new byte[1024];

        int bytesRecieved = objSocket.Receive(bytes);
        string strReceived = System.Text.Encoding.ASCII.GetString(bytes, 0, bytesRecieved);
        Console.WriteLine("Received from client: " + strReceived);

        Console.WriteLine("Sending acknowledgement to client");
        string strSend = ("Command of: " + strReceived + " was processed successfully");
        objSocket.Send(System.Text.Encoding.ASCII.GetBytes(strSend));

        objSocket.Close();
     }
  }

}

客户:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Client {
class Program {
  static void Main(string[] args) {
     ClientWorkThread thread1 = new ClientWorkThread("I am thread 1");
     thread1.SendCommand();
     ClientWorkThread thread2 = new ClientWorkThread("I am thread 2");
     thread2.SendCommand();
     ClientWorkThread thread3 = new ClientWorkThread("I am thread 3");
     thread3.SendCommand();
     Console.Read();
  }
}


  public class ClientWorkThread {

     private Socket pSocket;
     private string command;
     public ClientWorkThread(string iCommand) {
        IPEndPoint objEnpoint = new IPEndPoint(IPAddress.Parse("***.***.***.***"), 8888);
        pSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        pSocket.Connect(objEnpoint);
        command = iCommand;
     }

     public void SendCommand() {
        Thread worker = new Thread(this.Send);
        worker.Start(pSocket);

     }

     public void Send(object iSending) {
        Socket objSocket = (Socket)iSending;
        objSocket.Send(System.Text.Encoding.ASCII.GetBytes(command + " now DO WORK "));
        Console.WriteLine("Sending: " + command + " now DO WORK ");
        byte[] bytes = new byte[1024];
        int bytesRecieved = objSocket.Receive(bytes);
        string strReceived = System.Text.Encoding.ASCII.GetString(bytes, 0, bytesRecieved);
        Console.WriteLine("Received from server: " + strReceived);
        objSocket.Close();
     }
  }
}

服务器输出: 从客户收到:我是线程 1 现在可以工作 向客户发送确认 从客户收到:我是线程 2 现在可以工作 向客户发送确认 从客户那里收到:我现在是线程 3 工作 向客户发送确认

客户端输出: 发送:我是线程 2 现在可以工作 发送:我现在是线程 3 工作 从服务器收到:命令:我是线程 2 现在 DO WORK 已成功处理 从服务器接收:命令:我是线程 3 现在 DO WORK 已成功处理 发送:我是线程 1 现在可以工作 从服务器收到:命令:我是线程 1 现在 DO WORK 已成功处理

您也可以使用 thread.Join() 让它们按顺序完成。

【讨论】:

  • 感谢您的建议。我同意您的代码对我来说更容易理解。我对此表示赞同,但我想看看在我接受答案之前是否能理解我的原始代码发生了什么。再次感谢您的回复。我很感激!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 2011-08-14
  • 2017-05-26
  • 2017-07-10
  • 1970-01-01
  • 2015-09-07
相关资源
最近更新 更多