C# TCP Socket Distinguish from different clients - c#

I might have a pretty basic question for some of you.
I have created a one(server) to many(clients) relationship where in which server selects a client to send and receive data from and to.
My question is, how can I distinguish incoming data from the different clients? Would I have to simply indicate this in a buffer?
Currently My server listens, accepts and adds clients to a list clientList:
private void StartListen()
{
//Creating a TCP Connection and listening to the port
tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666);
tcpListener.Start();
toolStripStatusLabel1.Text = "Listening on port 6666 ...";
int counter = 0;
appStatus = 0;
while (true)
{
try
{
client = tcpListener.AcceptTcpClient();
counter++;
clientList.Add(client);
IPEndPoint ipend = (IPEndPoint)client.Client.RemoteEndPoint;
//Updating status of connection
toolStripStatusLabel1.Text = "Connected from " + IPAddress.Parse(ipend.Address.ToString());
appStatus = 1;
th_inPutStream = new Thread(delegate () { inPutStream(client); });
th_inPutStream.Start();
th_checkConnection = new Thread(checkConnection);
th_checkConnection.Start();
}
catch (Exception err)
{
Cleanup_dep();
}
}
}
Now i assume if I want to send a message to a specific client, id select the client by clientList[i] and send data.
My question is, when data is available in the network stream to read, how can i know which client its coming from?
This is how I manage incoming data:
private void inPutStream(TcpClient client)
{
try
{
while (true)
{
NetworkStream networkstream = client.GetStream();
if (networkstream.DataAvailable == true)
{
int messageID = 0;
int messageSize = 0;
int bufferSize = 100;
//First retrieve size of header
byte[] fileSizeBytes = new byte[4];
int bytes = networkstream.Read(fileSizeBytes, 0, 4);
int dataLength = BitConverter.ToInt32(fileSizeBytes, 0);
//Read stream containing header with message details
byte[] header = new byte[dataLength];
int headerbyte = networkstream.Read(header, 0, dataLength);
string headerString = Encoding.ASCII.GetString(header, 0, headerbyte);
// Process Message information ie Message ID & Message Size
string[] headerSplit = headerString.Split(new string[] { "\r\n" }, StringSplitOptions.None);
Dictionary<string, string> headers = new Dictionary<string, string>();
foreach (string s in headerSplit)
{
if (s.Contains(":"))
{
headers.Add(s.Substring(0, s.IndexOf(":")), s.Substring(s.IndexOf(":") + 1));
}
}
//Fetch Message ID & Size
messageSize = Convert.ToInt32(headers["len"]);
messageID = Convert.ToInt32(headers["MsgId"]);
//Filter actions by Message ID
if (messageID == 1)//Machine info
{
string machineinfo = processMessage(messageSize, bufferSize, networkstream);
clientNames.Add(machineinfo.Split(new char[] { '\r', '\n' })[0]);
updateClientListUI();
}
if (messageID == 2)//CMD Commands
{
updateText(txtCmdConsole, processMessage(messageSize, bufferSize, networkstream));
}
}
}
}
catch (Exception err)
{
{
Cleanup_dep();
}
}
}
So if networkstream.DataAvailable == true then it reads, but how can i know from which stream?

Related

C# multi threaded or multi process?

