具有多个客户端的C#套接字(多线程逼近)

时间:2016-05-02 22:20:33

标签: c# multithreading sockets

TL; DR:我有一个管理一个客户端的C#服务器。什么是使其处理多个客户的最佳方法?

完整说明:
我正在实现一个C#套接字服务器应用程序,它需要同时处理多个客户端。

我的逻辑如下:

  1. 多个客户端连接到服务器,发送和接收数据(在这种情况下是简单文本 - 将其视为信使)。
  2. 服务器为每个客户端都有一个单独的线程,该线程发送回复。
  3. 这些每个客户端线程中的每一个都会产生一个额外的线程来监听相应客户端的数据(我不知道这种方法是否合适,我可以考虑使用某种异步读取,但我不明白如何他们工作,因为我从未使用过它们。
  4. 服务器通过使用列表框来区分每个客户端,我们可以假设列表框包含每个客户端的IP和唯一标识符以便于识别(假设用户名)。
  5. 到目前为止,我的代码只管理一个客户端:

    public partial class Form1 : Form
    {
        TcpListener listener;
        Socket clientSock;
        Thread listenThread, receiveThread;
        string sendMsg, recvMsg;
        byte[] sendMsgRaw, recvMsgRaw;
    
        public Form1() {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e) {
            // Handler for onExit.  
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(onExit);
        }
    
        private void listenButton_Click(object sender, EventArgs e) {
            // Launch thread that listens for clients attempting to connect. 
            listener = new TcpListener(8888); // I know this method is deprecated, will fix.  
            listener.Start();
    
            listenThread = new Thread(new ThreadStart(listenForClients));
            listenThread.Start();
        }
    
        private void sendButton_Click(object sender, EventArgs e) {
            // Send message to client.  
            sendMsg = sendTextbox.Text;
            sendMsgRaw = Encoding.ASCII.GetBytes(sendMsg + "\r\n");
            clientSock.Send(sendMsgRaw);
        }
    
        private void listenForClients() {
            // Listen for connection attempts. 
            clientSock = listener.AcceptSocket();
            disableButton(listenButton);
            receiveThread = new Thread(new ThreadStart(readFromClient));
            receiveThread.Start();
        }
    
        private void readFromClient() {
            // Read response from client. 
            while(true) {
                recvMsg = "";
                recvMsgRaw = new byte[1024];
    
                clientSock.Receive(recvMsgRaw);
                recvMsg = Encoding.ASCII.GetString(recvMsgRaw);
                appendRecvTextbox(recvMsg);
            }
        }
    
        private void appendRecvTextbox(string message) {
            // Append client's reply to a textbox. 
            if(InvokeRequired) {
                this.Invoke(new Action<string>(appendRecvTextbox), new object[] { message });
                return;
            }
            recvTextbox.AppendText(message + Environment.NewLine);
        }
    
        private void disableButton(Button btn) {
            // Disable a button. 
            if(InvokeRequired) {
                this.Invoke(new Action<Button>(disableButton), new object[] { btn });
                return;
            }
            btn.Enabled = false;
        }
    
        private void onExit(object sender, EventArgs e) {
            // Executed on program exit. 
            clientSock.Close();
        }
    }
    

    以下是我想到的表单的屏幕截图:http://prntscr.com/azkaed

    基本上,当选择了一个列表框项目(客户端)时,接收的数据区域将根据该客户端收到的内容而改变。
    当选择相同的列表框项(客户端)并发送消息时,它将被发送到该客户端和该客户端(没有多播或类似的东西)。

    仅供参考,我将按照相同的原则实施文件传输。

    我没有要求你们谦虚的人为我编写我的申请代码,这是徒劳的,我什么都不会学到。我想了解并处理所有这些是如何工作的。很明显,非常非常欢迎能够帮助我理解的代码示例。潜在的问题和疑问:

    1. 我的逻辑有缺陷吗?如果是这样,你有什么建议?我觉得我使用的线程太多而且我无法区分发送到客户端的线程和从客户端读取的线程。
    2. 当我创建一个线程时,它继承了什么?它会继承套接字的COPY(例如监听器)还是原始套接字本身?
    3. 如果我创建了这么多线程,我如何区分客户端(发送数据)的主线程和与之关联的读线程?
    4. 我应该选择UDP吗?无连接协议确实在我脑海中有意义,我可以在没有我需要处理的活动连接的情况下发送和接收IP。但我担心UDP的不可靠性。特别是因为我打算在此之后实现文件传输。
    5. 在我编写这个服务器之后,我注意到我没有将套接字绑定到端口,但它很高兴与cpp客户端进行通信。我记得在大学的网络课程中,如果我们使用TCP协议,我们需要绑定。我得出的结论是,绑定是不必要的。为什么是这样?
    6. 回到主要问题:在当前状态下,应用程序处理一个客户端。如何让它处理多个客户端?请注意,我选择了多线程,因为我一次连接的客户端数量有限 - 不到10个客户端。

      我知道这不是一个我想问的简单问题 - 但每一个答案都将深受赞赏 如果它有所不同,客户端将使用c / cpp,java,python等语言编写,甚至考虑使用PHP。这仅用于学习。但我怀疑这很重要。客户似乎更容易编程,因为逻辑较少,所以我不需要任何帮助。

      提前感谢您的任何建议。

1 个答案:

答案 0 :(得分:2)

首先,请注明以下链接:http://www.codeproject.com/Articles/429144/Simple-Instant-Messenger-with-SSL-Encryption-in-Cs

现在我会回答你的问题:

  1. 没有像#34;太多的线程&#34;。您可以使您的服务器每个请求有1个线程。该线程将读取并为该一个请求写入数据(这是所有Web服务的工作方式,它们通常可以处理每个用户最多100个线程)。表现明智。有一种称为线程交换的东西,但不要打扰它。

  2. 您可以为每个用户创建一个主题,例如上面的链接。或者你可以去静态,比如webservices,每个请求做1个帖子。

  3. 你应该看一下不像抽象事物的线程,但更像是一个对象。如果你总是只在一个对象中保留一个线程,那么你将永远不会遇到问题&#34;知道&#34;巫婆的线程在哪里。你需要记住锁和东西,但在一个对象中有一个线程。

  4. UDP不保证您的数据包将在它们发送的地方着陆。 TCP至少会尽力做到这一点。为了简单起见,请使用TCP。

  5. 我只知道您可能会偶然使用默认值。

  6. 希望这会有所帮助。

相关问题