三 异步套接字

虽然还有许多别的方法解决同步套接字中的问题的方法,但是综合比较来看,异步套接字无疑是大多数情况下最好的解决办法,这个问题稍后讨论。

1 原理

首先来说一下异步的原理(根据自己的理解写的,如果有不正确的地方,还望指正)。

思想其实很简单。在同步里面,当接收或者发送数据的时候,程序的进程是中止的,一直在等待,那么想要解决这个问题自然就会想到——接收或者发送数据的时候程序继续往下执行不就行了嘛!?(没打错,是两个标点。)

但是,这样想的话,问题就来了:我接受或者发送数据完毕之后本来是想执行一些针对这些数据或者另外相关的操作的,现在不理会数据的接受或者发送的话,那我的这些想要的操作怎么去执行啊?

举个例子,服务器端希望接受客户端的定制化的请求,客户端发送的数据是作为服务器端返回给客户端数据的参数的,这样当然要根据接收的数据返回特定的数据。现在不理会接收数据是否完毕程序继续执行的话怎么返回特定数据呢?

这个问题是这样解决的:你不是有许多操作吗,那好,我在该操作发生前定义该操作为之后的特定操作。

还是举例说明。服务器端希望在接收完客户端发送的数据后再将该数据发送回给客户端。那么,在这里我们就可以将把接收的数据发送回给客户端作为一个操作,程序里面自然就是一个函数了,将这个函数定义为服务器端接收完数据之后立即执行的函数。那么如何定义呢?.NET里面提供了专门的方法和委托,这个在代码里面说明。

原理就是这样,很简单,下面看代码示例吧。

2 代码示例

首先是服务器端代码。

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;// 这个对象只是将用到的四个变量包装一下
public class StateObject {public Socket workSocket = null;public const int BufferSize = 1024;public byte[] buffer = new byte[BufferSize];public StringBuilder sb = new StringBuilder();
}public class AsynchronousSocketListener {// 控制线程的的对象public static ManualResetEvent allDone = new ManualResetEvent(false);public AsynchronousSocketListener() {}public static void StartListening() {byte[] bytes = new Byte[1024];// 这个应该很熟了,和同步的一样IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());IPAddress ipAddress = ipHostInfo.AddressList[0];IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);Socket listener = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp );try {listener.Bind(localEndPoint);listener.Listen(100);while (true) {// Set the event to nonsignaled state.allDone.Reset();Console.WriteLine("Waiting for a connection...");//不同的地方开始了。这里采用的接收连接的方法不同//就像上面说的,我想在BeginAccept方法接收连接之后执行AcceptCallback函数//这里只是定义,程序到这还会继续往下执行,一直到allDone.WaitOne();listener.BeginAccept( new AsyncCallback(AcceptCallback),listener );// 这句代码将使程序不能继续往下执行,直到 allDone.Set();执行//allDone.WaitOne();}} catch (Exception e) {Console.WriteLine(e.ToString());}Console.WriteLine("\nPress ENTER to continue...");Console.Read();}public static void AcceptCallback(IAsyncResult ar) {// 当接收连接后,按照前面的定义会执行该函数,首先就是让主线程继续往下执行allDone.Set();//将接收的连接传递近来Socket listener = (Socket) ar.AsyncState;//调用EndAccept方法表示连接已经建立,建立的连接就是该方法的返回的对象Socket handler = listener.EndAccept(ar);StateObject state = new StateObject();state.workSocket = handler;//这里又出现了类似的定义。可以看出,BeginReceive函数的参数和同步里面Receive函数的参数前面是相同的
//只是后面多出了两个:定义在BeginReceive函数执行完毕以后所要执行的操作
//这里定义的是ReadCallback函数handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,new AsyncCallback(ReadCallback), state);}public static void ReadCallback(IAsyncResult ar) {String content = String.Empty;StateObject state = (StateObject) ar.AsyncState;Socket handler = state.workSocket;
//这里EndReceive函数不能理解为结束接收的意思,应该是至今为止接收到的意思
//因为即使使用了该函数,如果数据没有接收完需要再次接收的时候,数据是在前面接收的基础上
//接收剩余的部分int bytesRead = handler.EndReceive(ar);
//下面的if语句虽然没有用循环语句的表象,但是可以看到由于使用了递归的方法,因此整体可以看做是一个循环
//该循环确保接收到所有的数据
//当确定所有数据接受完毕后,会调用send方法if (bytesRead > 0) {// There  might be more data, so store the data received so far.state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));content = state.sb.ToString();if (content.IndexOf("<EOF>") > -1) {Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",content.Length, content );Send(handler, content);} else {// Not all data received. Get more.handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,new AsyncCallback(ReadCallback), state);}}}private static void Send(Socket handler, String data) {byte[] byteData = Encoding.ASCII.GetBytes(data);// 这里和前面的解释类似handler.BeginSend(byteData, 0, byteData.Length, 0,new AsyncCallback(SendCallback), handler);}private static void SendCallback(IAsyncResult ar) {try {Socket handler = (Socket) ar.AsyncState;           int bytesSent = handler.EndSend(ar);Console.WriteLine("Sent {0} bytes to client.", bytesSent);handler.Shutdown(SocketShutdown.Both);handler.Close();} catch (Exception e) {Console.WriteLine(e.ToString());}}public static int Main(String[] args) {StartListening();return 0;}
}
 