I have a small question about multithreaded connections between one (Server) to many (Clients) relationship.
My question is:
Is my script multithreaded? Or Multi process? I have received some feedback stating that it is multiprocess?
Would each client receive their stream independently?
I'd just like to know if this is the correct approach to this.
Here you can see I start to listen to client connections and create a thread for each client connection and I pass the client :
private void StartListen()
{
//Creating a TCP Connection and listening to the port
tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666);
tcpListener.Start();
toolStripStatusLabel1.Text = "Listening on port 6666 ...";
int counter = 0;
while (true)
{
try
{
client = tcpListener.AcceptTcpClient();
clientList.Add(client);
IPEndPoint ipend = (IPEndPoint)client.Client.RemoteEndPoint;
//Updating status of connection
toolStripStatusLabel1.Text = "Connected from " + IPAddress.Parse(ipend.Address.ToString());
th_inPutStream = new Thread(delegate () { inPutStream(client, counter); });
th_inPutStream.Start();
counter++;
}
catch (Exception err)
{
Cleanup_dep();
}
}
}
This is where I handle the client input stream where i pre-process the client input stream:
private void inPutStream(TcpClient client, int clientID)
{
try
{
while (true)
{
NetworkStream networkstream = client.GetStream();
if (networkstream.DataAvailable == true)
{
int messageID = 0;
int messageSize = 0;
int bufferSize = 100;
//First retrieve size of header
byte[] fileSizeBytes = new byte[4];
int bytes = networkstream.Read(fileSizeBytes, 0, 4);
int dataLength = BitConverter.ToInt32(fileSizeBytes, 0);
//Read stream containing header with message details
byte[] header = new byte[dataLength];
int headerbyte = networkstream.Read(header, 0, dataLength);
string headerString = Encoding.ASCII.GetString(header, 0, headerbyte);
// Process Message information ie Message ID & Message Size
string[] headerSplit = headerString.Split(new string[] { "\r\n" }, StringSplitOptions.None);
Dictionary<string, string> headers = new Dictionary<string, string>();
foreach (string s in headerSplit)
{
if (s.Contains(":"))
{
headers.Add(s.Substring(0, s.IndexOf(":")), s.Substring(s.IndexOf(":") + 1));
}
}
//Fetch Message ID & Size
messageSize = Convert.ToInt32(headers["len"]);
messageID = Convert.ToInt32(headers["MsgId"]);
//Filter actions by Message ID
if (messageID == 1)//Machine info
{
string machineinfo = receiveMessage(messageSize, bufferSize, networkstream);
clientNames.Add(machineinfo.Split(new char[] { '\r', '\n' })[0]);
updateClientListUI();
}
if (messageID == 2)//CMD Commands
{
cmdres = receiveMessage(messageSize, bufferSize, networkstream);
activeForm.updateText(cmdres);
}
}
}
}
catch (Exception err)
{
Cleanup_dep();
}
}
Since you are using Thread and not Process I don't see any indication as to why this would be a multi process approach.
Without knowing how TcpClient.GetStream is exactly implemented, I would be suprised if the call on one client would have anything to do with the same call on another client. Other than obvious, unavoidable, coherences like sharing the same bandwidth if the connections are accepted on the same nic of course.
Depends on what you are trying to do. If this is for work, I would question if there isn't an existing solution you could use. If this is for education there are a few improvements that come to mind:
use async and Task instead of Thread is usually recommended
using var networkstream = client.GetStream(); will make sure the stream is disposed of correctly

TCP Server closes connection after first message

