一、概述

  使用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. 微信小程序二维码识别

    目前市场上二维码识别的软件或者网站越来越多,可是真正方便,无广告的却少之很少. 于是,自己突发奇想做了一个微信二维码识别的小程序. 包含功能: 1.识别二维码 ①普通二维码 ②条形码 ③只是复制解析出 ...

  2. Eclipse3.2查找jre的问题

    前几天遇到一个问题,最开始电脑上使用的是解压的JDK(带jre),但是不能打开jar文件.所以从网上下载了一个jre1.8,然后问题来了,Eclipse打开就报错,弹出一个框,在eclipse的目录下 ...

  3. async await的使用

    var sleep = function (time) { return new Promise(function (resolve, reject) { setTimeout(function () ...

  4. Ubuntu/CentOs 搭建SVN服务器

    安装 CentOS : yum install subversion Ubuntu: sudo apt-get install subversion 查看Subversion版本: 使用“svn -- ...

  5. COCI2017-2018-2 San

    题意 有\(n \leq 40\)个节点,每个节点有权值\(H \leq 1e9\)和贡献\(v \leq 1e9\),从任意一个点可以向右跳到一个权值不小于它的节点,并获得该点贡献 可以从任意一个点 ...

  6. 有标号的DAG计数系列问题

    传送门 II 设 \(f_i\) 表示 \(i\) 个点的答案 那么枚举至少 \(j\) 个点的出度为 \(0\) \[\sum_{j=0}^{i}(-1)^j\binom{i}{j}f_{i-j}2 ...

  7. Web前端面试指导(十八):用纯CSS创建一个三角形的原理是什么?

    题目点评 三角形的图标在网页设计是很常见的,属于基本常识题,只要在练习做到过这个功能都能回答出来,可以把你做过的思路描述出来就可以了,本题的难易程度为简单 答题要点 1.采用的是均分原理 盒子都是一个 ...

  8. Angular1.x 之Providers (Value, Factory, Service and Constant )

    官方文档Providers Each web application you build is composed of objects that collaborate to get stuff do ...

  9. Django基础之form操作

    Form表单的功能 自动生成HTML表单元素 检查表单数据的合法性 如果验证错误,重新显示表单(数据不会重置) 数据类型转换(字符类型的数据转换成相应的Python类型) Form相关的对象包括 Wi ...

  10. 沉淀再出发:java中的CAS和ABA问题整理

    沉淀再出发:java中的CAS和ABA问题整理 一.前言 在多并发程序设计之中,我们不得不面对并发.互斥.竞争.死锁.资源抢占等等问题,归根到底就是读写的问题,有了读写才有了增删改查,才有了所有的一切 ...