从学习的角度来说,代码稍微有点长,这是异步相对于同步比较繁琐的地方,相比于同步只需一句代码而言。但是,我们可以将每个函数抽象成一句代码来看,那逻辑还是很清晰的。
下面再对代码中的一些小细节进行一下说明。
 handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler); 这样类似的函数,最后的两个参数,一个是委托,另一个是一个对象。委托很容易理解,就是之后要执行的函数操作,那后面的那个参数呢?
可以结合这个SendCallback(IAsyncResult ar)函数来理解。上面委托之后的对象实际上包含在SendCallback函数的参数IAsyncResult ar里面。vs帮助文档里是这样解释的:IAsyncResult 表示异步操作的状态,IAsyncResult 类有一个属性AsyncState,此属性返回一个对象,该对象是启动异步操作的方法的最后一个参数。那这样就很明显了,只要使用
Socket handler = (Socket) ar.AsyncState;
就可以将委托后面的对象提取出来使用。
到目前为止,我们可以这样理解异步,就是将一系列的操作预先定义好,之后就不需要重新接手,而让那程序按照预先的定义自行执行。
下面是异步客户端的代码。相比来说,异步客户端没有异步服务器端重要,因为客户端一般不需要同时接收许多连接,只要客户端与服务器端通信即可。
public class AsynchronousClient {private const int port = 11000;// 客户端多了一些线程的控制标识,为了在需要的时候控制线程private static ManualResetEvent connectDone = new ManualResetEvent(false);private static ManualResetEvent sendDone = new ManualResetEvent(false);private static ManualResetEvent receiveDone = new ManualResetEvent(false);private static String response = String.Empty;private static void StartClient() {try {//这里还是一样IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");IPAddress ipAddress = ipHostInfo.AddressList[0];IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);Socket client = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);// 不同的地方开始在这里,不过也是先连接,同时要定义好连接完毕后执行的操作client.BeginConnect( remoteEP, new AsyncCallback(ConnectCallback), client);//等到连接成功后再继续执行connectDone.WaitOne();// 发送数据至服务器端,是先发送,与服务器端的先接收不同Send(client,"This is a test<EOF>");sendDone.WaitOne();//也需要等待// 接收服务器端发送的数据Receive(client);receiveDone.WaitOne();Console.WriteLine("Response received : {0}", response);client.Shutdown(SocketShutdown.Both);client.Close();} catch (Exception e) {Console.WriteLine(e.ToString());}}private static void ConnectCallback(IAsyncResult ar) {try {//这里都一样了Socket client = (Socket) ar.AsyncState;client.EndConnect(ar);Console.WriteLine("Socket connected to {0}",client.RemoteEndPoint.ToString());connectDone.Set();} catch (Exception e) {Console.WriteLine(e.ToString());}}private static void Receive(Socket client) {try {StateObject state = new StateObject();state.workSocket = client;client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0,new AsyncCallback(ReceiveCallback), state);} catch (Exception e) {Console.WriteLine(e.ToString());}}private static void ReceiveCallback( IAsyncResult ar ) {try {StateObject state = (StateObject) ar.AsyncState;Socket client = state.workSocket;int bytesRead = client.EndReceive(ar);if (bytesRead > 0) {state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));client.BeginReceive(state.buffer,0,StateObject.BufferSize,0,new AsyncCallback(ReceiveCallback), state);} else {if (state.sb.Length > 1) {response = state.sb.ToString();}receiveDone.Set();}} catch (Exception e) {Console.WriteLine(e.ToString());}}private static void Send(Socket client, String data) {byte[] byteData = Encoding.ASCII.GetBytes(data);client.BeginSend(byteData, 0, byteData.Length, 0,new AsyncCallback(SendCallback), client);}private static void SendCallback(IAsyncResult ar) {try {Socket client = (Socket) ar.AsyncState;int bytesSent = client.EndSend(ar);Console.WriteLine("Sent {0} bytes to server.", bytesSent);sendDone.Set();} catch (Exception e) {Console.WriteLine(e.ToString());}}public static int Main(String[] args) {StartClient();return 0;}
}

总体来说,客户端和服务器端在原理上是一致的,只是执行的一些操作有些不同而已。

相信看到这里应该对异步套接字有了一定的了解了吧。

 