i have the following code executed by a listening Thread. What it does: Reading the first Message as the total message length then assemble all packets to a big data array. (Im sending images) It all works as intended.
But after the first image is recieved and the function is done. ("ImageLengthResetted" gets printed) It closes the connection. I think this is due the fact i am running out of the scope from:
using(connectedTcpClient = tcpListener.AcceptTcpClient())
and thats what kills the connection. How can i keep this connection open?
Adding another
while(true)
after i've been connected wont do the trick. As well as executing the while loop completle after the using statments.
private void ListenForIncommingRequests()
{
try
{
// Create listener on localhost port 8052.
localAddr = IPAddress.Parse(IPadrr);
Debug.Log(localAddr);
tcpListener = new TcpListener(localAddr, port);
Debug.Log("Before Init tcplistern");
tcpListener.Start();
Debug.Log("Server is listening");
Byte[] dataRecieved = new Byte[SEND_RECIEVE_COUNT];
while (true)
{
using (connectedTcpClient = tcpListener.AcceptTcpClient())
{
Debug.Log("Accepted TCP Client");
// Get a stream object for reading
using (NetworkStream stream = connectedTcpClient.GetStream())
{
int length;
Debug.Log("Accepted Stream");
// Read incomming stream into byte arrary.
while ((length = stream.Read(dataRecieved, 0, dataRecieved.Length)) != 0)
{
Debug.Log("receiving Loop lengt: " + length);
counterReceived++;
//Get Message length with first message
if (messageLength == 0)
{
messageLength = System.BitConverter.ToInt32(dataRecieved, 0);
data = new byte[messageLength];
messageJunks = messageLength / SEND_RECIEVE_COUNT;
restMessage = messageLength % SEND_RECIEVE_COUNT;
junkCounter = 0;
}
else
{
if (junkCounter < messageJunks)
{
Array.Copy(dataRecieved, 0, data, junkCounter * SEND_RECIEVE_COUNT, SEND_RECIEVE_COUNT);
junkCounter++;
}
else
{
Array.Copy(dataRecieved, 0, data, junkCounter * SEND_RECIEVE_COUNT, restMessage);
//Whole Message recieved, reset Message length
messageLength = 0;
readyToDisplay = true;
Debug.Log("ImageLengthResetteed");
}
}
}
}
}
}
}
catch (Exception socketException)
{
Debug.Log("SocketException " + socketException.ToString());
}
}
Client Side opens send Thread with following function where socketConnection gets globally initialized on the receiving thread of the client:
private void sendData(byte[] data)
{
try
{
//socketConnection = new TcpClient(IPadrr, port);
using (NetworkStream stream = socketConnection.GetStream())
{
//Prepare the Length Array and send first
byte[] dataLength = BitConverter.GetBytes(data.Length);
int packagesSend = 0;
int numberPackages = data.Length / SEND_RECIEVE_COUNT;
if (stream.CanWrite)
{
for (counter = 0; counter <= data.Length; counter += SEND_RECIEVE_COUNT)
{
if (counter == 0)
{
stream.Write(dataLength, 0, dataLength.Length);
}
if (packagesSend < numberPackages)
{
stream.Write(data, counter, SEND_RECIEVE_COUNT);
Thread.Sleep(20);
packagesSend++;
}
else
{
stream.Write(data, counter, data.Length % SEND_RECIEVE_COUNT);
Debug.Log("FINDISCHD");
}
}
}
}
}
catch (Exception err)
{
print(err.ToString());
}
}
Im glad for any help!
The Problem was on the Client Side. I initialized the
NetworkStream stream;
now globally in the same function the socketConnection gets init.

TCP Socket recieves data wrong in C#

