2

I have to make multiple clients communicate with the server, and the server chooses who to reply to. It is as if the client's only destination to send the message is the server. And the server chooses who to talk to.

The point is that I don't know how to make multiple clients first and direct the messages to any of those clients that I want.

I just got to do a 1 to 1. Client and server.

Also I do not know if I will have to use many threads, since I would need a thread to listen to all the connections of the new clients, another thread to listen to what they send me and thus be able to send.

There I leave my code.

SERVER

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Servidor_Chat
{
    class Server
    {
        IPAddress ipAddr;
        IPEndPoint endPoint;
        Socket s_Server;
        Socket s_Client;
        public Server()
        {
            ipAddr = IPAddress.Any;
            endPoint = new IPEndPoint(ipAddr, 1234);
            s_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            s_Server.Bind(endPoint);
            s_Server.Listen(1);
        } 

        public void Start()
        {
            Console.WriteLine("Esperando clientes...");
            s_Client = s_Server.Accept();
            Console.WriteLine("Un cliente se ha conectado.");
            IPEndPoint clientep = (IPEndPoint)s_Client.RemoteEndPoint;
            Console.WriteLine("Conectado con {0} en el puerto {1}", clientep.Address, clientep.Port);
        }

        public void Send(string msg)
        {
            string texto = "";
            byte[] textoAEnviar;
            texto = msg;
            textoAEnviar = Encoding.Default.GetBytes(texto);
            s_Client.Send(textoAEnviar, 0, textoAEnviar.Length, 0);
        } 

        public void Receive()
        {
            while (true)
            {
                Thread.Sleep(500);
                byte[] ByRec;
                string textoRecibido = "";
                ByRec = new byte[255];
                int a = s_Client.Receive(ByRec, 0, ByRec.Length, 0);
                Array.Resize(ref ByRec, a);
                textoRecibido = Encoding.Default.GetString(ByRec);
                Console.WriteLine("Client: " + textoRecibido);
                Console.Out.Flush();
            }
        }
    } 

    class Program
    {
        static void Main(string[] args)
        {
            Thread t;
            Server s = new Server();
            s.Start();
            t = new Thread(new ThreadStart(s.Receive));
            t.Start();
            while (true)
            {
                s.Send(Console.ReadLine());
            } 

            Console.WriteLine("Presione cualquier tecla para terminar");
            Console.ReadLine();
        }
    }
}

CLIENT

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading; 

namespace Cliente_Chat
{
    class Program
    {
        class Client
        {
            IPAddress ipAddr;
            IPEndPoint endPoint;
            Socket s_Client;
            public Client()
            {
                ipAddr = IPAddress.Parse("127.0.0.1");
                endPoint = new IPEndPoint(ipAddr, 1234);
                s_Client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            } 

            public void Start()
            {
                try
                {
                    s_Client.Connect(endPoint);
                    Console.WriteLine("Conectado con exito");
                }
                catch (SocketException e)
                {
                    Console.WriteLine("No se pudo conectar al servidor");
                    Console.WriteLine(e.ToString());
                    return;
                }
            } 

            public void Send(string msg)
            {
                string texto = "";
                byte[] textoAEnviar;
                texto = msg;
                textoAEnviar = Encoding.Default.GetBytes(texto);
                s_Client.Send(textoAEnviar, 0, textoAEnviar.Length, 0);
            } 

            public void Receive()
            {
                while (true)
                {
                    Thread.Sleep(500);
                    byte[] ByRec;
                    string textoRecibido = "";
                    ByRec = new byte[255];
                    int a = s_Client.Receive(ByRec, 0, ByRec.Length, 0);
                    Array.Resize(ref ByRec, a);
                    textoRecibido = Encoding.Default.GetString(ByRec);
                    Console.WriteLine("Server: " + textoRecibido);
                    Console.Out.Flush();
                }
            }
        } 

        static void Main(string[] args)
        {
            Thread t;
            Client c = new Client();
            c.Start();
            t = new Thread(new ThreadStart(c.Receive));
            t.Start();
            while (true)
            {
                c.Send(Console.ReadLine());
            } 

            Console.WriteLine("Presione cualquier tecla para terminar");
            Console.ReadLine();
        }
    }
}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Hades
  • 33
  • 1
  • 6
  • You would be able to find multiple examples of client server applications in the web. – theking2 Oct 16 '21 at 16:16
  • Depending on your requirements, there are multiple libraries and packages to accomplish what you are doing with minimal low-level code. If you will be using IIS for the server, you could use SignalR for both server and client and just focus on your business rules. The library will handle the rest. – eglease Oct 16 '21 at 16:31
  • If you cannot use IIS or don't want websockets, you can use https://github.com/chronoxor/NetCoreServer. It supports both client and server and does all the threading in the background so you can focus on your business logic. – eglease Oct 16 '21 at 16:33

2 Answers2

3

