C # TCPIP Network

Hello to all,

I am going to write code that should listen for TCPIP messages coming from GSM mobile phones via GPRS. In full time, I see that this works on a virtual private server, and it can process several messages every second.

I am a little virtuoso in network programming, so I did some research on the Internet and read some tutorials. The approach that I am currently considering is a Windows service that uses sockets to monitor the port. If my understanding is correct, do I need one socket to listen for connections with clients, and every time someone tries to connect to the port, will I be given a different socket with which to contact them? Does this mean more experienced ears?

I plan on using asynchronous communication, but for larger design issues, is it worth using streams or not. Threading is not what I really played with, and I am aware of several pitfalls - race conditions and debugging issues, but only two.

If I avoid threads, I know that I have to provide an object that acts as an identifier for a particular conversation. I thought of a GUID for this - any opinions?

Thanks in advance for any answers ...

Martin

+7
c # networking tcp
source share
5 answers

Starting with the .net framework 2.0 SP1, there are some changes to socket libraries associated with asynchronous sockets.

All multithreading is used under the hood. We do not need to use multithreading manually (we do not need to use ThreadPool explicitly). All we do is use BeginAcceptSocket to start accepting new connections and use SocketAsyncEventArgs after accepting a new connection.

Brief implementation:

//In constructor or in method Start var tcpServer = new TcpListener(IPAddress.Any, port); tcpServer.Start(); tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer); //In EndAcceptSocket Socket sock= lister.EndAcceptSocket(asyncResult); var e = new SocketAsyncEventArgs(); e.Completed += ReceiveCompleted; //some data receive handle e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize); if (!sock.ReceiveAsync(e)) {//IO operation finished syncronously //handle received data ReceiveCompleted(sock, e); }//IO operation finished syncronously //Add sock to internal storage 

