How to stop my program from freezing up with TCP connection C# - c#

I have created a TCP client and it connects fine but am a bit confused how to receive messages from the server without closing the connection ?
My current approach was to run a co-routine over the network stream read method but that freezes my program up so its obviously the wrong approach so am not sure how to fix it.
I want to keep the connection alive and read messages when ever they may arrive from the server.
This is what i have setup currently:
// the idea is to run a coroutine for recieving messages
private IEnumerator<float> _RunTCPSocket()
{
int timer = DateTime.Now.Second;
byte[] readBuffer = new byte[1024];
while (SocketManager.IsConnected)
{
// this is the keep alive packets to server to prevent timeout on server side
if (DateTime.Now.Second - timer > KeepAliveRate)
{
Debug.Log("Sending");
timer = DateTime.Now.Second;
SocketManager.Send(null);
}
int msgLength = SocketManager.Recieve(readBuffer);
if (msgLength > 0)
Debug.Log(Encoding.ASCII.GetString(readBuffer, 0, msgLength));
yield return Timing.WaitForOneFrame;
}
}
This is the code for the receive method:
public int Recieve(byte[] readBuffer)
{
if (!IsConnected)
return -1; //-1 signifies an error aka we are disconnected
try
{
// NetworkStream is from TcpClient.GetStream()
bytesRead = _networkStream.Read(readBuffer, 0, readBuffer.Length);
}
catch (Exception e)
{
IsConnected = false;
Debug.Log(e);
bytesRead = -1;
}
return bytesRead;
}
How do i prevent this from locking up my program ?

You can use Begin/End method to make your program responsible:
Document from microsoft
You can see that the using of BeginReceive method is so complex so personally, i don't think it's easy to use.
An alternative is to call the read/write method inside a Task.
The third option is use TcpClient which used on client side and TcpListener which used on server side. Those two class is just a wrapper for an underline TCP socket. Those wrapper can make your life much more easier with Stream and Async methods.
If you want to learn more about network programming with C#, i highly recomment this book: C# Network Programming by Richard Blum
Update
Code for working with Task:
public event EventHandler<ReceiveDataEventArgs> DataReceived = null;
public void StartReceive()
{
Task.Run(() =>
{
while (true)
{
var bytesRead = _networkStream.Read(readBuffer, 0, readBuffer.Length);
DataReceived?.Invoke(this, new ReceiveDataEventArgs
{
Data = bytesRead
});
}
});
}
public class ReceiveDataEventArgs : EventArgs
{
public byte[] Data { get; set; }
}

Related

Mutex implementation within IIS async controller action?

