Reputation: 513
I've been coding a chat program that is made of two parts: server and client. The server uses TcpListener
to listen for incoming requests to join the server, and the client uses TcpClient
that it uses to connect to the server, and send messages. So far I have the handshake that is the first message sent when the client connects - it contains the client's IP address and the nickname set. I don't know how to make the server to listen asynchronously, because if it's not async, then the chat will be client -> server -> client -> server
whereas it needs to be connected to multiple clients at once, and receive multiple messages at once. My code so far is written synchronously:
Client:
public void StartClient(string serverIP)
{
ReadWrite rw = new ReadWrite();
string nick = rw.GetNick();
string handshakeContents = "HANDSHAKE:" + nick + ":" + GetIP();
Int32 serverPort = 1336; // yay
TcpClient client = new TcpClient(serverIP, serverPort);
Console.WriteLine("Sending initial handshake: {0}", handshakeContents);
Byte[] data = System.Text.Encoding.ASCII.GetBytes(handshakeContents); // Convert handshakeContents to Byte[]
NetworkStream stream = client.GetStream(); // Instantiate object "stream" for use in read/write
stream.Write(data, 0, data.Length); // Send handshakeContents in Byte[] to server
Console.WriteLine(" - Connecting to IlanChat Server on ip {0}", serverIP);
Thread.Sleep(1000); // sleep 1s
data = new Byte[256];
string responseHandshake = String.Empty; // response handshake from server
Int32 bytes = stream.Read(data, 0, data.Length); // Read handshake.
responseHandshake = System.Text.Encoding.ASCII.GetString(data, 0, bytes); // Decode from Byte[] to ASCII string
Console.WriteLine(" - Received response handshake: {0}", responseHandshake);
Console.WriteLine(" - Successfully connected to IlanChat server on IP {0}", serverIP); // display message
Thread.Sleep(2000);
Console.Clear();
List<string> messagesRecieved = new List<string>();
while(true)
{
Console.Clear();
foreach (string msg in messagesRecieved)
{
Console.WriteLine(msg);
}
Console.WriteLine();
Console.Write("Enter message: ");
string msgRaw = Console.ReadLine();
string sendMsg = nick + ": " + msgRaw;
data = new Byte[256];
data = System.Text.Encoding.ASCII.GetBytes(sendMsg);
stream.Write(data, 0, data.Length); // finish this async shit
}
}
Server:
List<string> clientIP = new List<string>();
List<string> clientNicks = new List<string>();
string responseHandshake = "hello";
Int32 serverPort = 1336;
IPAddress machineIP = IPAddress.Parse(GetIP());
Console.Clear();
Console.WriteLine(" - Starting IlanChat server on IP {0}", machineIP);
TcpListener server = null;
server = new TcpListener(machineIP, serverPort);
server.Start();
Byte[] buffer = new Byte[256];
String data = null;
Console.WriteLine("Successfully started IlanChat server!");
while (true) // first alpha - only one user at a time
{
Console.WriteLine();
Console.WriteLine("Waiting for connections..");
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("User connecting..");
Console.WriteLine("Receiving handshake data..");
data = null;
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(buffer, 0, buffer.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(buffer, 0, i);
Console.WriteLine("Received handshake data: {0}", data);
Console.WriteLine("Processing data.."); // sample handshake: - HANDSHAKE:nick:ip
string tempNick = data.Replace("HANDSHAKE:", "");
string[] userDetails = tempNick.Split(':'); // should store to 0:nick, 1:ip
Console.WriteLine("Received client nick: {0}", userDetails[0]);
Console.WriteLine("Received client IP: {0}", userDetails[1]);
break;
}
Thread.Sleep(1100); // sleep
buffer = System.Text.Encoding.ASCII.GetBytes(responseHandshake);
Console.WriteLine("Sending response handshake..");
stream.Write(buffer, 0, buffer.Length);
}
Is there a way to make the server accept multiple connections at once, and maintain them? And is there a way to make the client receive multiple messages at once and type while refreshing the messages?
Upvotes: 0
Views: 2116
Reputation: 1963
You need to look at using threading (Task library, async/await) to do what you want. Try looking here:
To get you started, assuming the logic in your code is correct you want to split up the server/client, something like this:
static void RunServer()
{
List<string> clientIP = new List<string>();
List<string> clientNicks = new List<string>();
string responseHandshake = "hello";
Int32 serverPort = 1336;
IPAddress machineIP = IPAddress.Parse(GetIP());
Console.Clear();
Console.WriteLine(" - Starting IlanChat server on IP {0}", machineIP);
TcpListener server = null;
server = new TcpListener(machineIP, serverPort);
server.Start();
Byte[] buffer = new Byte[256];
String data = null;
Console.WriteLine("Successfully started IlanChat server!");
while (true) // first alpha - only one user at a time
{
Console.WriteLine();
Console.WriteLine("Waiting for connections..");
TcpClient client = server.AcceptTcpClient();
Task.Run(() => RunClient(client));
}
}
static void RunClient(TcpClient client)
{
Byte[] buffer = new Byte[256];
Console.WriteLine("User connecting..");
Console.WriteLine("Receiving handshake data..");
String data = null;
string responseHandshake = "hello";
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(buffer, 0, buffer.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(buffer, 0, i);
Console.WriteLine("Received handshake data: {0}", data);
Console.WriteLine("Processing data.."); // sample handshake: - HANDSHAKE:nick:ip
string tempNick = data.Replace("HANDSHAKE:", "");
string[] userDetails = tempNick.Split(':'); // should store to 0:nick, 1:ip
Console.WriteLine("Received client nick: {0}", userDetails[0]);
Console.WriteLine("Received client IP: {0}", userDetails[1]);
break;
}
Thread.Sleep(1100); // sleep
buffer = System.Text.Encoding.ASCII.GetBytes(responseHandshake);
Console.WriteLine("Sending response handshake..");
stream.Write(buffer, 0, buffer.Length);
}
Upvotes: 1