Full implementation:

 using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; namespace Ample { public class IPEndPointEventArgs : EventArgs { public IPEndPointEventArgs(IPEndPoint ipEndPoint) { IPEndPoint = ipEndPoint; } public IPEndPoint IPEndPoint { get; private set; } } public class DataReceivedEventArgs : EventArgs { public DataReceivedEventArgs(byte[] data, IPEndPoint ipEndPoint) { Data = data; IPEndPoint = ipEndPoint; } public byte[] Data { get; private set; } public IPEndPoint IPEndPoint { get; private set; } } /// <summary> /// TcpListner wrapper /// Encapsulates asyncronous communications using TCP/IP. /// </summary> public sealed class TcpServer : IDisposable { //---------------------------------------------------------------------- //Construction, Destruction //---------------------------------------------------------------------- /// <summary> /// Creating server socket /// </summary> /// <param name="port">Server port number</param> public TcpServer(int port) { connectedSockets = new Dictionary<IPEndPoint, Socket>(); tcpServer = new TcpListener(IPAddress.Any, port); tcpServer.Start(); tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer); } ~TcpServer() { DisposeImpl(false); } public void Dispose() { DisposeImpl(true); } //---------------------------------------------------------------------- //Public Methods //---------------------------------------------------------------------- public void SendData(byte[] data, IPEndPoint endPoint) { Socket sock; lock (syncHandle) { if (!connectedSockets.ContainsKey(endPoint)) return; sock = connectedSockets[endPoint]; } sock.Send(data); } //---------------------------------------------------------------------- //Events //---------------------------------------------------------------------- public event EventHandler<IPEndPointEventArgs> SocketConnected; public event EventHandler<IPEndPointEventArgs> SocketDisconnected; public event EventHandler<DataReceivedEventArgs> DataReceived; //---------------------------------------------------------------------- //Private Functions //---------------------------------------------------------------------- #region Private Functions //   private void Connected(Socket socket) { var endPoint = (IPEndPoint)socket.RemoteEndPoint; lock (connectedSocketsSyncHandle) { if (connectedSockets.ContainsKey(endPoint)) { theLog.Log.DebugFormat("TcpServer.Connected: Socket already connected! Removing from local storage! EndPoint: {0}", endPoint); connectedSockets[endPoint].Close(); } SetDesiredKeepAlive(socket); connectedSockets[endPoint] = socket; } OnSocketConnected(endPoint); } private static void SetDesiredKeepAlive(Socket socket) { socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); const uint time = 10000; const uint interval = 20000; SetKeepAlive(socket, true, time, interval); } static void SetKeepAlive(Socket s, bool on, uint time, uint interval) { /* the native structure struct tcp_keepalive { ULONG onoff; ULONG keepalivetime; ULONG keepaliveinterval; }; */ // marshal the equivalent of the native structure into a byte array uint dummy = 0; var inOptionValues = new byte[Marshal.SizeOf(dummy) * 3]; BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0); BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, Marshal.SizeOf(dummy)); BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2); // of course there are other ways to marshal up this byte array, this is just one way // call WSAIoctl via IOControl int ignore = s.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null); } //socket disconnected handler private void Disconnect(Socket socket) { var endPoint = (IPEndPoint)socket.RemoteEndPoint; lock (connectedSocketsSyncHandle) { connectedSockets.Remove(endPoint); } socket.Close(); OnSocketDisconnected(endPoint); } private void ReceiveData(byte[] data, IPEndPoint endPoint) { OnDataReceived(data, endPoint); } private void EndAcceptSocket(IAsyncResult asyncResult) { var lister = (TcpListener)asyncResult.AsyncState; theLog.Log.Debug("TcpServer.EndAcceptSocket"); if (disposed) { theLog.Log.Debug("TcpServer.EndAcceptSocket: tcp server already disposed!"); return; } try { Socket sock; try { sock = lister.EndAcceptSocket(asyncResult); theLog.Log.DebugFormat("TcpServer.EndAcceptSocket: remote end point: {0}", sock.RemoteEndPoint); Connected(sock); } finally { //EndAcceptSocket can failes, but in any case we want to accept new connections lister.BeginAcceptSocket(EndAcceptSocket, lister); } //we can use this only from .net framework 2.0 SP1 and higher var e = new SocketAsyncEventArgs(); e.Completed += ReceiveCompleted; e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize); BeginReceiveAsync(sock, e); } catch (SocketException ex) { theLog.Log.Error("TcpServer.EndAcceptSocket: failes!", ex); } catch (Exception ex) { theLog.Log.Error("TcpServer.EndAcceptSocket: failes!", ex); } } private void BeginReceiveAsync(Socket sock, SocketAsyncEventArgs e) { if (!sock.ReceiveAsync(e)) {//IO operation finished syncronously //handle received data ReceiveCompleted(sock, e); }//IO operation finished syncronously } void ReceiveCompleted(object sender, SocketAsyncEventArgs e) { var sock = (Socket)sender; if (!sock.Connected) Disconnect(sock); try { int size = e.BytesTransferred; if (size == 0) { //this implementation based on IO Completion ports, and in this case //receiving zero bytes mean socket disconnection Disconnect(sock); } else { var buf = new byte[size]; Array.Copy(e.Buffer, buf, size); ReceiveData(buf, (IPEndPoint)sock.RemoteEndPoint); BeginReceiveAsync(sock, e); } } catch (SocketException ex) { //We can't truly handle this excpetion here, but unhandled //exception caused process termination. //You can add new event to notify observer theLog.Log.Error("TcpServer: receive data error!", ex); } catch (Exception ex) { theLog.Log.Error("TcpServer: receive data error!", ex); } } private void DisposeImpl(bool manualDispose) { if (manualDispose) { //We should manually close all connected sockets Exception error = null; try { if (tcpServer != null) { disposed = true; tcpServer.Stop(); } } catch (Exception ex) { theLog.Log.Error("TcpServer: tcpServer.Stop() failes!", ex); error = ex; } try { foreach (var sock in connectedSockets.Values) { sock.Close(); } } catch (SocketException ex) { //During one socket disconnected we can faced exception theLog.Log.Error("TcpServer: close accepted socket failes!", ex); error = ex; } if ( error != null ) throw error; } } private void OnSocketConnected(IPEndPoint ipEndPoint) { var handler = SocketConnected; if (handler != null) handler(this, new IPEndPointEventArgs(ipEndPoint)); } private void OnSocketDisconnected(IPEndPoint ipEndPoint) { var handler = SocketDisconnected; if (handler != null) handler(this, new IPEndPointEventArgs(ipEndPoint)); } private void OnDataReceived(byte[] data, IPEndPoint ipEndPoint) { var handler = DataReceived; if ( handler != null ) handler(this, new DataReceivedEventArgs(data, ipEndPoint)); } #endregion Private Functions //---------------------------------------------------------------------- //Private Fields //---------------------------------------------------------------------- #region Private Fields private const int SocketBufferSize = 1024; private readonly TcpListener tcpServer; private bool disposed; private readonly Dictionary<IPEndPoint, Socket> connectedSockets; private readonly object connectedSocketsSyncHandle = new object(); #endregion Private Fields } } 
+8
source share

Surprisingly simple to create a multi-threaded server. Check out this example.

 class Server { private Socket socket; private List<Socket> connections; private volatile Boolean endAccept; // glossing over some code. /// <summary></summary> public void Accept() { EventHandler<SocketAsyncEventArgs> completed = null; SocketAsyncEventArgs args = null; completed = new EventHandler<SocketAsyncEventArgs>((s, e) => { if (e.SocketError != SocketError.Success) { // handle } else { connections.Add(e.AcceptSocket); ThreadPool.QueueUserWorkItem(AcceptNewClient, e.AcceptSocket); } e.AcceptSocket = null; if (endAccept) { args.Dispose(); } else if (!socket.AcceptAsync(args)) { completed(socket, args); } }); args = new SocketAsyncEventArgs(); args.Completed += completed; if (!socket.AcceptAsync(args)) { completed(socket, args); } } public void AcceptNewClient(Object state) { var socket = (Socket)state; // proccess } } 
+3
source share

Some tips from a guy who is mainly concerned with mobile networks: do your homework with a normal network connection, preferably on a local host. This will save you a lot of time during testing and will keep you updated until you figure out the approach that works best for you.

As for some specific implementation, I always use synchronized sockets (you will need to configure timeouts so as not to get stuck if something goes wrong), and everything works in separate threads that are synchronized using events. It is much easier than you think. Here are some useful links to get you started:

+1
source share

I am writing the same application right now and I am using this solution:

http://clutch-inc.com/blog/?p=4

It has been tested right now and works great. It is important to make this service only for receiving and storing messages (somewhere) without any other work. I am using NServiceBus to save messages. Another service accepts messages from the queue and does the rest.

+1
source share

Well, C # syntax isn’t fresh right now, but I don’t think it is very different from the Posix standard.

What you can do is when you create your socket for listening, you can specify a value for the lag (the maximum number of simultaneous connections for this server) and create a stream with the same size. Stream pools are easier to use than traditional. TCP you queue for all connections above the lag option.

0
source share

All Articles