转载于:https://www.cnblogs.com/ssor/archive/2009/08/29/1556448.html

关于.NET中socket中的异步套接字的研究二相关推荐

  1. python socket多线程 获取朋友列表_python中的(多线程)套接字列表/数组

    我对python有点陌生.我正在尝试在一个程序中创建和使用一个套接字列表/数组.所以我声明了一个数组,如下所示:myCSocks = ['CSock1', 'CSock2', 'CSock3', 'C ...

  2. 孙鑫MFC笔记之十四--多线程同步与异步套接字编程

    线程同步有三种方式: 1.      互斥对象涉及方法: HANDLE hMutex=CreateMutex(NULL,FALSE,NULL); //第二个参数为FALSE,将互斥对象声明为空闲状态 ...

  3. 线程同步与异步套接字编程

    1.利用事件对象来实现线程间的同步 新建一个win32 console application,取名Event,再建一个Event源文件,编辑: #include <iostream.h> ...

  4. MFC(线程同步与异步套接字,孙鑫C++第十六讲笔记整理)

    1.事件对象:来实现线程的同步.与互斥对象一样均属于内核对象.  当人工重置有信号时,所有线程均得到信号,所以不能设为人工重置.代码就不贴了,通过创建匿名的事件对象,也可以让一个程序只能运行一个实例. ...

  5. socket是什么?套接字是什么?

    网络编程就是编写程序使两台联网的计算机相互交换数据. 那么,这两台计算机之间用什么传输数据呢?首先你肯定先需要物理连接嘛. 在此基础上,只需要考虑如何编写数据传输程序.看似很麻烦,但实际上这点不用愁, ...

  6. 糖儿飞教你学C++ Socket网络编程——5.套接字编程步骤与函数

    TCP是一个面向连接的传输层协议,提供高可靠性的字节流传输服务,主要用于一次传输要交换大量报文的情形.为了维护传输的可靠性,TCP增加了许多开销:例如确认.流量控制.计时器以及连接管理等.TCP协议的 ...

  7. 传奇世界服务器端口被占用,Windows socket error: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。 (10048), on API 'bi...

    Windows socket error: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次. (10048), on API 'bind' 你们的系统80%都是XP的,这个就是XP系统的不适 ...

  8. java中套接字,如何在java中获得一个开放的套接字?

    在 Java中,我如何获得一个开放的套接字?我有2个JFrame;在第一个JFrame中,我打开了Client socket的连接.在同一个JFrame中,我创建了另一个JFrame(JFrame2) ...

  9. c语言socket鉴权,建立套接字后进行socket.io身份验证

    小编典典 这实际上并不难,但是您正以错误的方式进行处理.几件事: 您不能使用socket.io 设置 cookie:但是,您可以随时获取任何已连接客户端的cookie值.为了设置cookie,您将必须 ...

最新文章

  1. android 高通平台有前途吗,华为鸿蒙计划要适配高通平台了,可以告别安卓搭载鸿蒙OS了?...
  2. 小程序统一服务消息_[miniblog]小程序订阅消息踩坑记
  3. @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
  4. matlab patch函数_MATLAB实现紧束缚近似能带结构画图
  5. LOJ #2733 [JOI2016春季合宿]Sandwiches (DP)
  6. 使用oracle 的 PL/Sql 定时执行一个存储过程
  7. MOTOMAN-SV3X运动学建模验证图
  8. 如何在 Mac 上切换语言
  9. maven一键部署tomcat war包
  10. 棒!使用.NET Core构建3D游戏引擎
  11. 神技能!在上司靠近座位时,用人脸识别技术及时屏幕切换
  12. db2查看数据库端口
  13. 数据库学习总结与心得
  14. 为什么网站总显示服务器不能创建对象,IE浏览器出现“Automation 服务器不能创建对象”解决教程...
  15. python笔记:7.2.2.2 一元多因素方差分析_交互效应(购房面积影响因素交互效应)
  16. Jsp+Servlet飞机票预定系统(JavaWeb毕业设计源码)
  17. 关于win10开始菜单点击无反应解决方案
  18. Community Preserving Network Embedding 论文笔记
  19. java初中学历_20岁学java初中学历
  20. 清北学堂学习笔记 第一期

热门文章

  1. halcon中编程运算符_R编程中的运算符
  2. java des算法_Java DES算法程序
  3. android生命周期_Android活动生命周期– 7个阶段和功能
  4. java jvm内存模型_Java(JVM)内存模型– Java中的内存管理
  5. Java中的System.exit()
  6. 工作流 节点子线程_节点JS体系结构–单线程事件循环
  7. iphone备忘录自带的扫描功能扫完文件后如何保存为图片
  8. ROS的学习(二十)rosserial中的Publisher和Subscriber中的编程步骤
  9. 常见的C++关键字有哪些?
  10. 2021年最新的Java面试题,精选100题,大厂必备