I'm trying to send files from a Socket to another Socket. The sockets are running in two different applications, a client application and a server application. This is happening on the same machine now when testing it. The client first sends to the server when it is ready to receive first info about the file that will be sent to it like filename and file size in bytes in a 2056 bytes message. Then it sends a message each time it is ready to receive a new 2048 buffer of file content.
In short, this is the communication flow for each file:
client -"CLIENT_READY_TO_RECEIVE_FILE_INFO"-------> server
client <-"File name+file size of file coming next"- server
client -"CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER" -> server
client <-"2048 byte buffer of file content"-------- server
...the same flow is repeated until all files are sent to the client.
My problem is that the client receives the file info message wrong, although I have double checked that the server sends it corretly. They both use ASCII encoding. Here is my code:
Client code (receives the files):
private void fetchFilesThreadMethod()
{
String localHostName = Dns.GetHostName();
IPAddress[] localIPs = Dns.GetHostAddresses(localHostName);
IPAddress localIP = localIPs[2];
int port = 1305;
IPEndPoint localEP = new IPEndPoint(localIP,port);
fetchFilesSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
fetchFilesSocket.Bind(localEP);
fetchFilesSocket.Listen(1);
fetchFilesSocket = fetchFilesSocket.Accept();
while (true)
{
byte[] receivedFileInfo = new byte[2056];
byte[] receivedFileCont = new byte[2048];
byte[] comMessage = new byte[100];
byte[] comMessageBytes;
String fileInfoStr = String.Empty;
String fileNameStr = String.Empty; ;
String fileExtStr = String.Empty;
String comMessageStr = String.Empty;
String fileSizeStr = String.Empty;
ulong fileSize = 0;
ulong lastBytesSize = 0;
comMessageStr = "CLIENT_READY_TO_RECEIVE_FILE_INFO";
comMessageBytes = Encoding.ASCII.GetBytes(comMessageStr);
for (int i = 0; i < comMessageBytes.Length; i++)
comMessage[i] = comMessageBytes[i];
Console.WriteLine("Bytes available to be flushed by client: " + fetchFilesSocket.Available);
if (fetchFilesSocket.Available > 0)
{
Console.WriteLine(fetchFilesSocket.Available + " bytes flushed by client.");
byte[] flusher = new byte[fetchFilesSocket.Available];
fetchFilesSocket.Receive(flusher, 0, fetchFilesSocket.Available, SocketFlags.None);
}
fetchFilesSocket.Send(comMessage, 0, 100, SocketFlags.None);
Console.WriteLine("Client sent ready to receive file info.");
fetchFilesSocket.Receive(receivedFileInfo,0,2056, SocketFlags.None);
fileInfoStr = Encoding.ASCII.GetString(receivedFileInfo);
Console.WriteLine("Received file info:" + fileInfoStr);
fileNameStr = fileInfoStr.Split(new String[] { "ENDN" }, StringSplitOptions.None)[0];
Console.WriteLine("Received file name:" + fileNameStr);
fileExtStr = fileNameStr.Split('.').Last();
Console.WriteLine("Received file extension:" + fileExtStr);
fileSizeStr = fileInfoStr.Split(new String[] {"ENDS"},StringSplitOptions.None)[0];
fileSizeStr = fileSizeStr.Split(new String[] {"ENDN"},StringSplitOptions.None).Last();
Console.WriteLine("File size string:" + fileSizeStr);
fileSize = Convert.ToUInt64(fileSizeStr,10);
Console.WriteLine("Received file size:" + fileSize);
lastBytesSize = fileSize % 2048;
ulong byteCount = 0;
bool keepReceiving = true;
ulong buffersReceived = 0;
while (keepReceiving)
{
comMessageStr = "CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER";
comMessageBytes = Encoding.ASCII.GetBytes(comMessageStr);
for (int i = 0; i < comMessageBytes.Length; i++)
comMessage[i] = comMessageBytes[i];
fetchFilesSocket.Send(comMessage, 0, 100, SocketFlags.None);
Console.WriteLine("Console sent ready to receive buffer message.");
if (fileSize - byteCount >= 2048)
{
fetchFilesSocket.Receive(receivedFileCont, 0, 2048, SocketFlags.None);
buffersReceived++;
Console.WriteLine("Buffers received:" + buffersReceived);
byteCount = byteCount + 2048;
}
else
{
fetchFilesSocket.Receive(receivedFileCont, 0, 2048, SocketFlags.None); buffersReceived++;
byteCount = byteCount + 2048;
Console.WriteLine("Buffers received:" + buffersReceived);
keepReceiving = false;
}
Console.WriteLine("Bytes received " + byteCount + "/" + fileSize);
//Console.WriteLine("Received bytes in current file:" + byteCount);
}
Console.WriteLine("File received.");
}
}
Server code (sends the files):
private void fetchThreadMethod(Object commandArgs)
{
if (fetchSocket == null)
{
fetchSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int port = 1304;
IPEndPoint fetchSocketEP = new IPEndPoint(localIP,port);
fetchSocket.Bind(fetchSocketEP);
}
if (!fetchSocket.Connected)
try
{
fetchSocket.Connect(remoteIP, 1305);
if (fetchSocket.Connected)
Console.WriteLine("Server fetch socket connected.");
}catch(Exception e)
{
Console.WriteLine("Something went wrong when connecting the server fetch socket.");
}
FileCollector fCollector = new FileCollector();
String userName = Environment.GetEnvironmentVariable("USERPROFILE");
String targetDirectory = userName + "\\" + commandArgs;
Console.WriteLine("Path sent to file collector:" + targetDirectory);
fCollector.startFileCollector(targetDirectory);
List<FileNode> collectedFiles = fCollector.getCollectedFiles();
String comMessageStr = String.Empty;
foreach (FileNode fNode in collectedFiles)
{
comMessageStr = String.Empty;
byte[] sentFileInfo = new byte[2056];
byte[] sentFileCont = new byte[2048];
byte[] comMessage = new byte[100];
String fileName = fNode.getFileName();
String formattedFileInfo = fileName;
formattedFileInfo += "ENDN";
ulong fileSize = 0;
FileStream fStream = null;
try
{
fStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
FileInfo fInfo = new FileInfo(fileName);
fileSize = (ulong) fInfo.Length;
if (fileSize == 0)
continue;
formattedFileInfo += fileSize.ToString() + "ENDS";
}
catch (Exception e)
{
Console.WriteLine("Could not read from file:" + fileName);
deniedAccessFiles.Add(fileName);
continue;
}
byte[] fileInfoBytes = Encoding.ASCII.GetBytes(formattedFileInfo);
for (int i = 0; i < fileInfoBytes.Length; i++)
sentFileInfo[i] = fileInfoBytes[i];
while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_FILE_INFO"))
{
Console.WriteLine("Server waiting for file info ready message from client.");
fetchSocket.Receive(comMessage,0,100,SocketFlags.None);
comMessageStr = Encoding.ASCII.GetString(comMessage);
comMessageStr = comMessageStr.Substring(0,33);
Console.WriteLine("Received parsed message from client:" + comMessageStr);
}
Console.WriteLine("Server received file info ready message from client.");
comMessageStr = String.Empty;
Console.WriteLine("formattedFileInfo:" + formattedFileInfo);
Console.WriteLine("Sent file info:" + Encoding.ASCII.GetString(sentFileInfo));
fetchSocket.Send(sentFileInfo, 0, 2056, SocketFlags.None);
int readByte = 0;
ulong byteCount = 0;
ulong buffersSent = 0;
while (readByte != -1)
{
if (byteCount == 2048)
{
while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER"))
{
Console.WriteLine("Server waiting for ready for buffer message from client.");
fetchSocket.Receive(comMessage, 100, SocketFlags.None);
comMessageStr = Encoding.ASCII.GetString(comMessage);
comMessageStr = comMessageStr.Substring(0,38);
Console.WriteLine("Received parsed message from client 1:" + comMessageStr);
}
Console.WriteLine("Server received ready for buffer message from client.");
fetchSocket.Send(sentFileCont, 0, 2048, SocketFlags.None);
comMessageStr = String.Empty;
buffersSent++;
Console.WriteLine("Buffers sent:" + buffersSent);
byteCount = 0;
}
else
{
readByte = fStream.ReadByte();
if (readByte != -1)
{
sentFileCont[byteCount] = Convert.ToByte(readByte);
byteCount++;
}
}
}
while (!comMessageStr.Equals("CLIENT_READY_TO_RECEIVE_CONTENT_BUFFER"))
{
Console.WriteLine("Server waiting for ready for buffer message from client.");
fetchSocket.Receive(comMessage, 100, SocketFlags.None);
comMessageStr = Encoding.ASCII.GetString(comMessage);
comMessageStr = comMessageStr.Substring(0, 38);
Console.WriteLine("Received parsed message from client 2:" + comMessageStr);
}
Console.WriteLine("Server received ready for buffer message from client.");
fetchSocket.Send(sentFileCont, 0, 2048, SocketFlags.None);
buffersSent++;
Console.WriteLine("Buffers sent:" + buffersSent);
comMessageStr = String.Empty;
}
}
Console outputs:
My recommendation would be to utilise the TCPListener and TCPClient classes provided within System.Net.Sockets they really simplify sending messages over TCP/IP.
Server side you need something like this:
class MyTcpListener
{
public static void Listen()
{
TcpListener server = null;
byte[] bytes = new byte[256];
try
{
// Set the TcpListener on port 13000.
const int port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Connected!");
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
string data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine($"Received: {data}");
// Process the data sent by the client.
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Console.WriteLine($"Sent: {data}");
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Console.WriteLine($"SocketException: {e}");
}
finally
{
// Stop listening for new clients.
server?.Stop();
}
}
}
Obviously you'll want to adapt it to your program's structure. I took this from here but edited it slightly.
And on your client side you want to use something like this:
class MyTCPClient
{
static void Connect(String server, String message)
{
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
int port = 13000;
TcpClient client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array. Any encoding can be used as long as it's consistent with the server.
byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
NetworkStream stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine($"Sent: {message}");
// Receive the TcpServer.response. This is all optional and can be removed if you aren't recieving a response.
// Buffer to store the response bytes.
data = new byte[256];
// String to store the response ASCII representation.
// Read the first batch of the TcpServer response bytes.
int bytes = stream.Read(data, 0, data.Length);
string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {responseData}");
// Close everything.
stream?.Close();
client?.Close();
}
catch (ArgumentNullException e)
{
Console.WriteLine($"ArgumentNullException: {e}");
}
catch (SocketException e)
{
Console.WriteLine($"SocketException: {e}");
}
Console.WriteLine("\n Press Enter to continue...");
Console.Read();
}
}
}
Again, this needs to be adapted to your structure and I took it from here. The response stuff can all be removed if you aren't expecting a response from your server.
These two classes are extremely resilient and abstract away all of the complicated stuff, the size of the data buffers can also be changed to whatever size you need.

