When i send data through TCP server to client first time full name retrieved when i again send the another name like book it retrieved as (ook) in C sharp
client side code
sck = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
while (true)
{
IPEndPoint localEndPoint;
localEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.2"), 1);
try
{
sck.Connect(localEndPoint);
}
catch
{
Console.Write("Unable to connect to remote end point!\r\n");
);
}
Console.Write("Enter Text: ");
string text = Console.ReadLine();
byte[] data = Encoding.ASCII.GetBytes(text);
sck.Send(data);
Console.Write("Data Sent!\r\n");
sck.Close();
Console.ReadLine();
Server Side Code
while (true)
{
Socket accepted = sck.Accept();
Buffer = new byte[accepted.SendBufferSize];
int bytesRead = accepted.Receive(Buffer);
byte[] formatted = new byte[bytesRead];
for (int i = 0; i < bytesRead; i++)
{
formatted[i] = Buffer[i];
//Console.WriteLine(Buffer[i] + "\r\n");
}
//string strData = Encoding.ASCII.GetString(Buffer);
string strData = Encoding.ASCII.GetString(formatted);
Console.Write(strData + "\r\n");
accepted.Close();
}
There is something which I don't understand, you use Buffer as a variable but that is a type in C#, I don't know where and how you declare it.
Anyway, I would do it like this:
while (true)
{
Socket accepted = sck.Accept();
byte[] buffer = new byte[1024];
int ret=0;
StringBuilder sb= new StringBuilder();
while ((ret=accepted.Receive(buffer)) > 0)
{
sb.Append(Encoding.ASCII.GetString(buffer, 0, ret));
}
Console.Write(sb.ToString() + "\r\n");
accepted.Close();
}
In order to keep a dialog between both sockets you need something called a protocol, which means you know when you have received the whole data then you reply to the sender. A protocol could be: send a packet which inform what you are going to send, length of the message mainly. Other way is to stop reading when something comes into the message, for example the string "#END#" the return character '\r', etc. If you only want to send one single message and received back a reply message one thing you can do, however, is to shutdown the client part, only the send part Socket.Shutdown(SocketShutdown.Send);, with that, the sender tells the receiver it has sent all data, the receiver receives that signal and then it can send a reply message to the sender.
The client:
sck.Send(data);
Console.Write("Data Sent!\r\n");
// tell the server we have done sending data.
sck.Shutdown(SocketShutdown.Send);
// Now receive from the server
byte[] buffer = new byte[1024];
int ret = 0;
StringBuilder sb = new StringBuilder();
while ((ret = sck.Receive(buffer)) > 0)
{
sb.Append(Encoding.ASCII.GetString(buffer, 0, ret));
}
Console.Write(sb.ToString() + "\r\n");
sck.Close();
The server
while (true)
{
Socket accepted = sck.Accept();
byte[] buffer = new byte[1024];
int ret=0;
StringBuilder sb= new StringBuilder();
while ((ret=accepted.Receive(buffer)) > 0)
{
sb.Append(Encoding.ASCII.GetString(buffer, 0, ret));
}
Console.Write(sb.ToString() + "\r\n");
byte[] reply = Encoding.ASCII.GetBytes("I've got it!");
accepted.Send(reply);
accepted.Close();
}
Related
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
I have a TCP Client
Log.Warn("Trying to connect to " + IP);
TcpClient client = new TcpClient(IP, Port);
string command = "";
while (!command.Contains("quit"))
{
Log.WriteSingle("localhost#", ConsoleColor.DarkYellow);
Log.WriteSingle(IP + ":", ConsoleColor.Yellow);
command = Console.ReadLine();
byte[] data = Encoding.ASCII.GetBytes(command);
NetworkStream stream = client.GetStream();
stream.Write(data, 0, data.Length);
Log.Success("Sent command to network.");
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Log.Write("Server Says: " + responseData, ConsoleColor.DarkYellow);
}
and a TCP Server
while(true)
{
Log.Write("Waiting for connection...");
TcpClient client = server.AcceptTcpClient();
Log.Success("Connected! ");
//Update list (Currently useless)
clientList.Add(client);
Thread t = new Thread(new ThreadStart(()=>ConnectClient(client,bytes,data)));
t.Start();
}
public static void ConnectClient(TcpClient _client, byte[] _bytes, string _data)
{
_data = null;
NetworkStream stream = _client.GetStream();
int i;
while((i = stream.Read(_bytes,0,_bytes.Length))!=0)
{
_data = System.Text.Encoding.ASCII.GetString(_bytes,0,i);
Log.Write("Recieved: "+_data, ConsoleColor.Cyan);
//Send back to client
_data = _data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(_data);
stream.Write(msg,0,msg.Length);
Log.Write("Sent: "+_data);
}
_client.Close();
}
I have it setup so the server listens to client connections and pop them off in a new thread once they connect. The client can send the server a string, and the server reflects it back.
I assume I can use a dictionary to assign an ID and store the client, or even just a simple List.
How would I structure it so I can add them to a List or a Dictionary and still be able to connect multiple clients?
Thanks
Put your accept() loop in a thread of its own. It can add to the list each time a new client connects.
Leave your main thread to manage all the client threads, including the accept() thread.
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.
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
}
}
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.