一、概述

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

  Socket类提供了很多属性和操作方法,但Socket类并没有提供多少自身的状态维护,比如Connected 属性,按文档说法:”获取一个值,该值指示是否 Socket 连接到远程主机从上次以来 Send 或 Receive 操作“,也就说这个值只表示了上次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(); // 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
//
// <param name="acceptEventArg">The context object to use when issuing
// the accept operation on the server's listening socket</param>
public void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(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[] { ,,,,,0x41, };
Send(ping); }
}, null, , );
}

//

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

              socket.Shutdown(SocketShutdown.Both);
              socket.Close();

                }
else
{
if (e1.SocketError != SocketError.Success)
{
log.Error(e1.SocketError);
}
}
};
e1.SetBuffer(buffer, , buffer.Length);
if (!client.SendAsync(e1))
{
if (e1.BytesTransferred == )
{
              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 == )
{
TryConnectAsyn();
}
else
{
if (e.SocketError == SocketError.Success)
{
ushort h = ;
ushort f = ;
byte[] len = BitConverter.GetBytes(h);
byte[] fid = BitConverter.GetBytes(f);
byte[] simb = GetIdBytes(Sim);
byte[] login = new byte[] {
,len[],len[],fid[],fid[], 0x75,simb[],simb[],simb[],simb[], simb[],simb[],simb[],simb[],simb[],simb[],,,,,0x17,0x30,0x30,0x30, 0x30, 0x30 ,0x30 ,0x30, 0x30, 0x30, 0x31,0x0C, 0x22, 0x38, 0x4E, };
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, );
cs.Completed += (a, b) =>
{
Connected(cs);
};
if (!client.ConnectAsync(cs))
{
Connected(cs);
} }
catch (SocketException ex)
{
client.Close();
TryConnectAsyn();
}
catch (Exception ex)
{ } }

2、会话管理

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

三、数据传输

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

.Net Sokcet 异步编程的更多相关文章

  1. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  2. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  3. 关于如何提高Web服务端并发效率的异步编程技术

    最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...

  4. 异步编程 In .NET

    概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...

  5. C#异步编程(二)

    async和await结构 序 前篇博客异步编程系列(一) 已经介绍了何谓异步编程,这篇主要介绍怎么实现异步编程,主要通过C#5.0引入的async/await来实现. BeginInvoke和End ...

  6. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  7. [.NET] 怎样使用 async & await 一步步将同步代码转换为异步编程

    怎样使用 async & await 一步步将同步代码转换为异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6079707.html  ...

  8. [C#] 走进异步编程的世界 - 开始接触 async/await

    走进异步编程的世界 - 开始接触 async/await 序 这是学习异步编程的入门篇. 涉及 C# 5.0 引入的 async/await,但在控制台输出示例时经常会采用 C# 6.0 的 $&qu ...

  9. C#异步编程

    什么是异步编程 什么是异步编程呢?举个简单的例子: using System.Net.Http; using System.Threading.Tasks; using static System.C ...

随机推荐

  1. eclipse的debug技巧之一

    如下图,断点设置的地方会在i==0时就停住,假如我们想在i等于某个值的时候才停住,那么可以添加“过滤条件” 具体操作是在debug模式下,右键breakpoints标签下我们所设置的断点,点击右键菜单 ...

  2. Asp.Net实现在线网站安装(上)

    在很多年前,笔者在使用z-blog搭建个人部落格的时候,最大的感受就是z-blog在线安装功能! 因为在那个时候,以几K每秒的速度上传一个几M或者十几M的压缩包到虚拟主机上,是一个很痛苦的事情.特别是 ...

  3. 旋转/非旋转treap的简单操作

    treap(树堆) 是在二叉搜索树的基础上,通过维护随机附加域,使其满足堆性质,从而使树相对平衡的二叉树: 为什么可以这样呢? 因为在维护堆的时候可以同时保证搜索树的性质: (比如当一棵树的一个域满足 ...

  4. antd-mobile的按需加载

    "babel": { "presets": [ "react-app" ] } 主要问题是 依赖项的版本问题 以及 配置问题 新添加的con ...

  5. PHP进阶知识总结

    周末梳理了下这段时间看书的一些知识点,进步的过程不仅要实践,还要安排多看书.思考.总结. 只针对知识点进行了罗列和简单说明,很多细节还未整理好,待后面再专门详细写. 基础易忽略概念 PHP是一个支持面 ...

  6. 小程序-报错 xxx is not defined (已解决)

    小程序-报错 xxx is not defined (已解决) 问题情境: 这样一段代码,微信的小程序报错 is not defined 我 wxml 想这样调用 //wxml 代码 <view ...

  7. 我的CSDN博客&Github地址

    我的 CSDN 博客地址: https://blog.csdn.net/qq_40147863 Github 地址: https://github.com/xpwi

  8. CountDownTimer的用法及原理

    1.主线程中使用 值得注意的是,CountDownTimer可以在主线程中直接使用.验证一下回调的执行线程.在主线程中执行如下代码 CountDownTimer countDownTimer = , ...

  9. 有关平台支持的从经典部署模型到 Azure Resource Manager 的迁移的技术深入探讨

    本文将深入探讨如何从 Azure 经典部署模型迁移到 Azure Resource Manager 部署模型. 本文将介绍资源和功能级别的资源,让用户了解 Azure 平台如何在两种部署模型之间迁移资 ...

  10. C# 托管非托管资源释放

    1.C#几乎所有对象都为托管对象,不同点是有的对象封装了非托管资源. 2.C#大部分对象在进行垃圾回收时都可以回收,包括非托管资源,因为非托管资源都已经通过C#类进行了封装,会将非托管资源的释放放在析 ...