C#中的异步套接字服务器,客户端通过套接字服务器进行客户端通信

时间:2018-01-21 09:48:58

标签: c# sockets asynchronous socketserver

我正在尝试使用c#开发服务器/客户端异步套接字。我已按照MSDN链接的指南进行操作。在我的情况下,套接字服务器监听特定端点,许多客户端可以一次连接到服务器,客户端可以与服务器通信,服务器可以与客户端通信。假设客户端1和客户端2与服务器连接,客户端1可以向服务器发送消息,服务器可以发送给客户端1,同样适用于客户端2的情况。现在我希望客户端能够通过服务器相互通信。例如,客户端2想要与客户端1通信,因为客户端2将向服务器发送消息(此消息将包含一些预设字符;),然后服务器将从客户端2接收文本并将获取客户端1的处理程序并发送这个消息发送到客户端1,客户端1将响应服务器,现在我想将客户端1的响应发送给客户端2,但我不知道如何做到这一点,因为客户端1通过自己的处理程序与服务器进行通信,我很震惊,帮助将受到高度赞赏!  我的代码如下:

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



namespace SocketServer
{
    // 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 int clientNumber;
    }
    public class AsyncSocketServer
    {
        public static ManualResetEvent allDone = new ManualResetEvent(false);

        public static Dictionary<int, StateObject> Clients = new Dictionary<int, StateObject>();

        public static int connectedClient = 0;




        public AsyncSocketServer()
        {


        } 
        public static void startListening() {

            Byte[] bytes = new Byte[1024];
            int Port = 1122;

            IPAddress IP = IPAddress.Parse("127.0.0.1");
            IPEndPoint EP = new IPEndPoint(IP, Port);
            Socket listner = new Socket(IP.AddressFamily, SocketType.Stream, ProtocolType.Tcp);



            try
            {
                listner.Bind(EP);
                listner.Listen(100);

                while (true)
                {
                   allDone.Reset();

                    Console.WriteLine("Waiting for the Connection......");

                    listner.BeginAccept(new AsyncCallback(AcceptCallBack), listner);

                    allDone.WaitOne();
                }


            }
            catch(Exception e)
            {
                Console.WriteLine("Exception Occured ! in start listening method "+e.ToString());
            }

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

        }

        public static void AcceptCallBack(IAsyncResult ar)
        {
            connectedClient++;
            Console.WriteLine("client number " + connectedClient);
            allDone.Set();



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

            StateObject state = new StateObject();
            state.clientNumber = connectedClient;

            Clients.Add(connectedClient, state);
           Console.WriteLine("total clients {0}",Clients.Count());

            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.  
        try { 
        StateObject state = (StateObject) ar.AsyncState;
        state.sb.Clear();
        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.Substring(0, 3) == "cmd") {
                foreach (StateObject Client in Clients.Values) {
                    if (Client.clientNumber == 1) { 
                        Console.WriteLine("value is "+Client.clientNumber);
                        if (isClientConnected(Client.workSocket)){
                            Send(Client.workSocket, "did you receive my message");
                            //now client number 1 will response through its own handler, but i want to get response of 
                            //client number 1 and return this response to client number 2

                        }
                        else {
                            string responsemsg = "client number " + Client.clientNumber + " is disconnected !";
                            Console.WriteLine(responsemsg);
                            Send(handler,responsemsg);
                        }
                    }

                }
            }

            Console.WriteLine("Read {0} bytes from client {1} socket. \n Data : {2}",
                    content.Length, state.clientNumber,content);
            // Echo the data back to the client.  

            if (isClientConnected(handler))
            {
                Send(handler, content);
            }
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallBack), state);

        }
        }
        catch (SocketException e)
        {
            //once if any client disconnected then control will come into this block
            Console.WriteLine("Socket Exception Occured in Read Call Back : " + e.Message.ToString());

        }
        catch (Exception e)
        {
            //once if any client disconnected then control will come into this block
            Console.WriteLine("Exception Occured in Read Call Back : " + e.Message.ToString());

        }
        }
        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)
        {
            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);



                //handler.Shutdown(SocketShutdown.Both);
                //handler.Close();

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

        public static bool isClientConnected(Socket handler){

            return handler.Connected;
        }
        public static int Main(string[] args)
        {

            startListening();


            return 0;


        }
    }
}

2 个答案:

答案 0 :(得分:2)

对于任何基于套接字的复杂应用程序,我建议使用像DotNetty这样的套接字库来抽象传输层,并让您专注于应用程序逻辑。查看他们的SecureChat example,它可能与您尝试实现的非常类似。

我将一个DotNetty服务器的快速示例放在一起,允许您通过让客户端向服务器注册,然后让服务器在客户端之间路由消息来在客户端之间发送命令。

<div class="btn-file">
  <input type="file" id="FileUpload1" class="" disabled>
  <label for="FileUpload1" class="custom-file-upload">
    <i class="fa fa-upload"></i> Upload Logo
  </label>
</div>

答案 1 :(得分:0)

我也尝试基于MSDN的相同代码进行相同的操作 可能的解决方案是使用套接字列表:

List<Socket> clients = new List<Socket>();

然后,当客户端连接时,将客户端添加到列表中:

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);

        clients.Add(handler);
        ...
    }

您必须知道所连接的每个客户端的ID(句柄),然后才能向特定客户端发送一些消息:

 public void SendToOne(string id,string message)
    {
        foreach (Socket s in clients)
        {
            if (s.Handle.ToString() == id)
            {
                Send(s, message);
            }
        }

    }