My socket server is pretty simple so far:
public static void listen()
{
TcpListener server = null;
IPAddress address = IPAddress.Parse("127.0.0.1");
try
{
server = TcpListener.Create(5683);
server.Start();
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
while (true)
{
Thread.Sleep(10);
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Accepted Client");
Thread thread = new Thread (new ParameterizedThreadStart(SwordsServer.ClientHandler));
thread.IsBackground = true;
thread.Start(client);
}
}
public static void ClientHandler(object c)
{
TcpClient client = (TcpClient)c;
NetworkStream netstream = client.GetStream();
bool connected = true;
while (connected)
{
Thread.Sleep(10);
try
{
byte[] bytes = new byte[client.ReceiveBufferSize];
netstream.Read(bytes, 0, bytes.Length);
Console.WriteLine("got data");
netstream.Write(bytes, 0, bytes.Length);
}
catch (Exception e)
{
connected = false;
Console.WriteLine(e);
Console.WriteLine(e.StackTrace);
}
}
}
My question is, on a conceptual level, how would you keep tabs on each unique client and send updates from other threads to specific clients?
For example, if I have data for a specific client, how do I reach out to that client instead of broadcasting it? Or, if a client is no longer connected, how could I tell?
Thanks in advance for any assistance!
Your implementation for accepting multiple connections creates anonymous clients, meaning after more than 1 connection you wont be able to identify the right client. If identifying is the problem then you can do one thing, have the client send an identifier to the server like "Client1". Create a Dictionary and in your method ClientHandler(), read the identifier from client and add the TCPClient's object in the dictionary.
So the modified code would be something like this:
Dictionary<string, TCPClient> dictionary = new Dictionary<string, TCPClient>();
public static void ClientHandler(object c)
{
TcpClient client = (TcpClient)c;
NetworkStream netstream = client.GetStream();
bool connected = true;
while (connected)
{
Thread.Sleep(10);
try
{
byte[] bytes = new byte[client.ReceiveBufferSize];
//read the identifier from client
netstream.Read(bytes, 0, bytes.Length);
String id = System.Text.Encoding.UTF8.GetString(bytes);
//add the entry in the dictionary
dictionary.Add(id, client);
Console.WriteLine("got data");
netstream.Write(bytes, 0, bytes.Length);
}
catch (Exception e)
{
connected = false;
Console.WriteLine(e);
Console.WriteLine(e.StackTrace);
}
}
}
Do note: Your application should be intelligent enough to dynamically decide on to which client the updates should be sent.
Related
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.
I am new to programming and I am working on an asynchronous client server application.
I can send a message to the server from the client, but when I receive the data to the server (OnDataReceived Method) and try to send the same data back to the client (for testing purposes) I can't.
Not sure what other info I need to give, so please let me know, I don't mean to be vague.
SERVER CODE
public void OnDataReceived(IAsyncResult asyncResult)
{
try
{
SocketPacket socketData = (SocketPacket)asyncResult.AsyncState;
int iRx = 0;
iRx = socketData.currentSocket.EndReceive(asyncResult);
char[] chars = new char[iRx];
Decoder decoder = Encoding.UTF8.GetDecoder();
int charLen = decoder.GetChars(socketData.dataBuffer, 0, iRx, chars, 0);
String receivedData = new String(chars);
//BroadCast(receivedData);
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => lbxMessages.Items.Add(receivedData)));
//Updated Code
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => broadcast(receivedData)));
WaitForData(socketData.currentSocket);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\n OnDataRecieved: Socket has been closed\n");
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
public class SocketPacket
{
public Socket currentSocket;
public byte[] dataBuffer = new byte[50];//allowing the 50 digist to be sent at once
}
private void WaitForData(Socket socket)
{
try
{
if (workerCallBack == null)
{
workerCallBack = OnDataReceived;
}
SocketPacket sckPack = new SocketPacket();
sckPack.currentSocket = socket;
socket.BeginReceive(sckPack.dataBuffer, 0, sckPack.dataBuffer.Length, SocketFlags.None, workerCallBack, sckPack);
}
catch(SocketException se)
{
MessageBox.Show(se.Message);
}
}
Updated in response to Andrew's reply
I have a method that will be invoked when a client is connected
private void OnClientConnect(IAsyncResult asyncResult)
{
try
{
//Here we complete/end the Beginaccept() asynchronous call by
//calling EndAccept() - which returns the reference to a new socket object
workerSocket[clientCount] = listenSocket.EndAccept(asyncResult);
//Let the worker socket do the further processing for the just connected client
WaitForData(workerSocket[clientCount]);
//Now increment the client count
++clientCount;
if (clientCount<4)//allow max 3 clients
{
//Adds the connected client to the list
connectedClients.Add(listenSocket);
String str = String.Format("Client # {0} connected", clientCount);
this.Dispatcher.Invoke((Action)(() =>
{
//Display this client connection as a status message on the GUI
lbxMessages.Items.Add(str);
lblConnectionStatus.Content =clientCount + " Connected";
}));
//Since the main Socket is now free, it can go back and wait for
//other clients who are attempting to connect
listenSocket.BeginAccept(OnClientConnect, null);
}
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\n OnClientConnection: Socket has been closed\n");
}
catch (SocketException)
{
HandleClientDisconnect(listenSocket);
}
}
(UPDATED)
ADDED METHOD TO BROADCAST MESSAGE RECEIVED BY SERVER BACK TO CLIENT
public void broadcast(string msg)
{
//foreach (Socket item in connectedClients)
//{
Socket broadcastSocket;
broadcastSocket = workerSocket[0]; //sends message to first client connected
byte[] broadcastBytes = null;
broadcastBytes = Encoding.ASCII.GetBytes(msg);
broadcastSocket.Send(broadcastBytes);
//}
}
There is two sockets involved in server-side TCP communication. First socket is a listening socket and you should use it only for accepting new request. Then every time you accept new request from the client you're getting another socket for every connection.
You trying to send data back via listening socket but not via socket that you accepted.
Sergey has it right. If you're looking to have one server handle multiple clients, then you'll need some sort of ServerTerminal class which can listen for new connections and then setup some sort of "connectedclient" class to handle IO to that socket. Your "OnDataReceived" method would be in the connectedclient class.
In your socket accept routine it should look something like:
private void OnClientConnection(IAsyncResult asyn)
{
if (socketClosed)
{
return;
}
try
{
Socket clientSocket = listenSocket.EndAccept(asyn);
ConnectedClient connectedClient = new ConnectedClient(clientSocket, this, _ServerTerminalReceiveMode);
connectedClient.StartListening();
In the accept routine you're passed a socket - i've named this "clientSocket". This is the socket you want to write to, not the listening socket.
I want to do the following with a raw C# socket. I understand that usually the most appropriate way is via HTTP, with a HTTP client. The browser understands that this connection must be kept open in some way.
http://server.domain.com/protocol/do_something.txt
I am trying the following in C#, but have had no luck. What am I doing wrong? Is there a header missing? Should I be encoding what I'm sending to the server in some way? For the ReceiverSocket client, I'm using the following code, but it's just a very standard asynchronous socket client: https://stackoverflow.com/a/10390066/971580
ReceiverSocket socket = new ReceiverSocket("server.domain.com", 80);
socket.Connect();
System.Threading.Thread.Sleep(1000);
String message = "GET /protocol/do_something.txt HTTP/1.1";
message += "\r\n";
message += "\r\n";
socket.Send(message);
The socket can connect successfully, but I don't get any response when I send anything to the server. This is how I am connecting, sending and receiving.t (Apologies: I tried to do this in snippets, rather than including all the methods, but it looked horrid. . .)
public ReceiverSocket(String address, int port) : base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
messageQueue = new Queue<MessageBase>();
IPHostEntry ipHostInfo = Dns.GetHostEntry(address);
IPAddress ipAddress = ipHostInfo.AddressList[0];
remoteEP = new IPEndPoint(ipAddress, port);
}
public void Connect()
{
this.BeginConnect(remoteEP, ConnectCallback, this);
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
if (client.Connected)
{
client.EndConnect(ar);
Console.WriteLine("Connect Callback - Connected");
StateObject state = new StateObject();
state.workSocket = client;
state.BufferSize = 8192;
if (SocketConnected != null)
SocketConnected(client);
client.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0, ReceiveCallback, state);
}
else
{
Thread.Sleep(5000);
Connect();
}
}
catch (Exception ex)
{
Reconnect();
}
}
private void ReceiveCallback(IAsyncResult ar)
{
Console.WriteLine("Never gets here. . . ");
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
foreach (MessageBase msg in MessageBase.Receive(client, bytesRead, state))
{
// Add objects to the message queue
lock (this.messageQueue)
this.messageQueue.Enqueue(msg);
}
if (DataRecieved != null)
DataRecieved(client, null);
client.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0, ReceiveCallback, state);
}
else
{
Reconnect();
}
}
catch (SocketException)
{
Reconnect();
}
}
public void Send(String msg)
{
try
{
byte[] bytes = GetBytes(msg);
if (this.Connected)
{
Console.WriteLine("Sending: " + msg);
this.BeginSend(bytes, 0, bytes.Length, 0, SendCallback, this);
}
else
{
Reconnect();
}
}
catch (SocketException sox)
{
Reconnect();
}
catch (Exception ex)
{
int i = 0;
}
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
}
public class StateObject
{
public Socket workSocket = null;
public int readOffset = 0;
public StringBuilder sb = new StringBuilder();
private int bufferSize = 0;
public int BufferSize
{
set
{
this.bufferSize = value;
buffer = new byte[this.bufferSize];
}
get { return this.bufferSize; }
}
private byte[] buffer = null;
public byte[] Buffer
{
get { return this.buffer; }
}
}
Shouldn't the fact that I haven't included the message += "Connection: close" header mean that the socket should just start sending whatever data it has asynchronously? Just to note also: I can connect successfuly using Telnet and send the data, just not with a socket yet!
Any pointers at all would be appreciated.
Thanks.
Do NOT call Send() until ConnectCallback() is called first, otherwise you risk sending your data prematurely. Using Sleep() to wait for the connection is wrong.
Do NOT call BeginReceive() until after Send() has finished sending the data.
Because you are using HTTP 1.1, then yes, the connection is kept alive by default if you are connecting to an HTTP 1.1 server. The server's Connection response header will indicate whether the server is actually keeping the connection open or not.
Also, as stated by someone else, HTTP 1.1 requests MUST have a Host header or else the request is malformed and can be rejected/ignored by the server. HTTP 1.1 has a notion of virtual hosts running on the same IP, so the Host header tells the server which host the client wants to talk to.
I've read a couple of posts on SignalR and thought for a fun test project that I could create a web application to poll my onkyo receiver for status and display the results in a browser. For an initial test, I was successfully able to send the current time on the server back to the client by using this code in Application_Start:
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
while (true)
{
clients.addMessage(DateTime.Now.ToString());
Thread.Sleep(1000);
}
});
In the client javascript, i have the following code:
// Proxy created on the fly
var kHub = $.connection.kudzuHub;
// Declare a function on the hub so that the server can invoke it
kHub.addMessage = function (message) {
console.log('message added');
$('#messages').append('<li>' + message + '</li>');
};
// start the connection
$.connection.hub.start();
So all of that works fine. Every second, I get a new list item containing the current server date and time.
Now when I add this code to read data from the Onkyo receiver, it breaks: (still in Application_Start)
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
try
{
while (true)
{
string host = ConfigurationManager.AppSettings["receiverIP"].ToString();
int port = Convert.ToInt32(ConfigurationManager.AppSettings["receiverPort"]);
TcpClient tcpClient = new TcpClient(host, port);
NetworkStream clientSockStream = tcpClient.GetStream();
byte[] bytes = new byte[tcpClient.ReceiveBufferSize];
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
tcpClient.Close();
clients.addMessage(System.Text.Encoding.ASCII.GetString(bytes));
Thread.Sleep(50);
}
}
catch (SocketException ex)
{
// do something to handle the error
}
});
I set a break point and stepped through the code. It gets to this line and then returns.
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
It never finishes the rest of the code to send the message to the client. What am I doing wrong?
Thanks.
I would make some structural changes to your loop to allow the receiver time to respond, remove the overhead of retrieving the configuration every 50 milliseconds, and cleanup the open network stream:
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
TcpClient tcpClient = null;
NetworkStream clientSockStream = null;
try
{
string host = ConfigurationManager.AppSettings["receiverIP"].ToString();
int port = Convert.ToInt32(ConfigurationManager.AppSettings["receiverPort"]);
while (true)
{
if (tcpClient == null) {
tcpClient = new TcpClient(host, port);
clientSockStream = tcpClient.GetStream();
}
if (clientSockStream.CanRead) {
byte[] bytes = new byte[tcpClient.ReceiveBufferSize];
try {
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
} catch (Exception ex) {
// Add some debug code here to examine the exception that is thrown
}
tcpClient.Close();
// Closing the client does not automatically close the stream
clientSockStream.Close();
tcpClient = null;
clientSockStream = null;
clients.addMessage(System.Text.Encoding.ASCII.GetString(bytes));
}
Thread.Sleep(50);
}
}
catch (SocketException ex)
{
// do something to handle the error
} finally {
if (tcpClient != null) {
tcpClient.Close();
clientSockStream.Close();
}
}
});
I have make an application to communicate with an IP Camera. That is configured to make connection on a predefined address. And TCP Listener is running on that address and accepts connection from camera. When camera connects i send command to get MJpeg stream from camera and camera starts sending stream in response to command.
I am using asynchronous method to read stream from socket. But after sometime my application is stuck while reading data from network stream.
I am using this code to read Data from network stream and i have write some messages on the screen to get status of camera connection.
private void ReadData()
{
try
{
string msg = "Reading Data... client connected " + _camClient.Connected.ToString() +
"... netStream Readable " +
_netStream.CanRead.ToString();
Console.WriteLine(msg);
_callback = new AsyncCallback(GetData);
_buffer = new byte[Buffersize];
_async = _netStream.BeginRead(_buffer, 0, Buffersize, _callback, null);
}
catch (Exception ex) { Console.WriteLine("ReadData: " + ex.Message); }
}
private void GetData(IAsyncResult result)
{
try
{
int read = _netStream.EndRead(result);
if (read > 0)
{
_data = new byte[read];
Array.Copy(_buffer, 0, _data, 0, read);
ProcessData();
}
ReadData();
}
catch (Exception ex) { Console.WriteLine("GetData: " + ex.Message); }
}
Firstly asynchronous methods on network streams are well known for loosing data or for never returning data!
In your case it could be the ProcessData call is blocking.
What you should do is to spawn a new background thread with a blocking listener (non asynchronous) and then use a new thread to read the data while the listener continues to listen.
I have an example here.
private static Socket s_chatListener;
public static void Listen(IPAddress LocalIPAddress, int Port)
{
IPEndPoint ipend = new IPEndPoint(LocalIPAddress, Port);
s_chatListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s_chatListener.Bind(ipend);
s_chatListener.Listen(10);
while (true)
{
Socket handler = s_chatListener.Accept();
ParameterizedThreadStart pst = new ParameterizedThreadStart(loadMessageFromSocket);
Thread t = new Thread(pst);
t.Start(handler);
}
}
private static void loadMessageFromSocket(object socket)
{
Socket handler = (Socket)socket;
string data = "";
while (true)
{
byte[] butes = new byte[1024];
int rec = handler.Receive(butes);
data += ASCIIEncoding.ASCII.GetString(butes);
if (data.IndexOf("\0") > -1)
break;
}
handler.Shutdown(SocketShutdown.Both);
handler.Close();
Console.Write(data);
}
Now just call the Listen Method from a new Thread in your main form or caller class.
Hope this helps.