0

I'm facing a weird trouble with sockets and threads. It's a simple multithread server-clients, a just wan't to send and receive messages.

Here's the server code:

public class Server { Thread t; Thread listen; byte[] data1; byte[] data2; NetworkStream ns; TcpClient client; TcpListener newsock; public bool Running; List<ClStr> ListOfStreams = new List<ClStr>(); List<ClientList> ListOfClients = new List<ClientList>(); public Server() { Listen = new Thread(new ThreadStart(ExecuteThread)); Listen.Start(); } private void ExecuteThread() { newsock = new TcpListener(Globals.ServerPort); newsock.Start(); Running = true; LoopClients(); } public void LoopClients() { try { while (Running) { client = newsock.AcceptTcpClient(); t = new Thread(new ParameterizedThreadStart(NewClient)); t.Start(client); ClientList LCli = new ClientList(client); ListOfClientes.Add(LCli); } } catch (SocketException e) { MessageBox.Show(e.ToString()); } finally { newsock.Stop(); } } private void NewClient(object obj) { TcpClient client = (TcpClient)obj; ns = client.GetStream(); ClStr Cli = new ClStr(ns); ListOfStreams.Add(Cli); string TicketDelivery = "TKT:" + Cli.Ticket.ToString(); byte[] TKTbuffer = Encoding.ASCII.GetBytes(TicketDelivery); ns.Write(TKTbuffer, 0, TKTbuffer.Length); while (true) { int recv; data2 = new byte[200 * 1024]; recv = ns.Read(data2, 0, data2.Length); if (recv == 0) break; MessageBox.Show(Encoding.ASCII.GetString(data2, 0, recv)); } } 

Note that every new client is a new thread. No problem until here.

See the client code.

public class Client { Thread Starter; Socket server; byte[] data; string stringData; int recv; public int Ticket; public void Connect() { data = new byte[200 * 1024]; IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), Port); server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { server.Connect(ipep); Starter = new Thread(new ThreadStart(execute)); Starter.Start(); } catch (SocketException e) { MessageBox.Show("Unable to connect to server."); Starter.Abort(); return; } } public void SendMessageToServer(string msg) { server.Send(Encoding.ASCII.GetBytes(msg)); } private void execute() { while (true) { data = new byte[200 * 1024]; recv = server.Receive(data); stringData = Encoding.ASCII.GetString(data, 0, recv); byte[] byteData = data; MessageBox.Show(stringData); } } 

Now here's the problem:

I can only send an message to server (using the method SendMessageToServer) from the last client connected to server. If I add 3 clients, and try to send an message with first added, nothing happens! It seems that the client is not connected to the socket, but is not true, because I can send messages from the server with no problem.

So, considering that the server creates one thread per client, how can I send messages to server from any connected client?

3
  • Can you provide a sample of how you're calling SendMessageToServer()? Commented Apr 6, 2015 at 21:16
  • I answered with the async solution, but if you don't want to use the async methods you might find some info here. For the record, i don't want to say that it's not possible to do it without async methods, but I never managed to get it working back in the day and so I was forced to learn the async way (which is actually pretty simple if you find yourself a simple solution) stackoverflow.com/a/19218867/1274820 Commented Apr 6, 2015 at 22:10
  • public void SendoMessageToServer (string msg) { server.Send(Encoding.ASCII.GetBytes(msg)); } Commented Apr 7, 2015 at 0:59

1 Answer 1

1

I really think you should use Async methods if you intend to make an async server.

This is a very basic async server that sends whatever it receives to all connected clients.

You can run it like this:

Server s = new Server(1338); s.startListening(); 

Try connecting your client to it:

