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



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

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

我的逻辑如下:

  1. 多个客户端连接到服务器,发送和接收数据(在这种情况下是简单的文本,可以将其视为信使)
  2. 服务器为每个客户端都有一个单独的线程,用于发送回复
  3. 每个每个客户端线程都会生成一个额外的线程来侦听各自客户端的数据(我不知道这种方法是否好,我可以考虑使用某种异步读取,但我不知道它们是如何工作的,因为我从未使用过它们)
  4. 服务器通过使用列表框来区分每个客户端,我们可以假设列表框包含每个客户端的IP和一个唯一的标识符,以便于识别(假设用户名)

到目前为止,我的代码只管理一个客户端:

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 + "rn");
        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发送和从IP接收。但我担心UDP的不可靠性。特别是因为我打算在此之后实现文件传输
  5. 在我对这个服务器进行编码后,我注意到我没有将套接字绑定到端口,但它正在愉快地与cpp客户端通信。我记得在大学的网络课上,如果我们使用TCP协议,我们需要绑定。我得出的结论是,约束是不必要的。为什么会这样

所以回到主要问题:在目前的状态下,该应用程序处理一个客户端。如何让它处理多个客户端?请注意,我选择多线程是因为一次连接的客户端数量有限——不到10个客户端。

我知道这不是一个容易的问题,但我会非常感激每一个答案
如果有什么不同的话,客户端将使用c/cpp、java、python等语言的混合体编写,我甚至在考虑PHP。这只是为了学习。但我怀疑这是否重要。客户端似乎更容易编程,因为逻辑更少,所以我不需要任何帮助。

提前谢谢你的建议。

首先,有这个链接:http://www.codeproject.com/Articles/429144/Simple-Instant-Messenger-with-SSL-Encryption-in-Cs

现在我将回答您的问题:

  1. 没有"线程太多"这样的东西。您可以使您的服务器每个请求有1个线程。这个线程将读取和写入该请求的数据(这就是所有Web服务的工作方式,它们通常每个用户最多处理100个线程)。性能方面。有一种叫做线程交换的东西,但不要为此而烦恼。

  2. 您可以为每个用户创建一个线程,就像上面的链接一样。或者,您可以像Web服务一样进行静态处理,每个请求执行1个线程。

  3. 你应该把线程看作一个对象,而不是抽象的东西。如果你总是在一个对象中只保留一个线程,你就永远不会有问题"知道"女巫线程在哪里。Ofc你必须记住锁之类的东西,但在一个对象中只有一个线程。

  4. UDP不能保证您的数据包会到达发送位置。TCP至少会尽力做到这一点。所以为了简单起见,请使用TCP。

  5. 我唯一的线索是,你可能会意外地使用默认值。

希望这会有所帮助。

最新更新