Do not use any low-level Socket libraries like TcpListener or System.Net.Sockets unless you are trying to learn socket programming or doing a school project. There are plenty of well-supported .NET libraries for doing client-server communication that handle low level sockets and multi-threading for you so you can focus on your business rules.

Will the final server run on IIS? If yes, look into using SignalR. It supports both client and server and has advance user management on the server side so the server can send replies or data to individual clients or whole groups based on custom criteria.

If you cannot use IIS, try NetCoreServer, It features async client and server and has examples using TCP, UDP, HTTP, and WebSockets.

There are multiple other libraries to consider. If you do decide to use sockets take a look at the list in this answer.

Update

Since this is for a school project where socket programming is a requirement, you can do the following.

Server

  • Use a tread-safe collection like a ConcurrectDictionary to store connected clients
  • Each client object would use events to receive data from its client as well as detect a client disconnect
  • The server would subscribe to those events and do whatever it needs when a message is received
  • When the server does any client operation like sending messages, it needs to lock the operation to avoid deadlocks
  • When a client disconnects, make sure to unsubscribe from any subscriptions for this client to avoid memory leaks
  • Here is a good example

Client

  • Can use TCPClient or plain Socket and SocketAsyncEventArgs
  • You can look at SocketAsyncEventArgs to check when an operation completes
  • You can use async sockets so you do not need to do threads manually
  • Here is a good example and here is another
eglease
  • 2,445
  • 11
  • 18
  • 28
  • 1
    If it is a school project to understand the operation, the question is a little different, the server behaves as a client at the same time, the only difference is that the server receives all the clients and can respond to each one separately, and the client obviously can only talk privately to the server. – Hades Oct 17 '21 at 00:29
  • I see. Looks like you are trying to do a chatroom where the chatroom is the server and the clients are clients. The server needs to keep a list of connected clients, I suggest `ConcurrentDictionary` where it adds or removes clients as they connect/disconnect. When the server needs to send something, it can iterate through the client list sending to each client using whatever business rules you have. This will need to be multi-threaded but you can avoid the details by using `Events` and `locks`. – eglease Oct 17 '21 at 01:00
  • Here is a good example https://gist.github.com/sontx/4fc63c676bf9b03789865b58928a113d. Notice that they have a `List` to store connections and `lock`s when sending data to avoid deadlocks. – eglease Oct 17 '21 at 01:03
  • Sure is equal to this. https://www.youtube.com/watch?v=QrdfegS3iDg&t=613s. As the server generates a list of clients, and I can select who to talk to about the connected ones. But the client does not, it does not generate a list and can only speak to one (To the server) Now I am trying to understand and pass all that video to pure socket if possible. – Hades Oct 17 '21 at 01:33
  • Where are you having the issue and need help? – eglease Oct 17 '21 at 01:43
  • At the moment I know how to create multiple clients, what if I think I would be missing from the server side as I do to be able to write to each client separately. I imagine a metood sendMessage and as a parameter the unique ID of the client. As for example I select a client, its id is taken and when it sent a message it sent its id and the message as a parameter. And I do the method. I am right? – Hades Oct 17 '21 at 02:19
  • That should be fine. The client can send its name after it connects and use can use it as a key in the `ConcurrentDictionary`. – eglease Oct 17 '21 at 02:24
  • i am on one if i can only use sockets. or if or if I have to use tcpclient or tpclistener. – Hades Oct 18 '21 at 17:27
  • You can use either. `TcpListener` and `TcpClient` might be easier. You can also use plain sockets on one (client or server) and `Tcp...` on the other. – eglease Oct 18 '21 at 17:53
  • You can help me with something, in the events, I have been trying to understand it for hours or copy from the examples you gave me, and I can't do it. I was already able to make it multi-tenant, using tcplcient and tcplistener. I have a method to send a message to a specific client. The question that I do not know how to place the events in connected, disconnected and message received. – Hades Oct 20 '21 at 20:43
  • Is this on the client or the server? You will need to create an `Event` and subscribe to it. The server example in the answer shows how this is done in the `Worker` class. – eglease Oct 20 '21 at 20:50
  • Of course I try to understand but I can't. I am on the server. I almost have everything the same as the code, only the events are missing. Onda never showed anything. And I tried to show something but it didn't come out either – Hades Oct 20 '21 at 21:05
  • I'm trying the disconnected client – Hades Oct 20 '21 at 21:06
  • So you want to disconnect (close connection) to the client from the server? ```tcpClient.GetStream().Close(); tcpClient.Close();``` – eglease Oct 20 '21 at 21:08
  • Nono, but show a message when disconnected through events. As in the example – Hades Oct 20 '21 at 21:14
  • The events show that the client has disconnected on its own and it just lets the server be aware of this. Please create a new questions with your latest code. – eglease Oct 20 '21 at 21:15
  • Also try to copy and paste all the code and modify it in such a way that the user does not ask, and that it only shows what I send and it did not work for me either. Now I create another question. – Hades Oct 20 '21 at 21:21
  • The same is that the code is the same as the one in the example. Equal to 100%. The only thing that doesn't work for me is the BroadcastMessage – Hades Oct 20 '21 at 21:23
  • 1
    https://stackoverflow.com/questions/69653295/i-dont-know-how-to-use-event-in-c-tcp-ip – Hades Oct 20 '21 at 21:34
  • Seems like the question was deleted. Was it by you or someone else? – eglease Oct 20 '21 at 22:58
  • 1
    For me, it was my mistake. I do not know what happened to me that put the Console.WriteLine and it did not show it. and now it shows it. It must be for so many hours on the pc that I don't even know what to do. What if I am having problems in passing it to Windows Forms. – Hades Oct 20 '21 at 23:16
  • 1
    https://es.stackoverflow.com/questions/491989/i-dont-know-how-to-use-threads-in-windows-form-tcp Do you know what this can be? I also tried to pass the example code to windows Form and the same thing happens to me. In this case it is the customer in question. – Hades Oct 21 '21 at 03:59
  • I am getting `Página no encontrada` when I go to the new question. – eglease Oct 21 '21 at 13:19
  • There are many examples on how this can be done with Win Forms. Check you http://csharp.net-informations.com/communications/csharp-client-socket.htm. You can also use a background thread but there will be an issue communicating between it and the GUI. – eglease Oct 21 '21 at 13:26
  • I do not know why it is not seen. But the example that you sent me chatroom server pass it to windows forms. Do not leave me. And I don't know what the solution would be or adapt the code or change all the code completely without the server and client classes, and put everything in Form1 – Hades Oct 21 '21 at 19:03
  • Create a new questions on the English site. I cannot post on the Spanish site as the answers will get flagged. You can also create a question on the Spanish site and see if someone can answer it. – eglease Oct 21 '21 at 19:38
  • https://stackoverflow.com/questions/69669162/tcp-server-i-can-from-another-class-call-the-components-of-form1 Now I think so. Sorry if I use the translator a lot – Hades Oct 21 '21 at 21:51
  • I have a problem, you saw the example code that you gave me, for example I stop the server (I hit the stop button) and the client sends a message and the server keeps receiving. I do not know how to fix it – Hades Oct 26 '21 at 00:51
  • Did the problem above get solved? Were you able to connect it to the GUI to start? If yes, close that issue and open a new open. You should just need to add the close commands for the server to the stop button. I'll look at it in the morning. – eglease Oct 26 '21 at 03:02
  • Also, make sure to mention if you want to restart the server after a close or if you shut down the program. They could be a delay between stopping the server and it releasing the socket. – eglease Oct 26 '21 at 03:04
