简体   繁体   中英

C# sockets with multiple clients (multiple threads approach)

TL;DR: I have a C# server which manages one client. What's the best approach for making it handle multiple clients?

Full explanation:
I'm implementing a C# socket server application which needs to handle multiple clients at the same time.

My logic is as follows:

  1. Multiple clients connect to the server, sending and receiving data (in this case simple text - think of it as a messenger).
  2. The server has a separate thread for each client, thread which sends out replies.
  3. Each of these per-client threads spawn an additional thread that listens for the respective client's data (I do not know if this approach is good, I can consider using some kind asynchronous reads but I do not understand how they work as I've never used them).
  4. The server differentiates each client by using a listbox, we can assume the listbox contains each client's IP and a unique identifier for easy recognition (assume a user name).

The code I have so far only manages one client:

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

Here is the screenshot of the form I have in mind: 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).

FYI later on I will implement a file transfer as well, following the same principles.

I'm not asking for you humble people to code my application for me, that is unproductive and I will learn nothing. I want to understand and process how all of this works. Obviously code samples which would help me understand are very, very welcome. Potential problems and questions:

  1. Is my logic flawed? If so, what do you suggest? I feel like I'm using too many threads and I won't be able to differentiate between a thread which sends to a client and a thread which reads from a client.
  2. When I create a thread, what does it inherit? Would it inherit a COPY of a socket (the listener for example) or the original socket itself?
  3. If I create so many threads how can I differentiate between the main thread for the client (which sends data) and the read thread associated with it?
  4. Should I go for UDP? A connectionless protocol sure makes sense in my head, I could just send and receive to/from an IP without an active connection that I need to take care of. But I'm worried about UDP's unreliability. Especially since I intend to implement file transfers after this.
  5. After I coded this server, I noticed that I did not bind the socket to a port yet it's happily communicating with a cpp client. I remember from my networking classes back in college that we need to bind if we're using the TCP protocol. I draw the conclusion that binding is unnecessary. Why is this?

So back to the main problem: In its current state, the app handles ONE client. How can I make it handle multiple clients? Do note that I chose multithreading because I'll have a limited amount of clients connected at a time - under 10 clients.

I know this is not an easy question I'm asking - but every answer will be deeply appreciated.
If it makes any difference, the clients will be written in an amalgam of languages like c/cpp, java, python, I'm even considering PHP. This is for the sole purpose of learning. But I doubt this matters. Clients seem far easier to program since there's less logic, so I do not require any help there.

Thank you in advance for any suggestion.

First, have this link: http://www.codeproject.com/Articles/429144/Simple-Instant-Messenger-with-SSL-Encryption-in-Cs

Now I will anwser your questions:

  1. There is no such thing like "too many threads". You can make your server that would have 1 Thread per request. This thread would read, and write data for that one request (This is how all webservices works, they usualy handles up to 100 threads per user). Performance wise. There is a thing called thread swapping, but dont bother with that.

  2. You can make one thread per user, like in link above. Or you can go static, like webservices and do 1 thread per request.

  3. You should look at threads not like an abstract thing, but more like an object. If you would always keep only one thread in one object you will never have a problem "knowing" witch thread is where. Ofc you have to remember about locks and stuff, but within one object one thread.

  4. UDP does not guarantee that your packets will land where they were sent. TCP will at least try its best to do that. So to keep it simple go for TCP.

  5. I have only clue, that you might use default one by accident.

Hope this will help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM