簡體   English   中英

具有多個客戶端的 C# 套接字(多線程方法)

[英]C# sockets with multiple clients (multiple threads approach)

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 + "\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

Basically when a listbox item (client) is selected, the received data area would change according to what has been received by that client.
And when that same listbox item (client) is selected and send a message, it would be sent to that client and that client alone (no multicast or something similar).

僅供參考,我將按照相同的原則實現文件傳輸。

我不是要你們謙虛的人為我編寫我的應用程序,那是徒勞的,我什么也學不到。 我想了解和處理所有這些是如何工作的。 顯然,可以幫助我理解的代碼示例非常非常受歡迎。 潛在的問題和疑問:

  1. 我的邏輯有問題嗎? 如果是這樣,你有什么建議? 我覺得我使用了太多線程,我無法區分發送到客戶端的線程和從客戶端讀取的線程。
  2. 當我創建一個線程時,它繼承了什么? 它會繼承套接字(例如偵聽器)的 COPY 還是原始套接字本身?
  3. 如果我創建了這么多線程,我如何區分客戶端(發送數據)的主線程和與之關聯的讀取線程?
  4. 我應該選擇UDP嗎? 無連接協議在我的腦海中肯定是有意義的,我可以在沒有需要處理的活動連接的情況下向/從 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. 您可以為每個用戶創建一個線程,如上面的鏈接。 或者你可以像 webservice 一樣靜態,每個請求做 1 個線程。

  3. 您不應將線程視為抽象的事物,而應更像是一個對象。 如果您總是在一個對象中只保留一個線程,那么“知道”女巫線程在哪里永遠不會有問題。 Ofc 你必須記住鎖和其他東西,但在一個對象中一個線程。

  4. UDP 不保證您的數據包將到達發送它們的位置。 TCP 至少會盡力做到這一點。 因此,為了簡單起見,請使用 TCP。

  5. 我只有線索,您可能會不小心使用默認值。

希望這會有所幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM