代码之家  ›  专栏  ›  技术社区  ›  Martin Milan

TCPIP与C联网#

  •  7
  • Martin Milan  · 技术社区  · 16 年前

    大家好,

    我要写一些代码来监听来自GSM手机的TCPIP信息。随着时间的推移,我认为这是在虚拟专用服务器上运行的,并且很可能每秒处理多条消息。

    我是一个网络编程的处女,所以我在互联网上做了一些研究,并阅读了一些教程。目前我正在考虑的方法是使用套接字监视端口的Windows服务。如果我的理解是正确的,我需要一个套接字来监听来自客户机的连接,并且每次有人试图连接端口时,我都会被传递另一个套接字来与他们通信?这对更有经验的人来说是正确的吗?

    我计划使用异步通信,但更大的设计问题是是否使用线程。线程并不是我真正玩过的东西,我知道有很多陷阱——竞争条件和调试问题只有两个。

    如果我避免线程,我知道必须提供一个对象作为特定会话的标识符。我在想这个的借口-有什么意见吗?

    提前感谢您的回复…

    马丁

    5 回复  |  直到 16 年前
        1
  •  8
  •   Sergey Teplyakov    16 年前

    从.NET Framework 2.0 SP1开始,与异步套接字相关的套接字库中有一些更改。

    引擎盖下使用的所有多线程。我们不需要手动使用多线程(甚至不需要显式使用threadpool)。我们所做的一切-使用 BeginAcceptSocket 用于开始接受新连接,并使用 SocketAsyncEventArgs 接受新连接后。

    简短实施:

    //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
    

    全面实施:

    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
        }
    }
    
        2
  •  3
  •   ChaosPandion    16 年前

    制作多线程服务器非常简单。看看这个例子。

    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
  •  1
  •   wasker    16 年前

    从主要处理移动网络的人那里得到一些建议:用常规的网络连接做作业,最好是在本地主机上。这将节省测试期间的大量时间,并使您保持清醒,直到您找到最适合您的方法。

    对于某些特定的实现,我总是使用同步套接字(如果出现问题,您需要配置超时以避免卡住),并且所有内容都在独立的线程中运行,这些线程在事件的帮助下进行同步。比你想象的简单多了。以下是一些有用的链接,可以帮助您开始:

        4
  •  1
  •   dariol    16 年前

    我现在写的是同一个应用程序,我使用的解决方案如下:

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

    现在已经测试过了,效果很好。重要的是要使此服务仅用于接收和存储消息(在某个地方),而不进行其他工作。我在用 NServiceBus 用于保存邮件。其他服务从队列接收消息,并执行其余的操作。

        5
  •  0
  •   Andres    16 年前

    好吧,C语法现在在我的头脑中并不新鲜,但我认为它与POSIX标准没有太大的区别。

    您可以做的是,当您创建侦听套接字时,您可以为backlog(该服务器的最大同时连接数)指定一个值,并创建一个具有相同大小的线程拉取。线程池比传统的更容易使用。您在backlog参数上方为所有连接排队的TCP。

    推荐文章