2

You can use TCP/IP to communicate with server using multiple clients

check this question and answers Server Client send/receive simple text

You don't need to deal with threads and tasks since .NET TCP classis takes care of that for you.

Note that in the TCP listener code you must do it in a while loop in order to keep the listener up and running:

 static void Main(string[] args)
{
    //---listen at the specified IP and port no.---
    IPAddress localAdd = IPAddress.Parse(SERVER_IP);
    TcpListener listener = new TcpListener(localAdd, PORT_NO);
    Console.WriteLine("Listening...");
    listener.Start();
    while (true)
    {
        //---incoming client connected---
        TcpClient client = listener.AcceptTcpClient();

        //---get the incoming data through a network stream---
        NetworkStream nwStream = client.GetStream();
        byte[] buffer = new byte[client.ReceiveBufferSize];

        //---read incoming stream---
        int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);

        //---convert the data received into a string---
        string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
        Console.WriteLine("Received : " + dataReceived);

        //---write back the text to the client---
        Console.WriteLine("Sending back : " + dataReceived);
        nwStream.Write(buffer, 0, bytesRead);
        client.Close();
    }
    listener.Stop();
    Console.ReadLine();
}
sabsab
  • 1,073
  • 3
  • 16
  • 30
  • Sure, I would have understood that, the question that from what I try if I keep it as a thread like this, I can't do anything else, right? Because, for example, if I want to answer the messages of all the clients that are talking to me, I could not because there is the while, therefore I would not have to use a thread or task? Sorry for the ignorance. It is a school project. – Hades Oct 17 '21 at 00:27
  • Console.WriteLine("Sending back : " + dataReceived); you can replace this code with what ever you want to respond to sender or do any event – sabsab Oct 17 '21 at 21:16
  • Sure, that's how it is to talk to everyone, but if in case you want to talk to a specific one, would you have to save the socket or something so to speak? – Hades Oct 18 '21 at 01:39
  • 1
    Yes, you would have to save a socket or `TcpClient` for every client on the server. Hence you would have a Collection of clients and select who to send data to from that collection. – eglease Oct 18 '21 at 21:58
  • @Hades if the answer helped you please accept the answer – sabsab Oct 19 '21 at 17:54