I created a multithreaded socket server in C # but it is not compatible with Unity, here is my code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThreaded_Server
{
class Program
{
class ComClient //Class interne
{
public Socket sock2 { get; set; } //Recupère la socket du client
public int numero { get; set; } //Et son numéro
public ComClient(Socket sock, int num)
{
this.sock2 = sock;
this.numero = num;
}
}
private int nClients;
public Program() //Constructeur
{
Console.WriteLine(" >>> Setting up the server... ");
for (int i = 0; i < 3; i++) //Waiting Time
{
Console.WriteLine("...");
System.Threading.Thread.Sleep(1000); //Wait 1 Sec
}
Console.WriteLine(" >>> Server is up ! ");
Socket sock1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Création de la socket type IpV4, Point à Point et Protocole Tcp.
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse("192.168.20.222"), 2906); //Adresse et port d'écoute
sock1.Bind(ipEP); //Bind la socket sur le IpEndPoint
sock1.Listen(1); //Demande d'écoute
while (true) //Boucle infinie
{
Socket sock = sock1.Accept(); //Attente et acceptation des clients
++ nClients;
ComClient ComC = new ComClient(sock, nClients); //Donne la socket et le numero au client
Thread th1 = new Thread(Comm); //Thread pour la communication client
th1.Start(ComC); //Recupère les 2 paramètres grâce a la class ComClient
}
}
private void Comm(object obj) //Se lance a chaque connexion d'un client
{
try
{
ComClient ComC = obj as ComClient; //Pioche dans la class ComClient
NetworkStream stream = new NetworkStream(ComC.sock2);
TextReader textR = new StreamReader(stream); //Attente d'une chaine de caractères
TextWriter textW = new StreamWriter(stream); //Envoie d'une chaine de caractères vers le client
Console.WriteLine(" >>> Connection from client number: " + ComC.numero); //Affiche la connexion du client et son num
textW.WriteLine(" >>> Connection in progress... ");
textW.Flush(); //Nettoyage du buffer et envoie
for (int i = 0; i < 1; i++) //Waiting Time
{
textW.WriteLine("...");
textW.Flush(); //Nettoyage du buffer et envoie
System.Threading.Thread.Sleep(1000); //Wait 1 Sec
}
textW.WriteLine(" >>> Connection successful, you are the client number: " + ComC.numero);
textW.Flush(); //Nettoyage du buffer et envoie
string ipAdr = ComC.sock2.RemoteEndPoint.ToString(); //Adresse IP du client + port
Console.WriteLine(" >>> Client's ip address: " + ipAdr);
while (true)
{
string request = textR.ReadLine(); //Attente et lecture message client
Console.WriteLine(" >>> Text from client number: " + ComC.numero);
Console.WriteLine(request);
textW.WriteLine(" >>> Echo: " + request);//Réponse au client
textW.Flush(); //Nettoyage du buffer et envoie
}
}
catch
{
ComClient ComC = obj as ComClient; //Pioche dans la class ComClient
Console.WriteLine(" >>> Client disconnected: " + ComC.numero);
ComC.sock2.Close(); //Fermeture de la socket
ComC.sock2.Dispose();
}
}
static void Main(string[] args)
{
new Program(); //Instance de la classe
}
}
}
I’m a beginner with Unity, so I’m not sure what I need to do to make it compatible with Unity. I think I already have to remove the main because there are none in the Unity scripts, and also add “MonoBehavior” with my class, but i’m not sure.
What I would like at the end is to be able to recover the position of the boat in the water, its axis, etc. And that its data be sent to my socket server
Is it possible? Because I know how to recover the position of the boat, but I do not know how to make my server compatible with Unity, can someone help me ?
You can just import the server’s namespace and create a server with “new Program();” from wherever. That should work. Unless you want your server to get sent messages by your Unity game? In that case you just run it in the normal manner as a different program and then write a small thing to send the boat’s coordinates to the same socket as your server is listening to.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
public class ServerSocket : MonoBehaviour {
class ComClient //Class interne
{
public Socket sock2 { get; set; } //Recupère la socket du client
public int numero { get; set; } //Et son numéro
public ComClient(Socket sock, int num)
{
this.sock2 = sock;
this.numero = num;
}
}
private int nClients;
// Use this for initialization
void Start () {
Thread th = new Thread(Listen);
th.Start();
}
private void Listen()
{
Console.WriteLine(" >>> Setting up the server... ");
for (int i = 0; i < 3; i++) //Waiting Time
{
Console.WriteLine("...");
System.Threading.Thread.Sleep(1000); //Wait 1 Sec
}
Console.WriteLine(" >>> Server is up ! ");
Debug.Log("Serveur en marche");
Socket sock1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Création de la socket type IpV4, Point à Point et Protocole Tcp.
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse("192.168.20.222"), 2906); //Adresse et port d'écoute
sock1.Bind(ipEP); //Bind la socket sur le IpEndPoint
sock1.Listen(1); //Demande d'écoute
Debug.Log("Socket en écoute");
while (true) //Boucle infinie
{
Debug.Log("Attente de client");
Socket sock = sock1.Accept(); //Attente et acceptation des clients
Debug.Log("Client accepté");
++nClients;
ComClient ComC = new ComClient(sock, nClients); //Donne la socket et le numero au client
Thread th1 = new Thread(Comm); //Thread pour la communication client
th1.Start(ComC); //Recupère les 2 paramètres grâce a la class ComClient
Debug.Log("Thread créé pour le client");
}
Debug.Log("Sortie boucle");
}
private void Comm(object obj) //Se lance a chaque connexion d'un client
{
Debug.Log("Avant le try");
try
{
Debug.Log("Entrée dans le try");
ComClient ComC = obj as ComClient; //Pioche dans la class ComClient
NetworkStream stream = new NetworkStream(ComC.sock2);
TextReader textR = new StreamReader(stream); //Attente d'une chaine de caractères
TextWriter textW = new StreamWriter(stream); //Envoie d'une chaine de caractères vers le client
Console.WriteLine(" >>> Connection from client number: " + ComC.numero); //Affiche la connexion du client et son num
textW.WriteLine(" >>> Connection in progress... ");
textW.Flush(); //Nettoyage du buffer et envoie
for (int i = 0; i < 1; i++) //Waiting Time
{
textW.WriteLine("...");
textW.Flush(); //Nettoyage du buffer et envoie
System.Threading.Thread.Sleep(1000); //Wait 1 Sec
}
textW.WriteLine(" >>> Connection successful, you are the client number: " + ComC.numero);
textW.Flush(); //Nettoyage du buffer et envoie
string ipAdr = ComC.sock2.RemoteEndPoint.ToString(); //Adresse IP du client + port
Console.WriteLine(" >>> Client's ip address: " + ipAdr);
Debug.Log("Adresse ip du client récupérée");
while (true)
{
string request = textR.ReadLine(); //Attente et lecture message client
Console.WriteLine(" >>> Text from client number: " + ComC.numero);
Console.WriteLine(request);
textW.WriteLine(" >>> Echo: " + request);//Réponse au client
textW.Flush(); //Nettoyage du buffer et envoie
}
}
catch
{
ComClient ComC = obj as ComClient; //Pioche dans la class ComClient
Console.WriteLine(" >>> Client disconnected: " + ComC.numero);
ComC.sock2.Close(); //Fermeture de la socket
ComC.sock2.Dispose();
Debug.Log("Client déconnecté");
}
}
// Update is called once per frame
void Update () {
}
}
I attached it to an empty Gamobject named “Socket server” and i launched my game.
So…
When I connect to telnet, the connection is accepted, I get back into my while, and I go back to the beginning to wait for another client connection, that’s ok.
But my telnet client is empty, I can not type anything in it and it does not show me anything, not even the greeting “You are the client number …”
I get the impression that my thread running a thread does not work (line 58-59)
Thread th1 = new Thread(Comm); //Thread pour la communication client
th1.Start(ComC); //Recupère les 2 paramètres grâce a la class ComClient
Can someone explain me what is wrong ?
And also sorry for the tags, i edited this
Thanks
I ran a quick test against my client implementation and I can connect, send and receive data.
That’s all completely byte[ ]-based though, it’s not using a TextReader to interpret the incoming data as text.
What you wanna make sure is, that - using the StreamReader’s ReadLine() - your incoming data needs to end with a newline \n. Otherwise it won’t trigger as it’s not interpreted as a line. Perhaps that’s already one of the issues that you struggle with.
I’d also like to point out that you shouldn’t create dedicated threads for everything. If you want to avoid blocking, either poll whether an action can complete (pending accept / bytes available to read) or just use the async versions of the methods, since these make use of the threadpool. The only thing to ensure would be that an Unity API access is done on the main thread (if necessary).
I modified a little my code, it works almost perfectly, the only worries remaining is to close the socket
My code remains locked in this while.
while (true)
{
request = textR.ReadLine(); //Attente et lecture message client
Console.WriteLine(" >>> Text from client number: " + ComC.numero);
Console.WriteLine(request);
textW.WriteLine(" >>> Echo: " + request);//Réponse au client
textW.Flush(); //Nettoyage du buffer et envoie
}
To close the socket, I wrote this code just below my while.
//ComClient ComC = obj as ComClient; //Pioche dans la class ComClient
Console.WriteLine(" >>> Client disconnected: " + ComC.numero);
ComC.sock2.Close(); //Fermeture de la socket
ComC.sock2.Dispose();
Debug.Log("Client déconnecté");
I tried using a try and catch, but it does not work
So the remaining solution was to put in end condition of my while “If the client is no longer connected”
I tried this:
while (!ComC.sock2.Connected)
But that didnt work
Can you explain me how can i say (“Continue as long as the client is logged in”) ?
One thing that may contribute to that is the fact that your threads run in the foreground. Any thread created using the constructor is a foreground-thread by default. There’s a property to change that behaviour, but like mentioned earlier, you should rather not create threads explicitly. Make use of the threadpool whenever you can. I’d highly recommend to just use the async methods.
Now with the above in mind, note that there are basically two situations:
your server disconnects: since you’re currently running foreground threads, they won’t terminate when the parent process is terminated. They’d terminate whenever they’re done. A background thread would terminate with your application.
Either way, you wanna make sure that you disconnect and properly dispose all clients whenever the connection is no longer needed.
your client disconnects: the code above should throw an exception as it’s going to attempt a read/write operation on a no-longer valid connection (most-likely a SocketException).
You’d automatically break out of the loop if it’s wrapped within a try/catch, unless the operation to fail is wrapped in another try/catch within the loop.
The sole check for “am i connected” won’t work in probably 99,xx% of all cases with your current code. For this to work, your connection needed to terminate just between a successful write&flush operation and the evaluation of the loop’s condition. If it happens to either run into a race condition or terminates after the evaluation, you’ll be left off with an exception from textR.ReadLine() anyway.