I currently have a client-server application that involves a Silverlight client and a .NET server. The .NET portion uses the Tcp classes provided in System.Net.Sockets namespace whereas the Silverlight client uses raw sockets. I'm porting into this from code that currently uses the HttpListener classes because it doesn't suit my needs. The Http classes, though, have on the SL side the ability to use Begin* and End* style asynchronous methods that allow me to specify a handler once the operation has completed. I'm having trouble getting this to work with the new system. My current strategy is to include the handler as part of the UserToken. However, it seems that this token is not getting updated.
Here is some redacted code. I am able to get the two sides to talk to each other, but it seems the correct UserToken is not being sent.
public class ClientUserToken
{
public Handler Handler { get; set; }
public string Test { get; set; }
public ClientUserToken(Handler handler, string test)
{
Handler = handler;
Test = test;
}
}
public class SocketClient
{
private Socket _clientSocket;
private string _ipAddress;
private int _port;
private void OpenSocket()
{
var endPoint = new DnsEndPoint(_ipAddress, _port);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(null, "foo");
args.RemoteEndPoint = endPoint;
args.Completed += OnSocketCompleted;
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.ConnectAsync(args);
}
void OnSocketCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect: ProcessConnect(e); break;
case SocketAsyncOperation.Receive: ProcessReceive(e); break;
case SocketAsyncOperation.Send: ProcessSend(e); break; // this never gets called
}
}
void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
byte[] response = new byte[1024];
e.SetBuffer(response, 0, response.Length);
_clientSocket.ReceiveAsync(e);
}
}
void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
var userToken = e.UserToken as ClientUserToken; // this token is always the one set in ProcessConnect
// process the data
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is never called, for some reason
void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var userToken = e.UserToken as ClientUserToken;
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is the public API that users use to actually send data across
public void SendToServer(byte[] data, int len, Handler handler)
{
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(handler, "bar");
args.SetBuffer(data, 0, len);
if (!_clientSocket.SendAsync(args))
{
ProcessReceive(args);
}
}
}
As the comments above suggest, in ProcessReceive, userToken.Test is always "foo" and userToken.Handler is always null.
I have so far not been able to break into ProcessSend so I can't see what SocketAsyncEventArgs it actually sent. Anyone have a clue why this event isn't firing (Completed after a Send)? Am I screwing up my SendToServer function?
I realize there may be other existing problems with synchronization and such, but I don't think that's the issue here. One thing I did try was setting up a ManualResetEvent to ensure that no one sends data to the server before the connection has been completed (ProcessConnect) but that did not solve the issue, either.
Any help will be appreciated!
EDIT: So the reason this is happening is because when I call ReceiveAsync in the ProcessConnect function, it is being used when the server is sending back the response for my data. Hence, UserToken "foo" is present in the handler. The next time the server sends data, the ReceiveAsync uses the args with the UserToken "bar". So it is kind of out of sync, for the duplex communication bit. I can't ensure that the SocketAsyncEventArgs that I sent from the client-side is the same one that is used on the response. It seems like the only solution is to have the SL client open two sockets--one for server-initiated data and the other for client-initiated requests. However, this means I'm not taking advantage of the duplex nature.
This model won't work because I'm creating a new SocketAsyncEventArgs on each send, which means that the data can come back on any of these args. I've been moving towards a model with a pool of SocketAsyncEventArgs and each client can only have one request/response at a time.
Related
So basically I want my server to raise an event (or a callback) when a connected client sends data. I can't come up with a solution to this problem and can't find anything online after days of searching.
What I've thought of was making an asynchronous foreach loop that looped through all the connected users, and check if there is any data to be read on each one (using TcpClient.Avaliable, but a network stream could also check this) but an infinite loop like this without any stop would be bad practice and use an insane amount of resources (from what I understand at least, I am new to threading and networking).
There is logic I need to be executed whenever the server gets data from a client (in this case a message, because it's a chat application), basically broadcast it to every other user, but I just can't find out how to detect if any user has sent data so that it raises an event to broadcast the message, log the message, etc...
Please be "soft" with the explanations as I am new to threading/networking and ty in advance.
As per request here is my code, take note that it is prototype-y and a bit unfinished, but I'm sure it gets the point across:
//Properties
public List<User> ConnectedUsers { get; private set; } = new List<User>();
public TcpListener listener { get; set; }
public bool IsListeningForConnections { get; set; }
public int DisconnectionCheckInterval { get; set; } //in seconds
//Events
public event EventHandler<ServerEventArgs> UserConnected;
public event EventHandler<ServerEventArgs> MessageReceived;
public NetworkManager()
{
listener = new TcpListener(IPAddress.Parse("192.168.1.86"), 6000); //binds // TODO: Change to: user input / prop file
DisconnectionCheckInterval = 10;
IsListeningForConnections = false;
}
public async void StartListeningForConnections()
{
IsListeningForConnections = true;
listener.Start();
while (IsListeningForConnections)
{
User newUser = new User();
newUser.TcpClient = await listener.AcceptTcpClientAsync();
OnUserConnected(newUser); // raises/triggers the event
}
}
public void StartListeningForDisconnections()
{
System.Timers.Timer disconnectionIntervalTimer = new System.Timers.Timer(DisconnectionCheckInterval * 1000);
//TODO: setup event
//disconnectionIntervalTimer.Elasped += ;
disconnectionIntervalTimer.AutoReset = true;
disconnectionIntervalTimer.Enabled = true;
//disconnectionIntervalTimer.Stop();
//disconnectionIntervalTimer.Dispose();
}
public async void StartListeningForData()
{
//??????????
}
public async void SendData(string data, TcpClient recipient)
{
try
{
byte[] buffer = Encoding.ASCII.GetBytes(data);
NetworkStream stream = recipient.GetStream();
await stream.WriteAsync(buffer, 0, buffer.Length); //await
Array.Clear(buffer, 0, buffer.Length);
}
catch { } //TODO: handle exception when message couldn't be sent (user disconnected)
}
public string ReceiveData(TcpClient sender)
{
try
{
NetworkStream stream = sender.GetStream();
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
return Encoding.ASCII.GetString(buffer).Trim('\0');
}
catch
{
return null; //TODO: handle exception when message couldn't be read (user disconnected)
}
}
protected virtual void OnUserConnected(User user)
{
ConnectedUsers.Add(user);
UserConnected?.Invoke(this, new ServerEventArgs() { User = user });
}
protected virtual void OnMessageReceived(User user, Message message) //needs trigger
{
MessageReceived?.Invoke(this, new ServerEventArgs() { User = user, Message = message });
}
basically a different class will call all the 3 classes that start with "StartListeningForX", then one of the 3 corresponding events are raised when one of the checks goes through (disconnection/connection/new message), and process that data, I just can't get my hands on how to call an event when a new message arrives for each user.
What I've thought of was making an asynchronous foreach loop that looped through all the connected users, and check if there is any data to be read on each one (using TcpClient.Avaliable, but a network stream could also check this) but an infinite loop like this without any stop would be bad practice and use an insane amount of resources
The standard practice is to have an "infinite" loop for each connected client, so that there is always a read going on every socket. I put "infinite" in quotes because it will actually eventually stop; either by reading 0 bytes (indicating end of stream) or by receiving an exception (indicating a broken connection).
I am new to threading/networking
It's funny how often I see developers trying to learn networking and threading at the same time. Let me be clear: threading and TCP/IP sockets are both extremely complicated and take quite a bit of time to learn all the sharp corners. Trying to learn both of these topics at once is insane. I strongly recommend choosing one of them to learn about (I'd recommend threading first), and only after that one is mastered, proceed to the other.
RabbitMQ
If you have access to the client side code, I'd consider using something like RabbitMQ, or a similar queue service. This allows to link the different apps together through a message broker or queue, and get messages/events real time.
There are functions you can call on event received.
I am learning to use the SocketAsyncEventArgs stuff using the MSDN tutorial. I was just wondering a few things, namely how I could go about implementing the server with full-duplex capabilities.
Currently, the MSDN example teaches you to create a server that first listens, then when something is received, to send it back. Only then does the server start listening again.
The problem I am having coming up with my own solution is that the SocketAsyncEventArgs object has only one event, Completed that is fired for both sends and receives. It has no other events.
I read on some horribly translated site that I
must use two SocketAsyncEventArgs, one receives a hair.
-unknown
I find there is a disturbingly small amount of infromation on this supposedly "enhanced" socket implementation...
Heres a little bit of my code so you can see what i'm up to.
//Called when a SocketAsyncEventArgs raises the completed event
private void ProcessReceive(SocketAsyncEventArgs e)
{
Token Token = (Token)e.UserToken;
if(e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
Interlocked.Add(ref TotalBytesRead, e.BytesTransferred);
Console.WriteLine(String.Format("Received from {0}", ((Token)e.UserToken).Socket.RemoteEndPoint.ToString()));
bool willRaiseEvent = ((Token)e.UserToken).Socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
// done echoing data back to the client
Token token = (Token)e.UserToken;
// read the next block of data send from the client
bool willRaiseEvent = token.Socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
void IOCompleted(object sender, SocketAsyncEventArgs e)
{
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
Thanks!
The solution was to create a separate SocketAsyncEventArgs for both Send and Receive.
It takes a bit more memory but not much because there is no need to allocate a buffer to the Send args.
I'm currently writing a component to communicate with an Ethernet based device and am having to use asynchronous sockets. At times when I receive specific 'commands' from the device, I need to raise an event for whatever program is using my component (most usually a WinForm.) I'm creating a sample form for the user but I am having difficulty allowing the client form to receive the events and modify the form; I'm getting the typical "Cross-thread operation not valid: Control 'listStrings' accessed from a thread other than the thread it was created on."
I've tried reading over Implementing the Event-based Asynchronous Pattern, and Walkthrough: Implementing a Component That Supports the Event-based Asynchronous Pattern, though it doesn't quite seem to be exactly what I need, especially when reading "Opportunities for Implementing the Event-based Asynchronous Pattern" in the first link.
.Net / C# is more of a hobby than profession, and in this project - this is the last piece I need to figure out before being able to complete it. Would it be better to use a "thread-safe" (I know, everyone throws that term around like it only means one thing) existing TCP/IP component rather than trying to implement it myself?
EDIT: Here's my network class code to show you how I'm implementing it now. I forget where I came across this snippet, but it's worked fine up until I've added the form.
internal class Network
{
private Device dev;
private TcpClient client;
private NetworkStream ns;
private byte[] buffer = new byte[2048];
private Queue<byte[]> _msgQ = new Queue<byte[]>();
public Network(Device d)
{
dev = d;
}
internal void Connect(string ipAddress, int port)
{
client = new TcpClient();
client.BeginConnect(ipAddress, port, new AsyncCallback(OnConnect), null);
}
internal byte[] getLocalIp()
{
return ((IPEndPoint)client.Client.LocalEndPoint).Address.GetAddressBytes();
}
private void OnConnect(IAsyncResult ar)
{
try
{
client.EndConnect(ar);
ns = new NetworkStream(client.Client);
ns.BeginRead(buffer, 0, 2048, new AsyncCallback(OnRead), null);
while (_msgQ.Count > 0)
{
byte[] message = _msgQ.Dequeue();
ns.Write(message, 0, message.Length);
}
dev.dvDevice._connected = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
internal void Disconnect()
{
try
{
client.Close();
dev.dvDevice._connected = false;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
internal void Write(byte[] message)
{
if ((!client.Connected) || ns == null)
{
_msgQ.Enqueue(message);
return;
}
ns.Write(message, 0, message.Length);
}
private void OnWrite(IAsyncResult ar)
{
try
{
ns.EndWrite(ar);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void OnRead(IAsyncResult ar)
{
try
{
int recv = ns.EndRead(ar);
byte[] message = new byte[recv];
Buffer.BlockCopy(buffer, 0, message, 0, recv);
dev.dvDevice._mh.Parse(message);
ns.BeginRead(buffer, 0, 2048, new AsyncCallback(OnRead), null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Device is the class which is exposed to the client. It contains a MessageHandler (_mh) class which does all the parsing. Device contains the public event which is called by the MessageHandler on specific responses. Hopefully this helps in what I have so far; I'd prefer not having to rewrite too much, but to make it right (and work properly), I will if I must.
EDIT (2):
My goal for this library is that the user should not at all have to manage any of the threads - so when an event is raised, say "ReceiveString", the user should just be able to act on it without any thought.
EDIT (3):
More code for completeness.
public delegate void OnStringEvent(byte[] str);
public class Device
{
internal struct _device
{
// other stuff too, but here's what's important
public bool _connected;
public bool _online;
public MessageHandler _mh;
public Network _net;
}
public event OnStringEvent OnString;
internal void ReceiveString(byte[] str)
{
OnString(str);
}
internal _device dvDevice;
public Device(int device_number, int system_number)
{
dvDevice = new _device(device_number, system_number);
dvDevice._mh = new MessageHandler(this);
dvDevice._net = new Network(this);
}
}
internal class MessageHandler
{
private Device dev;
public MessageHandler(Device d)
{
dev = d;
}
public void Parse(byte[] message)
{
// The code goes through the message and does what it needs to
// and determines what to do next - sometimes write back or something else
// Eventually if it receives a specific command, it will do this:
dev.ReceiveString(ParseMessage(ref _reader));
}
}
Do youself a favor and rely on TPL to do the synchronization lifting for you. Example:
NetworkStream stream = MySocket.NetworkStream;
// creat a Task<int> returning the number of bytes read based on the Async patterned Begin- and EndRead methods of the Stream
Task<int> task = Task<int>.Factory.FromAsync(
fs.BeginRead, fs.EndRead, data, 0, data.Length, null);
// Add the continuation, which returns a Task<string>.
return task.ContinueWith((task) =>
{
if (task.IsFaulted)
{
ExceptionTextBox.Text = task.Exception.Message;
}
else
{
ResultTextBox.Text = string.Format("Read {0} bytes into data", task.Result);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
I like #Polity's answer, being an Rx fan I would say use Rx (Reactive Extensions)
//we convert a typical begin/end (IAsyncPattern) into an observable sequence
//it returns a Func -read() that takes a byte, two ints and returns one.
var read = Observable.FromAsyncPattern<byte[], int, int, int>
(networkStream.BeginRead, networkStream.EndRead)
.ObserveOn(Scheduler.Dispatcher);
// Now, you can get an IObservable instead of an IAsyncResult when calling it.
byte[] someBytes = new byte[10];
IObservable<int> observable = read(someBytes, 0, 10);
observable.Subscribe(x=>
//x will be the returned int. You can touch UI from here.
);
Based on your code I can see that another thread calls the OnString event, then I assume when you subcribe to it, you're just adding the string into the listbox.
device.OnString += new OnStringEvent(device_onstring);
void device_onstring(byte[] str)
{
listStrings.Items.Add(...);//this is wrong, will give cross thread op error.
//you do this:
this.Invoke(new MethodInvoker(delegate()
{
listStrings.Items.Add(..);
//or anything else that touches UI
});
// this should refer to a form or control.
}
You can handle this in 2 places depending on your design. If the event is raised from a different thread you can handle it in the event handler by checking the .invokeReqeuired property of the form( or other control) handling the event. If it returns true you should use the .beginInvoke method to marshal the call to the proper thread.
Depending on your design you can handle it from the other end by passing your component an instance of the form you want to marshal to. Before you raise the event, check .invokeRequired and marshal the call so that the event is raised in the proper thread. This way the code using your library doesn't have to worry about threads, but it required that your library have a reference to system.windows.forms.
This should be a pretty easy problem to solve: you just need to execute any code in your form that updates controls using Invoke.
The precise implementation will depend on how the async code is calling back into your form. If you add that code to your question, we can provide a more complete answer.
I've developing (or trying to, anyway) a program that uses Asynchronous Socket to, supposedly, pass strings to and fro the server and client, at any time.
This program requires no more than one client be connected to a server. I tried Socket Programming, but I found out it blocks the program until either one receives something.
Since I have only a basic understanding of Asynchronous socket programming, I just went for the simplest one I could find, or at least, the simplest one I could understand.
Here's my code for the Server:
public Socket g_server_conn;
public byte[] g_bmsg;
public bool check = false;
private void net_As_Accept(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
g_server_conn = server_conn.EndAccept(iar);
g_bmsg = new byte[1024];
check = true;
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_As_Send(IAsyncResult iar)
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
try
{
Socket server_conn = (Socket)iar.AsyncState;
server_conn.EndReceive(iar);
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
public void net_Data_Send(string msg2snd) // Function for sending through socket
{
MessageBox.Show(msg2snd);
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_server_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_server_conn);
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do something
}
}
And here's my code for the Client:
private void net_As_Connect(IAsyncResult iar)
{
try
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndConnect(iar);
g_bmsg = new byte[1024];
check = true;
string toSendData = "&" + net_Name;
net_Data_Send(toSendData);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "GG");
}
}
private void net_As_Send(IAsyncResult iar)
{
Socket client_conn = (Socket)iar.AsyncState;
client_conn.EndSend(iar);
}
private void net_As_Receive(IAsyncResult iar)
{
if (g_bmsg.Length != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
check = false;
}
}
public void net_Data_Send(string msg2snd)
{
byte[] byData = System.Text.Encoding.ASCII.GetBytes(msg2snd);
g_client_conn.BeginSend(byData, 0, byData.Length, SocketFlags.None, new AsyncCallback(net_As_Send), g_client_conn);
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
private void net_Data_Receive(string txt)
{
if (lblBuffer.InvokeRequired)
lblBuffer.Invoke(new MethodInvoker(delegate { net_Data_Receive(txt); }));
else
lblBuffer.Text = txt;
if (txt.StartsWith("&"))
{
// Do Something
}
else if (txt.StartsWith("$"))
{
// Do something Else
}
}
Now, the Client can connect to the Server fine. The Client can even send in a string containing the user's name to the Server, which will then be displayed on the Server. The Server then sends out the name of its user to the Client, which the client receives and displays. Whatever is sent is stored in a Label (lblBuffer)
But afterwards, say I have the following code:
private void btnSendData_Click(object sender, EventArgs e)
{
string posMov = "Stuff to send";
net_Data_Send(posMov);
}
The Client receives nothing. Putting a Message Box in net_Data_Send(msg2snd) function reveals that the server does in fact send out the message. In fact, putting in the Message Box in that function makes it work (the Client receives it), for reasons I don't know. Since I haven't tried sending a message from the Client to the Server (other than the name when the Client Connects), I assume the Client will have the same problem sending to the Server.
Also, when it does send the second message (by putting a Message Box in the net_Data_Send function), only parts of the Label (lblBuffer) are overwritten. So if I my name is "Anon E. Moose", and the Server sends that when the Client connects, and I try to send out, say, "0.0" (via button press) the Label on the Client would then read "0.0n E. Moose".
What did I do wrong? Can I have some help on this, please?
Perhaps I have a problem with net_Data_Receive and net_Data_Send?
I think you need to call BeginReceive on your client again, it looks like you are only calling it once, so after it has received the server name, it isn't listening for any more data from the server
private void net_As_Receive(IAsyncResult iar)
{
var bytesRead = g_client_conn.EndReceive(iar);
if (bytesRead != 0)
{
net_Data_Receive(Encoding.ASCII.GetString(g_bmsg, 0, bytesRead));
check = false;
}
g_client_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_client_conn);
}
also, as I mentioned in my comment, use the bytesRead value to work out how much of the buffer you need to use.
You will need to work out if the data you have received from the socket is the full amount, or if you need to read more data to make up the current message from the other side.
BeginReceive doesn't just call its callback whenever a new packet (string in your case arrives). In fact. BeginReceive or any raw socket method works in a stream based fasion, not packet based. See http://msdn.microsoft.com/en-us/library/bew39x2a.aspx for an example.
What you need to do, is in your 'net_As_Receive' callback method (naming is terrible imo), you need to make a call first to socket.EndRecieve(IAsyncResult), which in turn returns the total bytes currently available. After that, you have to make a decision whether to receive more data or not.
For example:
private StringBuilder packetBuilder;
{
if (packetBuilder == null)
packetBuilder = new StringBuilder();
// finalyze the receive
int length = g_server_conn.EndReceive(iar);
if (length != 0)
{
// get the total bytes received. Note that the length property is of that of the number of bytes received rather than that of the buffer
packetBuilder.Append(Encoding.ASCII.GetString(g_bmsg, 0, length));
net_Data_Receive(packetBuilder.ToString());
check = false;
}
// receive the next part
g_server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(net_As_Receive), g_server_conn);
}
Note that this example doesnt care about packages. It will work if your lucky but there is a good change either a part of a string will be shown or 2 different strings will be combined. A good implementation will look for a string end and only show that part while buffering the rest untill a new string end is found. You can also use a StreamReader for making your life much easier
I have around 5000 modem (thin clients), and I want to communicate with them, one of a my method is like this : string GetModemData(modemID), now I have an open port in server that listens to modem and I'm using socket programming to send data to modems (calling related function), but when i want send data to multiple modem in a same time and get response from them, I don't know what should i do? I can send data to one modem and waiting for its response and then send another data to other modems (sequential), but the problem is client should be wait long time to get answer(may be some different client want to get some information from modems so they all will be wait into the Q or something like this), I think one way to solving this problem is to use multiple port and listen for each modem to related port, but it takes too many ports and also may be memory usage going up and exceed my available memory space, so some lost may be occurred (is this true?). what should to do ? I'd thinking about Parallelism, but i think its not related i should to wait for one port, because i don't know should to pass current received data to which client. I'm using asp.net.
currently I'm doing like this:
private void StartListener()
{
ModemTcpListener = new TcpListener(ModemPort);
//ClientTcpListener = new TcpListener(ClientPort);
ModemTcpListener.Start();
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
}
and in return
private void DoReadModemCallback(IAsyncResult ar)
{
try
{
bool bRet = ar.AsyncWaitHandle.WaitOne(420000);
Modem modem = ar.AsyncState as Modem;
if (!bRet || modem == null)
{
return;
}
}
catch{}
// now send data to which client?????? if i'm going to use async????
}
and :
private void DoAcceptModemCallback(IAsyncResult ar)
{
try
{
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
TcpClient tcpClient = ModemTcpListener.EndAcceptTcpClient(ar);
Modem modem= new Modem(tcpClient, "");
tcpClient.GetStream().BeginRead(modem.Buffer, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(DoReadModemCallback), modem);
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
Log.Write("a Modem connect ...");
}
catch (Exception ex)
{
}
}
Heres an example keeping track of all your clients. I've compacted it for readability. You should really split it up into multiple classes.
I'm using Pool (which I just created and commited) and SimpleServer. Both classes are part of a library that I'm currently building (but far from done).
Don't be afraid of having 5000 sockets open, they do not consume much resources when you are using asynchronous operations.
public class SuperServer
{
private List<ClientContext> _clients = new List<ClientContext>();
private SimpleServer _server;
private Pool<byte[]> _bufferPool;
public SuperServer()
{
// Create a buffer pool to be able to reuse buffers
// since your clients will most likely connect and disconnect
// often.
//
// The pool takes a anonymous function which should return a new buffer.
_bufferPool = new Pool<byte[]>(() => new byte[65535]);
}
public void Start(IPEndPoint listenAddress)
{
_server = new SimpleServer(listenAddress, OnAcceptedSocket);
// Allow five connections to be queued (to be accepted)
_server.Start(5);
}
// you should handle exceptions for the BeginSend
// and remove the client accordingly.
public void SendToAll(byte[] info)
{
lock (_clients)
{
foreach (var client in _clients)
client.Socket.BeginSend(info, 0, info.Length, SocketFlags.None, null, null);
}
}
// Server have accepted a new client.
private void OnAcceptedSocket(Socket socket)
{
var context = new ClientContext();
context.Inbuffer = _bufferPool.Dequeue();
context.Socket = socket;
lock (_clients)
_clients.Add(context);
// this method will eat very few resources and
// there should be no problem having 5000 waiting sockets.
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
//Woho! You have received data from one of the clients.
private void OnRead(IAsyncResult ar)
{
var context = (ClientContext) ar.AsyncState;
try
{
var bytesRead = context.Socket.EndReceive(ar);
if (bytesRead == 0)
{
HandleClientDisconnection(context);
return;
}
// process context.Inbuffer here.
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
return;
}
// use a new try/catch to make sure that we start
// read again event if processing of last bytes failed.
try
{
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
}
}
// A client have disconnected.
private void HandleClientDisconnection(ClientContext context)
{
_bufferPool.Enqueue(context.Inbuffer);
try
{
context.Socket.Close();
lock (_clients)
_clients.Remove(context);
}
catch(Exception err)
{
//log exception
}
}
// One of your modems
// add your own state info.
private class ClientContext
{
public byte[] Inbuffer;
public Socket Socket;
}
}
Used classes:
Pool: http://fadd.codeplex.com/SourceControl/changeset/view/58858#1054902
SimpleServer: http://fadd.codeplex.com/SourceControl/changeset/view/58859#1054893
You need to use the asynchronous tcp/ip methods. This article shows how:
http://www.codeproject.com/KB/IP/asyncsockets.aspx
The critical piece is the BeginReceive() and related callback functions. Any more q's, please leave comments to this answer ;) BEST OF LUCK!
You need multi threading, whenever a client establishes a connection to the server start a new thread for it and start communication send/receive.
Here are some articles explaining multithreading in c#,
c-sharpcorner
codeproject
And here's a sample server application with multithreading,
http://www.dotnetspider.com/resources/2829-A-multi-readed-server-C-which-finds-prime-num.aspx