c# - C#异步套接字客户端/服务器在服务器响应上挂起

标签 c# sockets asynchronous client server

我一直在玩我在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()的调用,而没有其他任何调用。这使我相信服务器无法正确完成从客户端的数据读取,尽管我不确定为什么。我想念什么?

最佳答案

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

服务器

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()使它们按顺序完成执行。

关于c# - C#异步套接字客户端/服务器在服务器响应上挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29650877/

相关文章:

c# - 这是对设置功能的滥用吗?

c# - .NET 项目中的条件引用是否可以消除警告?

c# - 绕过证书验证

sockets - Erlang 变量模式匹配

c - ifconfig->which 的结果是我要提供给 TCP/IP 套接字的 IP 地址

c++ - grpc c++ 异步完成队列事件

node.js - 在服务器上异步运行方法

c# - 调试时忽略某个位置的异常?

c++ - 在 ruby​​ 脚本和正在运行的 c++ 程序之间进行通信

javascript - async.waterfall 失去了范围