class Server { private int port; private Socket serverSocket; private List<StateObject> clientList; private const int DEFAULT_PORT = 1338; public Server() { this.port = 1338; //default port serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientList = new List<StateObject>(); } public Server(int port) { this.port = port; serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientList = new List<StateObject>(); } public void startListening(int port = DEFAULT_PORT) { this.port = port; //Bind to our port serverSocket.Bind(new IPEndPoint(IPAddress.Any, this.port)); //Listen on the port serverSocket.Listen(1); //Async Method to wait for a client to connect serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); } private void AcceptCallback(IAsyncResult AR) { try { //A client has connected so create a stateobject for them //This will hold their buffer and socket StateObject state = new StateObject(); //This is where the socket is passed to our state state.workSocket = serverSocket.EndAccept(AR); //Add this client to our client list clientList.Add(state); //Async method to receive data from them (Non Blocking) state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state); //Immediately go back to waiting for a new client (Non Blocking) serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); } catch { } } private void ReceiveCallback(IAsyncResult AR) { //A client has sent us data //We pass ourselves the state in our previous method //Now we can retrieve that StateObject from the callback StateObject state = (StateObject)AR.AsyncState; //Use this to get the socket so we can retrieve the data from it Socket s = state.workSocket; //Variable to hang on to the amount of data we received int received = 0; //Call the socket EndReceive Method received = s.EndReceive(AR); //If we received no data from the client, they have disconnected if (received == 0) { //Remove them from our clientlist clientList.Remove(state); //Tell the other clients that they have left foreach (StateObject others in clientList) others.workSocket.Send(Encoding.ASCII.GetBytes("Client disconnected " + s.LocalEndPoint)); //We return here - this callback thread is now dead. return; } //If we received data if (received > 0) { //Append the data to our StringBuilder state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, received)); } //Build the string and let's see if it contains a new line character string content = state.sb.ToString(); //If the data has a new line character if(content.Contains("\n") && content.Length > 1) { //Send this data to every other client foreach (StateObject others in clientList) if (others != state) //Make sure we don't send it back to the sender others.workSocket.Send(Encoding.ASCII.GetBytes(content.ToCharArray())); state.sb.Clear(); //Clear their StringBuilder so we can retrieve the next message } //Clear the temporary buffer Array.Clear(state.buffer, 0, StateObject.BufferSize); //And prepare to receive more data s.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state); } } class StateObject { public Socket workSocket = null; public const int BufferSize = 1024; public byte[] buffer = new byte[BufferSize]; public StringBuilder sb = new StringBuilder(); } 

Asynchronous

Some explanation:

No problem at all. I didn't really understand them either so I steered clear of them for too long. Basically the callback methods are registered as events. When something triggers them (like receiving data), the server executes the function. Calling BeginAccept can be thought of as creating a new thread that waits for a connection before executing the linked method.

When it receives one, it executes the AcceptCallback method that we entered as a parameter. From there, AcceptCallback sets up BeginAccept again which waits for a new connection as well as BeginReceive which waits for data to be sent from the client.

Neither of these methods block execution (think of it as creating a new thread and starting it) and as soon as they are triggered their respective functions are called.

Each client is assigned a StateObject that contains their socket and a buffer, and these are put in a list that we can loop through and access.

The StateObject is passed as a parameter with the async calls so that we can tell which client sent the data and store it in a separate buffer.

Second edit:

Your client receive code looks okay. The server will not respond with data to the client sending data. If I sent the server "Hello!" it will forward that message to all other connected clients. If you want to try receiving something, connect 2 clients. Or, you can simply remove the line of code if (others != state) //Make sure we don't send it back to the sender

I wrote a client/server for chat purposes and the code can be found here:

Async chat server buffer issue

Sign up to request clarification or add additional context in comments.

5 Comments

I really appreciate your answer, certainly I'll google it! But, could you explain to me (in very basics words) how async works? More specifically, I'm not well familliarized with Callback term.. to be honest, its the first time that I'll try to use it.. but thanks again
I edited with some explanation. I'm on my phone, but I will write more if you want more of an explanation.
Its perfect.. I understood a bit more, thank you! I put your server code on my application, and my client sent an message that was successfully received by server. However, my client can't receive any messages from the server. Could you post some code example of a simple client?
Your client receive code looks okay. The server will not respond with data to the client sending data. If I sent the server "Hello!" it will forward that message to all other connected clients. If you want to try receiving something, connect 2 clients. Or, you can simply remove the line of code if (others != state) //Make sure we don't send it back to the sender
Ok! Finally works! Thanks for your time! Helps me a lot buddy!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.