1、1第第10章章 TCP协议开发协议开发10.0 引言引言10.1 相关相关概念概念 10.2 定义与获取主机信息定义与获取主机信息 10.3 同步套接字编程同步套接字编程 10.4 同步套接字开发举例同步套接字开发举例简单聊天程序简单聊天程序10.5 使用使用TcpClient和和TcpListener简化简化TCP编程编程 10.6 异步套接字编程异步套接字编程 10.7 异步套接字开发举例异步套接字开发举例 10.8 小结小结 210.0 引言引言 1.面向连接的套接字面向连接的套接字 使用使用TCP协议来建立协议来建立IP地址端点之间的会话。地址端点之间的会话。如图:如图:服务器服务器
2、客户端客户端Socket()Bind()Listen()Accept()Receive()Send()Close()Socket()Connect()Send()Receive()Close()310.0 引言引言 410.0 引言引言 510.0 引言引言2.服务器端编程服务器端编程1 指定指定IP地址和端口号地址和端口号(IPAddress、IPEndPoint)IPAddress ip=IPAddress.Parse(127.0.0.1);IPEndPoint iep=new IPEndPoint(ip,6788);2 创建套接字创建套接字Socket socket=new Socket
3、(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);610.0 引言引言3 套接字与套接字与IP端口绑定端口绑定:Bind4 侦听客户端的请求侦听客户端的请求:Listen5 接收客户端的请求接收客户端的请求:Accept6 接收客户端发送的信息接收客户端发送的信息:Receive (?)7 向客户端发送信息向客户端发送信息:Send8 关闭套接字关闭套接字 :Close (代码见代码见 TcpChatOne)710.0 引言引言3.服务器端编程需做的特殊处理服务器端编程需做的特殊处理 发送信息使用用户自身触发的事件发
4、送信息使用用户自身触发的事件,但接收但接收对方发来的信息是对方来触发的事件对方发来的信息是对方来触发的事件;是不是意是不是意味着随时都处于接收状态味着随时都处于接收状态,类似类似QQ的聊天的聊天,对方对方何时发来信息是不确定的何时发来信息是不确定的.如何解决如何解决?810.0 引言引言4.客户端编程客户端编程1 向服务器端发出请求向服务器端发出请求 Connect来处理来处理2 其他处理的方式类似服务器端其他处理的方式类似服务器端910.1 基本概念基本概念基本概念基本概念-同步套接字同步套接字 在在通通过过Socket进进行行连连接接、接接收收、发发送送操操作作时时,客客户户机机或或服服务
5、务器器会会暂暂停停当当前前工工作作,处处于于等等待待状状态态,即一直等到有数据时才继续执行下面的语句。即一直等到有数据时才继续执行下面的语句。同同步步套套接接字字适适用用于于网网络络传传输输量量不不太太大大的的情情况。况。10-异步套接字异步套接字 在通过在通过Socket进行连接、接收、发送操作时,进行连接、接收、发送操作时,客户机或服务器不会暂停当前工作,而是利用客户机或服务器不会暂停当前工作,而是利用callback机制进行连接、接收和发送处理。机制进行连接、接收和发送处理。异步套接字适用于需要在网络上进行大量数异步套接字适用于需要在网络上进行大量数据传输的应用程序。据传输的应用程序。1
6、110.2 定义与获取主机信息定义与获取主机信息定义主机对象定义主机对象IPEndPoint类类IPAddress类类获取主机信息获取主机信息Dns类中常用方法类中常用方法 Dns.GetHostName()Dns.GetHostByName(string hostname)Dns.GetHostByAddress(address)Dns.Resolve()121.定义主机对象定义主机对象 IPEndPointIPEndPoint 类类包包含含应应用用程程序序连连接接到到主主机机上上的的服服务务所所需需的的主主机机和和端端口口信信息息。通通过过组组合合服服务务的的主主机机 IP IP 地地址址
7、和和端端口口号,号,IPEndPointIPEndPoint 类形成到服务的连接点。类形成到服务的连接点。常用的方法原型:常用的方法原型:public public IPEndPoint(IPAddressIPEndPoint(IPAddress address,address,intint port);port);说明:说明:addressaddress:IPIP地址地址 portport:端口号端口号功能:初始化功能:初始化IPEndPointIPEndPoint类的实例。类的实例。用法举例:用法举例:IPAddressmyServerIPIPAddressmyServerIP=new I
8、PAddress.Parse(“127.0.0.1”);=new IPAddress.Parse(“127.0.0.1”);IPEndPoint myServer=new IPEndPoint(myServerIP,13);132.2.主机解析主机解析程序实例程序实例Dns.Resolve()的使用的使用using System;using System.Net;public class TestFileStream static void Main()Console.Write(输入主机名或者输入主机名或者IP地址:地址:);string str=Console.ReadLine();IPH
9、ostEntry host=Dns.Resolve(str);for(int i=0;i0)int sen=clientSocket.Send(bytes,start,dataleft,SocketFlags.None);start+=sen;dataleft-=sen;21(2)使用使用NetworkStream类的类的Write()方法方法 如果创建了如果创建了NetworkStream对象,就可以直接使用对象,就可以直接使用该对象发送数据,例如该对象发送数据,例如:NetworkStream netstream=new NetworkStream(clientSocket);string
10、 message=发送的数据发送的数据;byte bytes=System.Text.Encoding.Unicode.GetBytes(message);netstream.Write(bytes,0,bytes.Length);与套接字的与套接字的Send()方法不同,方法不同,NetworkStream 对象对象的的Write()方法返回值为方法返回值为void。224.接收数据接收数据 与发送数据相似,接收数据的方法也有与发送数据相似,接收数据的方法也有两种:两种:(1)使用使用Socket类的类的Receive方法方法 (2)使用使用NetworkStream类的类的Read方法,方
11、法,其用法与发送数据类似。其用法与发送数据类似。23例如:例如:byte message=new byte1024;NetworkStream netstream=new NetworkStream(clientSocket);int len=netstream.Read(message,0,message.Length);注意注意:Read()方法有一个整型的返回值,表明实际从方法有一个整型的返回值,表明实际从TCP缓冲区中读取了多少字节的数据,这是因为有缓冲区中读取了多少字节的数据,这是因为有可能可能TCP缓冲区还没有接收到远程设备发送过来的缓冲区还没有接收到远程设备发送过来的指定长度的数
12、据。指定长度的数据。245.TCP协议的无消息边界问题协议的无消息边界问题 TCP协议的无消息边界问题协议的无消息边界问题 TCP协议是无消息边界的,即不能保证来自单个协议是无消息边界的,即不能保证来自单个Send方法的数据能被单个方法的数据能被单个Receive方法读取。方法读取。例:例:第一次发送:第一次发送:abcdefgabcdefg第二次发送:第二次发送:12345671234567接收方接收的数据:接收方接收的数据:第一次接收:第一次接收:abcdefg1234567abcdefg1234567极端情况下:极端情况下:第一次接收:第一次接收:abcdabcd第二次接收:第二次接收:
13、efg12efg12第三次接收:第三次接收:345673456725TCP协议的无消息边界问题协议的无消息边界问题 26 解决方法:解决方法:发送固定长度的消息发送固定长度的消息 将消息长度与消息一起发送将消息长度与消息一起发送,比如字符串,比如字符串消息前用消息前用2个字节表明本次消息长度。个字节表明本次消息长度。使用特殊标记分隔消息使用特殊标记分隔消息(要求消息本身不(要求消息本身不 包括特殊标记符)包括特殊标记符)。2710.4 简单聊天程序简单聊天程序10.4.1 服务器端编程服务器端编程281“开始监听开始监听”的的Click事件代码事件代码private void buttonSt
14、art_Click(object sender,System.EventArgs e)this.buttonStart.Enabled=false;IPAddress ip=IPAddress.Parse(this.textBoxIp.Text);IPEndPoint server=new IPEndPoint(ip,Int32.Parse(this.textBoxPort.Text);socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);29socket.Bind(server);so
15、cket.Listen(10);clientSocket=socket.Accept();this.listBoxState.Items.Add(与客户与客户 +clientSocket.RemoteEndPoint.ToString()+建立连接建立连接);thread=new Thread(new ThreadStart(AcceptMessage);thread.Start();302.接收消息方法接收消息方法private void AcceptMessage()while(true)try NetworkStream netStream=new NetworkStream(clien
16、tSocket);byte datasize=new byte4;netStream.Read(datasize,0,4);31int size=System.BitConverter.ToInt32(datasize,0);Byte message=new bytesize;int dataleft=size;int start=0;while(dataleft0)int recv=netStream.Read(message,start,dataleft);start+=recv;dataleft-=recv;32this.richTextBoxAccept.Rtf=System.Text
17、.Encoding.Unicode.GetString(message);Catch this.listBoxState.Items.Add(与客户断开连接与客户断开连接);break;333 添加添加“发送信息发送信息”的的Click事件代码。事件代码。private void buttonSend_Click(object sender,System.EventArgs e)string str=this.richTextBoxSend.Rtf;int i=str.Length;if(i=0)return;else i*=2;/为什么为什么i2 byte datasize=new byte
18、4;datasize=System.BitConverter.GetBytes(i);byte sendbytes=System.Text.Encoding.Unicode.GetBytes(str);34try NetworkStream netStream=new NetworkStream(clientSocket);netStream.Write(datasize,0,4);netStream.Write(sendbytes,0,sendbytes.Length);netStream.Flush();this.richTextBoxSend.Rtf=;Catch MessageBox.
19、Show(无法发送无法发送!);354“停止监听停止监听”的的Click事件代码。事件代码。private void buttonStop_Click(object sender,System.EventArgs e)this.buttonStart.Enabled=true;try socket.Close();if(clientSocket.Connected)clientSocket.Shutdown(SocketShutdown.Both);clientSocket.Close();thread.Abort();catch MessageBox.Show(监听尚未开始,关闭无效监听尚未
20、开始,关闭无效!);365 窗口关闭前触发的事件代码。窗口关闭前触发的事件代码。private void Form1_Closing(object sender,System.ComponentModel.CancelEventArgs e)socket.Close();if(clientSocket.Connected)clientSocket.Shutdown(SocketShutdown.Both);clientSocket.Close();thread.Abort();3710.4.2 客户端编程客户端编程381.“请求连接请求连接”的的Click事件代码。事件代码。private v
21、oid buttonRequest_Click(object sender,System.EventArgs e)IPAddress ip=IPAddress.Parse(this.textBoxIP.Text);IPEndPoint server=new IPEndPoint(ip,Int32.Parse(this.textBoxPort.Text);socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);39trysocket.Connect(server);catch Message
22、Box.Show(与服务器连接失败与服务器连接失败);return;this.buttonRequest.Enabled=false;this.listBoxState.Items.Add(与服务器连接成功与服务器连接成功);Thread thread=new Thread(new ThreadStart (AcceptMessage);thread.Start();402.接收消息函数接收消息函数private void AcceptMessage()while(true)try NetworkStream netStream=new NetworkStream(socket);byte d
23、atasize=new byte4;netStream.Read(datasize,0,4);int size=System.BitConverter.ToInt32 (datasize,0);Byte message=new bytesize;int dataleft=size;int start=0;41while(dataleft0)int recv=netStream.Read(message,start,dataleft);start+=recv;dataleft-=recv;this.richTextBoxReceive.Rtf=System.Text.Encoding.Unico
24、de.GetString(message);catch this.listBoxState.Items.Add(“服务器断开连接。服务器断开连接。”);break;423.“发送信息发送信息”的的Click事件代码。事件代码。private void buttonSend_Click(object sender,System.EventArgs e)string str=this.richTextBoxSend.Rtf;int i=str.Length;if(i=0)return;elsei*=2;byte datasize=new byte4;datasize=System.BitConve
25、rter.GetBytes(i);43byte sendbytes=System.Text.Encoding.Unicode.GetBytes(str);try NetworkStream netStream=new NetworkStream(socket);netStream.Write(datasize,0,4);netStream.Write(sendbytes,0,sendbytes.Length);netStream.Flush();this.richTextBoxSend.Text=;catchMessageBox.Show(无法发送无法发送!);444.“关闭连接关闭连接”的的
26、Click事件代码。事件代码。private void buttonClose_Click(object sender,System.EventArgs e)trysocket.Shutdown(SocketShutdown.Both);socket.Close();this.listBoxState.Items.Add(与主机断开连接与主机断开连接);thread.Abort();catch MessageBox.Show(“尚未与主机连接,尚未与主机连接,断开无效断开无效!);this.buttonRequest.Enabled=true;455.关闭窗口前触发的事件代码。关闭窗口前触发的
27、事件代码。private void Form1_Closing(object sender,System.ComponentModel.CancelEventArgs e)socket.Shutdown(SocketShutdown.Both);socket.Close();4610.4.3 分析分析1.作为服务器是否能接收多个客户端呢作为服务器是否能接收多个客户端呢?2.服务器和客户端聊天是否合乎逻辑服务器和客户端聊天是否合乎逻辑?3.如何改进程序如何改进程序?4710.5 使用使用TcpClient和和TcpListener类简化类简化TCP编程编程TcpClient类类用于连接、发送和收
28、发数据用于连接、发送和收发数据TcpListener类类用于监听是否有传入的连接请求用于监听是否有传入的连接请求4810.5.1 TcpClient 类类1.构造函数构造函数1 TcpClient()将套接字与自动选择的本地将套接字与自动选择的本地IP地址和端口地址和端口号绑定;号绑定;2 TcpClient(IPEndPoint iep)将套接字与本地将套接字与本地IPEndPoint进行绑定;进行绑定;3 TcpClient(string host,int port);将套接字与自动选择的本地将套接字与自动选择的本地IP地址和端口号地址和端口号绑定绑定,并且与远程设备建立连接并且与远程设备
29、建立连接.Host指远程主指远程主机的机的IP地址或主机名地址或主机名;492.常用方法常用方法 1 Connect()与远程设备建立与远程设备建立TCP连接连接 2 GetStream()获取能够发送和接收数据的获取能够发送和接收数据的 NetworkStream 对象对象.(建立连接后才能使用建立连接后才能使用)3 Close()关闭关闭TCP连接连接5010.5.2 TcpListener类类1.构造函数构造函数 1 TcpListener(int port)将将TcpListener与指定的本地端口号进行绑定与指定的本地端口号进行绑定 2 TcpListener(IPAdress ad
30、dress,int port)将将TcpListener与指定的本地与指定的本地IP地址和端口号地址和端口号进行绑定进行绑定 3 TcpListener(IPEndPoint iep)将将TcpListener与指定的本地与指定的本地IPEndPoint进行进行绑定绑定 512.常用方法常用方法 1 AccpetTcpClient()该方法返回一个该方法返回一个TcpClient对象,可以通过这个对象进对象,可以通过这个对象进行收发信息行收发信息。TcpClient newClient=tcpListener.AccpetTcpClient();2 AcceptSocket()该方法返回一个该
31、方法返回一个Socket对象,可以通过这个对象进行对象,可以通过这个对象进行收发信息收发信息。3 Start()开始监听;开始监听;4 Stop()结束监听,关闭套接字。结束监听,关闭套接字。5210.5.3 课堂练习课堂练习 用用TcpClient和和 TcpListener两个类改两个类改写我们课本中的聊天程序写我们课本中的聊天程序.5310.5.4 综合示例综合示例1.功能要求功能要求:改进书本上的聊天程序改进书本上的聊天程序,实现多人之实现多人之间的聊天功能间的聊天功能.2.功能演示功能演示:见见TcpChatThree示例示例5455565758596010.5.4 综合示例综合示例
32、3.功能分析功能分析:1 如何实现服务器接收多个客户端如何实现服务器接收多个客户端?2 多个用户之间聊天如何实现多个用户之间聊天如何实现?每个用户既是客户端又是服务器每个用户既是客户端又是服务器端端?还是采用其他方法还是采用其他方法?3 私聊如何实现私聊如何实现?4 在线人员情况如何维护?在线人员情况如何维护?6110.5.4 综合示例综合示例4.实现实现:1 接收多个客户端:线程处理接收多个客户端:线程处理 2 客户端之间聊天:服务器转发客户端之间聊天:服务器转发 3 多人聊天和私聊:多人聊天和私聊:转发时设置转发对象转发时设置转发对象 4 在线人员维护在线人员维护 上线和离线时需要处理上线
33、和离线时需要处理6210.5.4 综合示例综合示例4.实现实现:5 消息类型消息类型 6 服务器需要保存所有客户的相关服务器需要保存所有客户的相关信息信息6310.5.4 综合示例综合示例5.思考思考:即时通信软件能不能这么做?即时通信软件能不能这么做?6410.6 异步套接字编程异步套接字编程10.6.1 概述概述 1.异步套接字的用途异步套接字的用途 可以解决同步套接字中出现的阻塞问题,采用可以解决同步套接字中出现的阻塞问题,采用asyncCallback委托机制。委托机制。2.asyncCallback委托委托 1 asyncCallback委托的声明格式委托的声明格式:public d
34、elegate void AsyncCallback (IAsyncResult ar)2 IAsyncResult 接口接口 该接口表示异步操作的状态,仅包括四个属性该接口表示异步操作的状态,仅包括四个属性:6510.6.1 概述概述lobject AsyncState:获取用户定义的对象,它限获取用户定义的对象,它限定或包含关于异步操作的信息。定或包含关于异步操作的信息。lWaitHandle AsyncWaitHandle:获取用于等待异:获取用于等待异步操作完成的步操作完成的 System.Threading.WaitHandle。lbool CompletedSynchronousl
35、y:获取异步操作是:获取异步操作是否同步完成的指示。否同步完成的指示。lbool IsCompleted:获取异步操作是否已完成的:获取异步操作是否已完成的指示。指示。3.异步套接字的方法:如下表所示异步套接字的方法:如下表所示6610.6.1 概述概述开始的方法开始的方法说明说明结束的方法结束的方法BeginAccept接收一个请求接收一个请求EndAcceptBeginConnect连接到远程主机连接到远程主机EndConnectBeginReceive接收数据接收数据EndReceiveBeginReceiveFrom从指定主机接收数据从指定主机接收数据EndReceiveFromBeg
36、inSend发送数据发送数据EndSendBeginSendTo向指定主机发送数据向指定主机发送数据EndSendTo67异步套接字结构图异步套接字结构图 服务器服务器 客户端客户端 Socket()Bind()Listen()BeginAccept()EndAccept()BeginReceive()EndReceive()BeginSend()EndSend()Close()Socket()BeginConnect()EndConnect()BeginSend()EndSend()BeginReceive()EndReceive()Close()6810.6.2 BeginAccept()
37、方法和方法和EndAccept()方法方法 异步异步Socket中使用中使用BeginAccept方法开始接收方法开始接收 新的连接请求,其方法原型为:新的连接请求,其方法原型为:public IAsyncResult BeginAccept(AsyncCallback callback,Object state)其中其中:参数参数1为为AsyncCallback类型的委托,委类型的委托,委托提供的方法用于接收连接并且调托提供的方法用于接收连接并且调EndAccept();参数参数2为为Object类型,用于将状态信息传递给委托类型,用于将状态信息传递给委托提供的方法。提供的方法。69示例示例
38、1:lSocket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);lIPEndPoint iep=new IPEndPoint(IPAddress.Any,6788);lsocket.Bind(iep);lint nCount=10;l socket.Listen(nCount);lsocket.BeginAccept(new AsyncCallback(ClientConnected),(Object)socket);70示例示例2:Private void ClientCon
39、nected(IAsyncResult ar)Socket server=(Socket)ar.AsyncState;Socket client server.EndAccept();.71 异步套接字编程中,客户端使用异步套接字编程中,客户端使用BeginConnect()方法连方法连接到远程主机,其格式为:接到远程主机,其格式为:IAsyncResult BeginConnect(EndPoint ep,AsyncCallBack callback,Object state)其中第一个参数是远程主机的其中第一个参数是远程主机的EndPoint对象,第二个对象,第二个参数用于和远程主机连接成
40、功后执行委托调用的方法,最参数用于和远程主机连接成功后执行委托调用的方法,最后一个参数是状态对象,用于传递必要的数据后一个参数是状态对象,用于传递必要的数据。EndConnect()方法的格式为:方法的格式为:public void EndConnect(IasyncResult ar)10.6.3 BeginConnect()方法和方法和EndConnect()方方法法72lIAsyncResult BeginSend(byte buffer,int offset,int size,SocketFlags socketFlags,AsyncCallback callback,Object s
41、tate)lint EndSend(AsyncResult asyncResult)lIAsyncResult BeginReceive(byte buffer,int offset,int size,SocketFlags socketFlags,AsyncCallback callback,Object state)lint EndReceive(IAsyncResult asyncResult)10.6.4 异步发送和接收数据异步发送和接收数据7310.7 异步套接字开发举例异步套接字开发举例10.7.1 服务器端开发服务器端开发7410.7.2 客户端开发客户端开发7510.8 小结小
42、结10.8.1 同步、同步、异步套接字开发比较异步套接字开发比较1.1.编程思路编程思路 11同步套接字同步套接字 服务器服务器 客户端客户端Socket()Bind()Listen()Accept()Receive()Send()Close()Socket()Connect()Send()Receive()Close()762 异步套接字异步套接字 服务器服务器 客户端客户端 Socket()Bind()Listen()BeginAccept()EndAccept()BeginReceive()EndReceive()BeginSend()EndSend()Close()Socket()Be
43、ginConnect()EndConnect()BeginSend()EndSend()BeginReceive()EndReceive()Close()772.数据收发数据收发 1 同步套接字同步套接字l Send、Receive(Socket)l Write、Read (NetworkStream)2 异步套接字异步套接字lBeginSend、EndSend(Socket)lBeginReceive、EndReceive(Socket)78用用SendSend发送信息发送信息int dataleft=bytes.Length;int start=0;while(dataleft0)int
44、sen=clientSocket.Send(bytes,start,dataleft,SocketFlags.None);start+=sen;dataleft-=sen;79用用WriteWrite发送信息发送信息NetworkStream netstream=new NetworkStream(clientSocket);string message=发送的数据发送的数据;byte bytes=System.Text.Encoding.Unicode.GetBytes(message);netstream.Write(bytes,0,bytes.Length);80用用ReceiveRec
45、eive接收信息接收信息int dataleft=size;int start=0;while(dataleft0)int sen=clientSocket.Receive(bytes,start,dataleft,SocketFlags.None);start+=sen;dataleft-=sen;81用用ReadRead接收信息接收信息int dataleft=size;int start=0;while(dataleft0)int sen=netstream.Read(bytes,start,dataleft);start+=sen;dataleft-=sen;8210.8.2 套接字的
46、创建与关闭套接字的创建与关闭1.1.套接字的创建套接字的创建Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);2.套接字的关闭套接字的关闭l Shutdown(SocketShutdown.Both);/关闭会话关闭会话l Close();/关闭套接字及其相关资源关闭套接字及其相关资源8310.8.3 TCP协议的无消息边界问题协议的无消息边界问题 TCP协议是无消息边界的,即不能保证来自单协议是无消息边界的,即不能保证来自单个个Send方法的数据能被单个方法的数据能被
47、单个Receive方法读取。解方法读取。解决方案:决方案:l 发送固定长度的消息发送固定长度的消息 l 将消息长度与消息一起发送将消息长度与消息一起发送,比如字符串消息,比如字符串消息 前用前用2个字节表明本次消息长度。个字节表明本次消息长度。l 使用特殊标记分隔消息使用特殊标记分隔消息(要求消息本身不(要求消息本身不 包括包括特殊标记符)特殊标记符)。8410.8.4 同步套接字的简化编程同步套接字的简化编程TcpClient类类用于连接、收发数据用于连接、收发数据TcpListener类类用于监听是否有传入的连接请求用于监听是否有传入的连接请求8510.8.5 TCP协议的特点协议的特点1 收发的数据必须为字节数组收发的数据必须为字节数组2 字节数组的数据并不是直接发送到远程机器字节数组的数据并不是直接发送到远程机器上,而是发送到了上,而是发送到了TCP缓冲区中(该缓冲区默认缓冲区中(该缓冲区默认大小为大小为1024字节),如图示。字节),如图示。3 面向连接的特性面向连接的特性(冰刃冰刃 软件观看软件观看)86TCP缓冲区缓冲区8710.8.6 高级话题高级话题l多线程技术多线程技术 同步套接字中接收消息处理同步套接字中接收消息处理l委托技术委托技术 异步套接字、线程异步套接字、线程l引用的本质理解引用的本质理解 异步套接字中参数传递异步套接字中参数传递