How to get length of data received in socket?

Pre:
I have client and server. I send some data from client (win forms) to the server (console). I send data using this on the client:
try {
sock = client.Client;
data = "Welcome message from client with proccess id " + currentProcessAsText;
sock.Send(Encoding.ASCII.GetBytes(data));
}
catch
{
// say there that
}
On server I receive data by this:
private void ServStart()
{
Socket ClientSock; // сокет для обмена данными.
string data;
byte[] cldata = new byte[1024]; // буфер данных
Listener = new TcpListener(LocalPort);
Listener.Start(); // начали слушать
Console.WriteLine("Waiting connections [" + Convert.ToString(LocalPort) + "]...");
for (int i = 0; i < 1000; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
}
private void Listeners()
{
Socket socketForClient = Listener.AcceptSocket();
string data;
byte[] cldata = new byte[1024]; // буфер данных
int i = 0;
if (socketForClient.Connected)
{
string remoteHost = socketForClient.RemoteEndPoint.ToString();
Console.WriteLine("Client:" + remoteHost + " now connected to server.");
while (true)
{
i = socketForClient.Receive(cldata);
if (i > 0)
{
data = "";
data = Encoding.ASCII.GetString(cldata).Trim();
if (data.Contains("exit"))
{
socketForClient.Close();
Console.WriteLine("Client:" + remoteHost + " is disconnected from the server.");
break;
}
else
{
Console.WriteLine("\n----------------------\n" + data + "\n----------------------\n");
}
}
}
}
}
Server start threads and starting listening socket on each.
Problem:
When client connects or sends message, server outputs message received + ~ 900 spaces (because buffer is 1024). How I can get received data length and allocate so much memory as needed for this data?
Per the MSDN article, the integer returned by Receive is the number of bytes received (this is the value you have assigned to i).
If you change your while loop to be like this then you will have the value you are looking for:
int bytesReceived = 0;
while (true)
{
i = socketForClient.Receive(cldata);
bytesReceived += i;
if (i > 0)
{
// same code as before
}
}

Socket Programming: Server/Clients and Thread Usage

static void Main(string[] args)
{
Console.Title = "Socket Server";
Console.WriteLine("Listening for client messages");
Socket serverSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPAddress serverIp = IPAddress.Any;
IPEndPoint serverEP = new IPEndPoint(serverIp, 8000);
SocketPermission socketPermission = new SocketPermission(NetworkAccess.Accept,
TransportType.Tcp,
"127.0.0.1", 8000);
serverSocket.Bind(serverEP);
serverSocket.Listen(2);
while(true)
{
//Socket connection = serverSocket.Accept();
connection = serverSocket.Accept();
Thread clientThread = new Thread(new ParameterizedThreadStart(MultiUser));
clientThread.Start(connection);
}
}
public static void MultiUser(object connection)
{
byte[] serverBuffer = new byte[10025];
string message = string.Empty;
int bytes = ((Socket)connection).Receive(serverBuffer, serverBuffer.Length, 0);
message += Encoding.ASCII.GetString(serverBuffer, 0, bytes);
Console.WriteLine(message);
TcpClient client = new TcpClient();
client.Client = ((Socket)connection);
IntPtr handle = client.Client.Handle;
}
I want to write a chat program which has one server and 2 clients. The problem is that, I can not direct the message sent from the client1 to client2 via the server. How can the server distinguish threads so that it can send the received message from client1 to client2?
Each client has their own handle. You can access this via the Handle property. For example:
TcpClient client = tcpListener.AcceptTcpClient();
IntPtr handle = client.Client.Handle; //returns a handle to the connection
Then all you need to do is store this in a hashtable, and iterate through it, looking for available data. When you detect data on the wire for one of the connections, then save it and retransmit it to the other clients in the table.
Remember to make sure that you make this multithreaded so a listen request on one client does not block any send or receive functions on other clients!
I've added some code here you should be able to work with (tested it out on my system)
private void HandleClients(object newClient)
{
//check to see if we are adding a new client, or just iterating through existing clients
if (newClient != null)
{
TcpClient newTcpClient = (TcpClient)newClient;
//add this client to our list
clientList.Add(newTcpClient.Client.Handle, newTcpClient);
Console.WriteLine("Adding handle: " + newTcpClient.Client.Handle); //for debugging
}
//iterate through existing clients to see if there is any data on the wire
foreach (TcpClient tc in clientList.Values)
{
if (tc.Available > 0)
{
int dataSize = tc.Available;
Console.WriteLine("Received data from: " + tc.Client.Handle); //for debugging
string text = GetNetworkString(tc.GetStream());
//and transmit it to everyone else
foreach (TcpClient otherClient in clientList.Values)
{
if (tc.Client.Handle != otherClient.Client.Handle)
{
Send(otherClient.GetStream(), text);
}
}
}
}
}
public void Send(NetworkStream ns, string data)
{
try
{
byte[] bdata = GetBytes(data, Encoding.ASCII);
ns.Write(bdata, 0, bdata.Length);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
protected string GetNetworkString(NetworkStream ns)
{
if (ns.CanRead)
{
string receivedString;
byte[] b = GetNetworkData(ns);
receivedString = System.Text.Encoding.UTF8.GetString(b);
log.Info("Received string: " + receivedString);
return receivedString;
}
else
return null;
}
protected byte[] GetNetworkData(NetworkStream ns)
{
if (ns.CanRead)
{
log.Debug("Data detected on wire...");
byte[] b;
byte[] myReadBuffer = new byte[1024];
MemoryStream ms = new MemoryStream();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = ns.Read(myReadBuffer, 0, myReadBuffer.Length);
ms.Write(myReadBuffer, 0, numberOfBytesRead);
}
while (ns.DataAvailable);
//and get the full message
b = new byte[(int)ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(b, 0, (int)ms.Length);
ms.Close();
return b;
}
else
return null;
}
You will want to call HandleClients from a main thread that checks to see if there are any pending requests or not, and runs on a loop.

Categories

Resources