博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.Net Sokcet 异步编程
阅读量:4961 次
发布时间:2019-06-12

本文共 6811 字,大约阅读时间需要 22 分钟。

一、概述

  使用Socket 进行实时通讯,如果使用APM,只需要一个Socket类即可。如果使用EAP,则还需要一个SocketAsyncEventArgs类。本文以EAP的方式展开讨论。

  Socket类提供了很多属性和操作方法,但Socket类并没有提供多少自身的状态维护,比如Connected 属性,按文档说法:”获取一个值,该值指示是否  连接到远程主机从上次以来  或  操作“,也就说这个值只表示了上次I/O的状态,而不是当前的,还有像Blocking 属性,阻塞的模式才有用。Socket类其实就是Windows socket api的一个函数及状态描述的集合。Socket类只是给我们提供了网络通讯的能力,并没有连接管理功能,所以Socket编程就是个制定协议、创建会话管理、进行数据输入输出(封包、解包)的过程,可能还要加上业务逻辑。

二、服务端编程

  服务端的主要的几个功能是:端口监听、接受连接、会话管理、数据传输管理。

1、监听

这部分代码比较公式化,无非启动监听后,将Accect动作交给SocketAsyncEventArgs类实例来完成,触发Completed事件:

public void Start(IPEndPoint localEndPoint)    {        // create the socket which listens for incoming connections        listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);        listenSocket.Bind(localEndPoint);        // start the server with a listen backlog of 100 connections        listenSocket.Listen(100);        // post accepts on the listening socket        StartAccept(null);                    //Console.WriteLine("{0} connected sockets with one outstanding receive posted to each....press any key", m_outstandingReadCount);        Console.WriteLine("Press any key to terminate the server process....");        Console.ReadKey();    }    // Begins an operation to accept a connection request from the client     //    // The context object to use when issuing     // the accept operation on the server's listening socket    public void StartAccept(SocketAsyncEventArgs acceptEventArg)    {        if (acceptEventArg == null)        {            acceptEventArg = new SocketAsyncEventArgs();            acceptEventArg.Completed += new EventHandler
(AcceptEventArg_Completed); } else { // socket must be cleared since the context object is being reused acceptEventArg.AcceptSocket = null; } m_maxNumberAcceptedClients.WaitOne(); bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { ProcessAccept(acceptEventArg); } } // This method is the callback method associated with Socket.AcceptAsync // operations and is invoked when an accept operation is complete // void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) { ProcessAccept(e); } private void ProcessAccept(SocketAsyncEventArgs e) { Interlocked.Increment(ref m_numConnectedSockets); Console.WriteLine("Client connection accepted. There are {0} clients connected to the server", m_numConnectedSockets); // Get the socket for the accepted client connection and put it into the //ReadEventArg object user token SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop(); ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket; // As soon as the client is connected, post a receive to the connection bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); if(!willRaiseEvent){ ProcessReceive(readEventArgs); } // Accept the next connection request StartAccept(e); }

2、会话管理

  对于长连接的话,你需要随时知道客户端状态,比如长时间无数据传输的情况可能是网络已经断开了,需要将这个连接给释放掉。根据Connected 属性描述,说明Socket自身并不能感知连接情况,只能通过读写才能确定网络是否断开。如果阻塞的方式读写数据,那在阻塞或读写的时候,连接断开后会发生SocketException或IOException,这容易确定但网络的情况,但异步模式会有点差别,首先没有读写的情况下不会发生异常,在只有异步读的情况下,如果客户没有做Close动作直接断开,服务端也不会报异常,也不会触发Completed 事件,然后这个连接就一直挂在那边,只到有读写动作。但从程序的可靠性上来讲,我们不能通过业务逻辑的读写来确定连接状态,而是通过一个独立读写机制来实现Socket类未提供的连接管理功能。这在Mina.net框架里是作为一个KeepAlived的过虑器来实现的,在超过设定读写空闲时间之后往客户端发送心跳包,以通过读写来确认连接的是否正常。当写入数据的时候,如果这个连接已经断开,则会触发写操作的SocketAsyncEventArgs.Completed ,事件中得到的SocketAsyncEventArgs.BytesTransferred==0以确定客户端已经断开连接。

  示例代码:

//用定时器定时写数据 void KeepAlive(){new Timer((a) =>            {                                byte[] ping = new byte[] { 2,0,3,0,0,0x41,3 };                Send(ping);                }            }, null, 0, 1000);}

//

private void Send(byte[] buffer)        {            var e1 = new SocketAsyncEventArgs();            e1.Completed += (c, d) =>            {                if (d.BytesTransferred == 0)                {

              socket.Shutdown(SocketShutdown.Both);

              socket.Close();

}                else                {                    if (e1.SocketError != SocketError.Success)                    {                        log.Error(e1.SocketError);                    }                }            };            e1.SetBuffer(buffer, 0, buffer.Length);            if (!client.SendAsync(e1))            {                if (e1.BytesTransferred == 0)                {              socket.Shutdown(SocketShutdown.Both);               socket.Close();                }                else                {                    if (e1.SocketError != SocketError.Success)                    {                        log.Error(e1.SocketError);                    }                }            }        }

 

二、客户端

1、连接

  客户端连接到服务器也分同步连接与异步连接,但客户端的异步连接存在一个问题,那就是如果服务器不可到达,异步连接的方式是没有任何反馈的。如下代码,如果远程主机无法连接,则不会抛出异常,也不会进入到Connected方法:

private void Connected(SocketAsyncEventArgs e)        {            if (e.BytesTransferred == 0)            {                TryConnectAsyn();            }            else            {                if (e.SocketError == SocketError.Success)                {                    ushort h = 32;                    ushort f = 1;                    byte[] len = BitConverter.GetBytes(h);                    byte[] fid = BitConverter.GetBytes(f);                    byte[] simb = GetIdBytes(Sim);                    byte[] login = new byte[] {                2,len[1],len[0],fid[1],fid[0], 0x75,simb[2],simb[3],simb[4],simb[5], simb[0],simb[1],simb[2],simb[3],simb[4],simb[5],116,236,123,222,0x17,0x30,0x30,0x30, 0x30, 0x30 ,0x30 ,0x30, 0x30, 0x30, 0x31,0x0C, 0x22, 0x38, 0x4E,3 };                    Send(login);                    //StartReceive();                }                else                {                    TryConnectAsyn();                }            }        }        private void ConnectAsyn()        {            client = new Socket(SocketType.Stream, ProtocolType.Tcp);            try            {                var cs = new SocketAsyncEventArgs();                cs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 5550);                cs.Completed += (a, b) =>                {                    Connected(cs);                };                if (!client.ConnectAsync(cs))                {                    Connected(cs);                }            }            catch (SocketException ex)            {                client.Close();                TryConnectAsyn();            }            catch (Exception ex)            {            }        }

2、会话管理

  客户端的连接状态管理与服务的实现类似,区别是服务端是管理多个连接,客户端只要管理一个连接。

三、数据传输

这里主要一些粘包的处理,这个网上有很多,不赘述。

转载于:https://www.cnblogs.com/icoolno1/p/7346522.html

你可能感兴趣的文章
Nginx配置文件nginx.conf中文详解(转)
查看>>
POJ 1988 Cube Stacking
查看>>
POJ 1308 Is It A Tree?(并查集)
查看>>
N进制到M进制的转换问题
查看>>
Android------三种监听OnTouchListener、OnLongClickListener同时实现即其中返回值true或者false的含义...
查看>>
MATLAB实现多元线性回归预测
查看>>
Mac xcode 配置OpenGL
查看>>
利用sed把一行的文本文件改成每句一行
查看>>
使用Asyncio的Coroutine来实现一个有限状态机
查看>>
Android应用开发:核心技术解析与最佳实践pdf
查看>>
python——爬虫
查看>>
2.2 标识符
查看>>
孤荷凌寒自学python第五十八天成功使用python来连接上远端MongoDb数据库
查看>>
求一个字符串中最长回文子串的长度(承接上一个题目)
查看>>
简单权限管理系统原理浅析
查看>>
springIOC第一个课堂案例的实现
查看>>
求输入成绩的平均分
查看>>
php PDO (转载)
查看>>
wordpress自动截取文章摘要代码
查看>>
[置顶] 一名优秀的程序设计师是如何管理知识的?
查看>>