I have written a web service that's an Asp.Net MVC application hosted in IIS. The data from the web service isn't retrieved from a database but from another server that is accessed via TCP. We'll call this the Data Server. There can be multiple Data Servers that the web service connects to.
For the sake of discussion, a user authenticates by specifying a username and password then a "Session" with a Socket is created with the appropriate Data Server. Assuming everything is good - we keep the Socket alive and pass the user a token that identifies the Session they belong to.
For each Socket I need to prevent traffic from interrupting each other. I assume that the best way to do that is to run the network traffic on a Socket in a serialized manner. I have achieved this by using a lock. My code to execute a query on this Data Server follows. The problem I'm having is that once in a while it appears that I'm getting two queries that collide and one may hang. I see a query come in but it looks like it gets stuck at the lock. Is the lock mechanism safe for IIS async calls? Is there instrumentation I can put in to make sure this is actually the bug? I've been looking for a while but I can't find guidance for this particular scenario.
private async Task<string> query(string request, AbstractPermission context = null, bool bUpdateDateTime = true)
{
try
{
string reply = "";
isErrorMsg = false;
//this lock prevents the keep alive thread from coming in here while we're on the socket
lock (socketLock)
{
sendDone.Reset();
receiveDone.Reset();
// Send test data to the remote device.
Send(Socket, request);
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(Socket);
receiveDone.WaitOne();
reply = QueryResponse;
} //done reading - let's unlock
if (bUpdateDateTime)
{
this.LastUsed = DateTime.Now;
}
return QueryResponse;
}
catch (Exception e)
{
throw e;
}
}
private void Send(Socket client, String data)
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
state.PostInitialRead = false;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
LogError(e);
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
bool PostInitialRead = state.PostInitialRead;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
//
//
var thisBatch = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
var endIdx = thisBatch.IndexOf('\x04');
if (!PostInitialRead)
{
if (bytesRead == 0)
throw new ApplicationException("Timeout waiting for response");
if (endIdx != -1)
{
thisBatch = thisBatch.Substring(0, endIdx);
}
if (state.buffer[0] != 0)
{
thisBatch = thisBatch.Substring(1, state.buffer[0]);
isErrorMsg = true;
}
else if (state.buffer[1] != 0)
{
thisBatch = thisBatch.Substring(2);
isErrorMsg = true;
}
else
{
thisBatch = thisBatch.Substring(2);
}
state.sb.Append(thisBatch);
state.PostInitialRead = true;
}
else
{
state.ms.Write(state.buffer, 0, endIdx!=-1?endIdx:bytesRead);
}
if (endIdx != -1)
{
// Got everything
state.sb.Append(Encoding.ASCII.GetString(state.ms.ToArray()));
QueryResponse = state.sb.ToString();
receiveDone.Set();
return;
}
else
{
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
1: Is hosting something like this within IIS completely absurd?
No. See, dotnetcore and modern development uses SignalR based on WebSockets for stuff like that - and it is hosted in IIS, so it is NOT completely absurd.
2: We can not really answer that. The IO part is trivial - IIS can handle this. But without details on the data server - no idea. YOu generally want to avoid locks as much as possible, but that does not mean totally. MRSV (multiple Reader, Single Writer), copy on write etc. can help minimizing writes, but at the end you will need SOME locking. It is NOT fun debugging this, but that is what people doing that get paid big bucks for.
General applicatiosn avoid all that by offloading the locking to the database at the backend - which spends a LOT of time by many people optimizing locking on data. If you can not do that (remember, we do not know at all what your data server does internally) - welcome to the hard programming.
Your best chance for debugging is trying to find a repro case and then - while it is stuck - attach a debugger. Stuff like that is NOTORIOUSLY hard to debug - but again, this is like the topmost level of regular programming (leaving out certain hardware and very special tasks).

Implementing an FTP Client in C# using TCP

I've been messing around with TCP sockets in C#, and I'm having some trouble communicating with an FTP server I have set up. I can connect initially and get the 220 message, but when I send the "USER nrcrast" command, I never get a response, and the DataAvailable property returns false. Anybody know what I'm doing wrong? Here's my code so far:
namespace TCPClient
{
public partial class TCPClientForm : Form
{
private TcpClient myClient;
NetworkStream stream;
public TCPClientForm()
{
InitializeComponent();
send();
}
void send()
{
while (true)
{
try
{
myClient = new TcpClient("nrcrast.dyndns.info", 21);
break;
}
catch (SocketException ex)
{
Console.WriteLine(ex.ToString());
}
}
stream = myClient.GetStream();
int sendOffset = 0;
int recOffset=0;
int dataLength;
Byte[] receiveData = new Byte[256];
// wait for a response
dataLength = stream.Read(receiveData, recOffset, receiveData.Length);
String recvdMessage = System.Text.Encoding.ASCII.GetString(receiveData, 0, dataLength);
Console.WriteLine(recvdMessage.ToString());
recOffset+=dataLength;
String message = "USER nrcrast";
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
stream.Write(data, 0, data.Length);
sendOffset += data.Length;
// wait for a response
while (!stream.DataAvailable)
{
}
dataLength = stream.Read(receiveData, 0, receiveData.Length);
recvdMessage = System.Text.Encoding.ASCII.GetString(receiveData, 0, dataLength);
Console.WriteLine(recvdMessage.ToString());
}
}
A shoot in the dark, you need to put a carriage return and new line at the end of the command
String message = "USER nrcrast\r\n";
If you're interested in looking over someone's shoulder on a similar project (not saying its perfect), I did the same thing a long time ago (this was back in .net 1.1, and ported it to .net 2.0 when the ssl stream stuff was added).
there are some tricky pieces to the FTP protocols with respect to timings of when you send commands, when the server expects you to open the data connection, when you read the server response, and so forth (depending on active / passive modes).
anyway, feel free to look over My FTP Client Library source code for reference as you do your own implementation. it's a pretty complete implementation and does auth ssl/tls as well.

Threading a Synchronous XNA Network

I have been doing several hours of research on a topic that I thought would've been very trivial. So far I've come up empty handed and wanted to see what you guys think. I'm currently messing with XNA (which is actually quite irrelevant now that I think about it) building a client/server architecture for a "game" project. Really nothing more than playing around with networking, animations, etc.
Anyway I've read quite a bit about the differences between Synchronous and Asynchronous networks and the viability of threading synchronous applications to simulate asynchronous behavior and have decided to do just that. Now I know my code isn't pretty but I'm just testing right now. Here's how it's (sort of) set up:
Game is run in the main thread.
On initialization->Connect to server.
Send x position of player class's sprite object to server.
Server receives, acknowledges with a print to console and sends the same data back.
Data is read into a logfile.
I've begun work on a messenger class that will eventually read (or sniff) the packets coming from the server and dispatch them accordingly making draw/update calls as needed. My problem is that I cant figure out how to properly thread the connection method to have that run (and then block?) separate from the Send/Receive loop (or what I'd like to be a continuous loop).
Like I said I'm no expert, I'm just doing this for fun so I may be all over the place. Anyway here are the essentials code-wise:
Networking.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
namespace DrawTest
{
class Networking
{
public void StartClient(Messenger m)
{
// Data buffer for incoming data.
StreamWriter _con = new StreamWriter("data.txt");
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
IPHostEntry ipHostInfo = Dns.Resolve("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,3000);
// Create a TCP/IP socket.
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
sender.Connect(remoteEP);
_con.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
while (m.isAlive)
{
m.SocketStream(sender, _con);
}
if (Messenger.mPacket() == "close_socket")
{
_con.WriteLine("Connection closed by client.");
sender.Shutdown(SocketShutdown.Both);
sender.Close();
}
} catch (ArgumentNullException ane) {
_con.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
_con.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
_con.WriteLine("Unexpected exception : {0}", e.ToString());
}
_con.Flush();
}
catch (Exception e) {
_con.WriteLine(e.ToString());
_con.Flush();
}
}
}
}
Messenger.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace DrawTest
{
public class Messenger
{
byte[] bytes = new byte[1024];
byte[] incBuffer = new byte[1024];
public bool isAlive = true;
Vector2 position = new Vector2(0.0f, 0.0f);
public Vector2 getPos()
{
return position;
}
public void setPos(Vector2 pos)
{
position = pos;
}
public void SocketStream(Socket s, StreamWriter logfile)
{
byte[] msg = null;
int bytesSent = 0;
int bytesRec = 0;
msg = BitConverter.GetBytes(position.X);
// Encode the data string into a byte array.
bytesSent = s.Send(msg);
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
//logfile.WriteLine(Messenger.mDecoder(incBuffer, bytesRec));
}
public string mDecoder(byte[] msg, int size)
{
string DecodedMessage;
byte[] bytes = new byte[1024];
DecodedMessage = Encoding.ASCII.GetString(msg, 0, size);
if (DecodedMessage == "close_socket")
{
isAlive = false;
return DecodedMessage;
}
return DecodedMessage;
}
public static string mPacket()
{
return null;
}
}
}
Think that should do it. The other code is relatively self-explanatory (abstract player/sprite classes and the typical XNA Game.cs)
Thanks in advance for any help!
You may do something along the lines of:
public void SendData(Socket s)
{
byte[] msg = null;
int bytesSent = 0;
msg = BitConverter.GetBytes(position.X);
// Encode the data string into a byte array.
bytesSent = s.Send(msg);
}
void ReceiveData(Socket s)
{
int bytesExpected = 1024; // somehow specify the number of bytes expected
int totalBytesRec = 0; // adds up all the bytes received
int bytesRec = -1; // zero means that you're done receiving
while(bytesRec != 0 && totalBytesRec < bytesExpected )
{
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
totalBytesRec += bytesRec;
}
}
Back in your StartClient class, you should start your receive thread first then send the data:
// Start your receive thread first
Thread t = new Thread(()=>{ReceiveData(sender);});
t.IsBackground = true;
t.Start();
// Then send the data
SendData(sender);
// Wait for the thread to terminate (if you need to)
t.Join(30000);
// Once you close the socket, then it will throw an exception
// in the receive thread (which you should catch) and you can
// exit the thread, thus terminating the thread.
This is roughly how you would start a thread that performs the receive.
Update (based on comments)
I would recommend that you take a look at some of the Patterns for Multithreaded Network Server in C#.
The server side should start a new thread for every client connection accepted and a "connection handler" should take over and manage the sending/receiving of data from there on:
while(serverRunning)
{
Socket clientSocket = serverSocket.Accept();
// You can write your own connection handler class that automatically
// starts a new ReceiveData thread when it gets a client connection
ConnectionHandler chandler = new ConnectionHandler(clientSocket);
// Have an on-client-disconnected event which you can subscribe to
// and remove the handler from your list when the client is disconnected
chandler.OnClinetDisconnectedEvent += new OnClientDisconnectedDelegate(OnClientDisconnected);
mHandlerList.Add(chandler);
}
// When you're terminating the program, then just go through
// the list of active ConnectionHandlers and call some method
// which tells them to close their connections with the clients
// and terminates the thread.
To be even more precise, you are likely to have the very similar behavior with the client's and the server's ReceiveData method: i.e. synchronously send a message back whenever they receive some message. Here is a more realistic example that might help you conceptualize it better:
void ReceiveData(Socket s)
{
int bytesExpected = 1024; // somehow specify the number of bytes expected
int totalBytesRec = 0; // adds up all the bytes received
int bytesRec = -1; // zero means that you're done receiving
while(bytesRec != 0 && totalBytesRec < bytesExpected )
{
// Receive the response from the remote device.
bytesRec = s.Receive(incBuffer);
totalBytesRec += bytesRec;
if(needToReply)
{
// Send another message
SendData(s);
}
}
}
This is a long running thread, of course, so you would generally like to have it run for as long as the player is connected to the internet. The comment about closing the connection and terminating the thread is specifically for the situation where you need to have a graceful exit (i.e. the player quits the game or the rare case that server needs to be shut down).

Could I have some advice on basic Asynchronous Socket Programming in C#?

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

How to connect to modems with tcp clients, in multiple port or other way?

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

Categories

Resources