【转】基于TCP协议的异步通讯(C#)

1.建立连接:
  在同步模式中,我们在服务器上使用Accept方法接入连接请求,而 在客户端我们则使用Connect方法来连接服务器。相对地,在异步模式下,服务器可以使用BeginAccept方法和EndAccept方法来完成连 接到客户端的任务,在客户端则通过BeginConnect方法和EndConnect方法来实现与服务器的连接。

  BeginAccept在异步方式下传入的连接尝试,它允许其他动作而不必等待连接建立才继续执行后面程序在调用BeginAccept之前,必须使用Listen方法来侦听是否有连接请求,等待他把请求放入消息队列中参数列表为:

BeginAccept(AsyncCallBack, Ojbect state)
AsyncCallBack:代表回调函数
state:表示状态信息,必须保证state中包含socket的句柄

 

而在回调方法中应调用EndAccept()方法来完成操作,该方法返回新的套接字对象,并在以后的通信中可以使用该套接字。其基本流程是:
(1)创建本地终节点,并新建套接字与本地终节点进行绑定;
(2)在端口上侦听是否有新的连接请求;
(3)请求开始接入新的连接,传入Socket的实例或者StateOjbect的实例

ContractedBlock.gif ExpandedBlockStart.gif View Code
//定义IP地址
IPAddress local=IPAddress.Prase("127.0,0,1");
IPEndPoint iep
=new IPEndPoint(local,13000);
//创建服务器的socket对象
server=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)
server.Bind(iep);
server.Listen(
20);
Server.BeginAccecpt(New AsyncCallBack(Accept),server);

 

当BeginAccept()方法调用结束后,一旦新的连接发生,将调用回调函数,而该回调函数必须包括用来结束接入连接操作的EndAccept()方法。

该方参数列表为 Socket EndAccept(IAsyncResult iar)

下面为回调函数的实例:

ContractedBlock.gif ExpandedBlockStart.gif View Code
void Accept(IAsyncResult iar)
{
//还原传入的原始套接字
Socket MyServer=(Socket)iar.ASyncState;
//在原始套接字上调用EndAccept方法,返回新的套接字
service=MyServer.EndAccept(iar);
}

 

服务器端已经准备好了,那么客户端应通过BeginConnect方法和EndConnect来远程连接主机。在调用BeginConnect方法 时必须注册相应的回调函数并且至少传第一个Socket的实例给state参数,以保证EndConnect方法中能使用原始的套接字。下面是一段 BeginConnect的调用:

 

ContractedBlock.gif ExpandedBlockStart.gif View Code
Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)
IPAddress ip
=IPAddress.Prase("127.0.0.1");
IpEndPoint iep
=new IPEndpoint(ip,13000);
sock.BeginConnect(iep,
new AsyncCallBack(Connect),sock);

 

EndConnect是一种阻塞方法,用于完成BeginConnect方法的异步连接诶远程主机的请求。在注册了回调函数后必须接收BeginConnect方法返回的IASynccReuslt作为参数。下面为代码演示:

ContractedBlock.gif ExpandedBlockStart.gif View Code
void Connect(IAsyncResult iar)
{
client
=(socket)iar.AsyncStete;
try
{
client.EndConnect(iar);
}
Catch(SocketException)
{
Console.WriteLine(
"无法连接");
}
Finally
{
}
}

 

下面对关于TcpListener类使用BeginAccetpTcpClient方法处理一个传入的连接尝试。下面是使用BeginAccetpTcpClient方法的代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
public static void DoBeginAccept(TcpListener listner)
{
//开始从客户端监听连接
Console.WriteLine("Waitting for a connection");
//接收连接
//开始准备接入新的连接,一旦有新连接尝试则调用回调函数DoAcceptTcpCliet
listner.BeginAcceptTcpClient
(
new AsyncCallBack(DoAcceptTcpCliet),
listner
);
}

//处理客户端的连接
public static void DoAcceptTcpCliet(IAsyncResult iar)
{
//还原原始的TcpListner对象
TcpListner listener=(TcpListner)iar.AsyncState;
//完成连接的动作,并返回新的TcpClient
TcpClient client=listner.EndAcceptTcp(iar);
Console.WriteLine(
"连接成功");
}

 

代码的处理业务逻辑为:
(1)调用BeginAccetpTcpClient方法开开始连接新的连接,当连接视图发生时,回调函数被调用以完成
连接操作;
(2)上面DoAcceptTcpCliet方法通过AsyncState属性获得由BeginAcceptTcpClient传入的listner实例
(3)在得到listner对象后,用它调用EndAcceptTcpClient方法,该方法返回新的包含客户端信息的TcpClient。

 

BeginConnect方法和EndConnect方法可用于客户端建立与服务端的连接,下面看实例:

ContractedBlock.gif ExpandedBlockStart.gif View Code
Public void doBeginConnect()
{
//开始与远程主机进行连接
client.BeginConnect(serverIP[0],13000,requestCallBack,client);
Console.WriteLine(
"开始与服务器进行连接");
}
Priveate
void requestCallBack(IAsyncResult iar)
{
try
{
//还原原始的TcpClient对象
client=(TcpClient)iar.AsyncState;
//
client.EndConnect(iar);
Console.WriteLine(
"与服务器{0}连接成功",client.Client.RemoteEndPoint);
}
Catch(Exception e)
{
Console.WriteLine(e.toString());
}
Finally
{
}
}

 

2.发送与接受数据:
在建立了套接字的连接后,就可以服务器端和客户端之间进行数据通信了。异步套接字用BeginSend和EndSend方法来负责数据的发送。注意在调用BeginSend方法前要确保双方都已经建立连接,否则会出异常。下面演示代码:

ContractedBlock.gif ExpandedBlockStart.gif View Code
Public void Connect(IAsyncState iar)
{
try
{
//
Socket socket=(Scoket)iar.AsyncState;
socket
=.EndConnect(iar);
byte[] buff=Encoding.ASCII.GetBytes("This is a test!");
s.BeginSend(buff,
0,buff.Length,new AsycCallBack(SendCallBack),socket=);
}
Catch(Exception e)
{
Console.WriteLine(e.toString());
}
Finally
{
}
}

//回调函数
Public void SendCallBack(IAsyncResult iar)
{
try
{
Socket s
=(Socket)iar.IAsyncState;
int send=s.EndSend(iar);
}
Catch(Exception)
{
Console.WriteLine(e.toString());
}
Finally
{
}
}

 

服务器接收数据是通过BeginReceive和EndReceive方法:

ContractedBlock.gif ExpandedBlockStart.gif View Code
//定义两个回调函数
public static void Listen_CallBack(IAsyncResult iar)
{
Socket s
=(Socket)iar.IAsyncState;
Socket s2
=s.EndAccept(iar);
s2.BeginReceive(buffer,
0,buffer.length, new AsyncCallBack(Read_Callback),s2);
}

public static void Read_Callback(IAsyncResult iar)
{
Socket s
=(Socket)iar.IAsyncState;
int recv=s.EndReceive(iar);
string str=Encoding.ASCII.GetString(buffer,0,recv);
Console.WriteLine(str);
}



(1)首先处理连接的回调函数里得到的通讯套接字s2,接着开始接收数据
(2)当数据发送到缓冲区中,BeginReceive方法试图从buffer数组中读取长度为buffer.length的数据块,并返回接收到的数据量recv。最后接收并打印数据。

下面是基于NetworkStream相关的异步发送和接收方法。
NetworkStream使用BeginRead和EndRead方法进行读操作,使用BeginWreite和EndWrete方法进行写操作,下面看实例:

ContractedBlock.gif ExpandedBlockStart.gif View Code
static void DataHandle(TcpClient client)
{
TcpClient tcpClient
=client;
//使用TcpClient的GetStream方法获取网络流
NetworkStream ns=tcpClient.GetStrem();
//检查网络流是否可读
if(ns.CanRead)
{
//定义缓冲区
byte[] read=new byte[1024];
ns.BeginRead(read,
0,read.Length,new AsyncCallBack(myReadCallBack),ns);
}
Else
{
Console.WriteLine(
"无法从网络中读取流数据");
}
}

public static void myReadCallBack(IAsyncResult iar)
{
NetwrokStream ns
=(NetwrokStream)iar.AsyncState;
byte[] read=new byte[1024];
String data
="";
int recv;

recv
=ns.EndReaD(iar);
data
=String.Concat(data,Encoding.ASCII.GetString(read,0,recv));

//接收到的消息长度可能大于缓冲区总大小,反复循环直到读完为止
while(ns.DataAvailable)
{
ns.BeginRead(read,
0,read.Length, new AsyncCallBack(myReadCallBack),ns);
}
//打印
Console.WriteLine("您收到的信息是"+data);
}



3.程序阻塞与异步中的同步问题:
.Net里提供了EventWaitHandle类来表示一个线程的同步事件。
EventWaitHandle即事件等待句柄,他允许线程通过操作系统互发信号和等待彼此的信号来达到线程同步的目的。
这个类有2个子类,分别为AutoRestEevnt(自动重置)和ManualRestEvent(手动重置)。下面是线程同步的几个方法:
(1)Rset方法:将事件状态设为非终止状态,导致线程阻塞。这里的线程阻塞是指允许其他需要等待的线程进行阻塞
即让含WaitOne()方法的线程阻塞;
(2)Set方法:将事件状态设为终止状态,允许一个或多个等待线程继续。该方法发送一个信号给操作系统,让处于等待的某个线程从阻塞状态转换为继续运行,即WaitOne方法的线程不在阻塞;
(3)WaitOne方法:阻塞当前线程,直到当前的等待句柄收到信号。此方法将一直使本线程处于阻塞状态直到收到信号为止,
即当其他非阻塞进程调用set方法时可以继续执行。

ContractedBlock.gif ExpandedBlockStart.gif View Code
IPhostEntry ipHostEntry=Dns.Resolve("host.contoso.com");
IpEndPoint iep
=new IpEndPoint(ipHostEntry.AddressList[0],13000);
Socket s
=new Socket(iep.Address.AddressFamily,SocketType.Stream,ProtocalType.Tcp);

//设置标志位
private ManualResetEvent allDone=new ManualResetEvent(false);
try
{
//绑定并监听
s.Bind(iep);
s.Listen(
13000);
while(true)
{
//让其他需要等待的线程阻塞
allDone.Reset();

Console.WriteLine(
"Waitting for a connect");
s.BeginAccept(
new AsyncCallBack(Listen_Callback),s);
//阻塞当前进程直到接收到信号
allDone.WaitOne();
}
}
catach(Exception e)
{
Conso.WriteLine(e.toString());
}
Finally
{
}

public static void Listen_Callback(IAsyncResult iar)
{
//发送信号让阻塞的线程继续执行
allDone.Set();
Socket s
=(Socket)iar.AsyncState;
Socket ns
=s.EndAccept(iar);
}



(1)试用了ManualRestEvent对象创建一个等待句柄,在调用BeginAccept方法前使用Rest方法允许其他线程阻塞;
(2)为了防止在连接完成之前对套接字进行读写操作,务必要在BeginAccept方法后调用WaitOne来让线程进入阻塞状态。


当有连接接入后系统会自动调用会调用回调函数,所以当代码执行到回调函数时说明连接已经成功,并在函数的第一句
就调用Set方法让处于等待的线程可以继续执行。


转载地址:http://www.cnblogs.com/1848/articles/1818458.html

转载于:https://www.cnblogs.com/liemkell/archive/2011/08/25/2153811.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前 言 6 第1章 进程、线程与网络协议 7 1.1 进程和线程 7 1.1.1 Process类 7 1.1.2 Thread类 9 1.1.3 在一个线程中操作另一个线程的控件 13 1.2 IP地址与端口 15 1.2.1 TCP/IP网络协议 16 1.2.2 IPAddress类与Dns类 17 1.2.3 IPHostEntry类 17 1.2.4 IPEndPoint类 17 1.3 套接字 19 1.3.1 Socket类 20 1.3.2 面向连接的套接字 21 1.3.3 无连接的套接字 23 1.4 网络流 24 1.5 习题1 25 第2章 TCP应用编程 27 2.1 同步TCP应用编程 28 2.1.1 使用套接字发送和接收数据 28 2.1.2 使用NetworkStream对象发送和接收数据 30 2.1.3 TcpClient与TcpListener类 31 2.1.4 解决TCP协议的无消息边界问题 33 2.2 利用同步TCP编写网络游戏 34 2.2.1 服务器端编程 34 2.2.2 客户端编程 49 2.3 异步TCP应用编程 66 2.3.1 EventWaitHandle类 67 2.3.2 AsyncCallback委托 69 2.3.3 BeginAcceptTcpClient方法和EndAcceptTcpClient方法 70 2.3.4 BeginConnect方法和EndConnect方法 70 2.3.5 发送数据 71 2.3.6 接收数据 72 2.4 异步TCP聊天程序 73 2.4.1 服务器端设计 73 2.4.2 客户端设计 79 2.5 习题2 83 第3章 UDP应用编程 84 3.1 UDP协议基础知识 84 3.2 UDP应用编程技术 84 3.2.1 UdpClient类 84 3.2.2 发送和接收数据的方法 86 3.3 利用UDP协议进行广播和组播 90 3.3.1 通过Internet实现群发功能 90 3.3.2 在Internet上举行网络会议讨论 96 3.4 习题3 101 第4章 P2P应用编程 102 4.1 P2P基础知识 102 4.2 P2P应用举例 104 4.3 习题4 114 第5章 SMTP与POP3应用编程 115 5.1 通过应用程序发送电子邮件 115 5.1.1 SMTP协议 115 5.1.2 发送邮件 116 5.2 利用同步TCP接收电子邮件 120 5.2.1 POP3工作原理 121 5.2.2 邮件接收处理 123 5.3 习题5 127 第6章 网络数据加密与解密 128 6.1 对称加密 128 6.2 不对称加密 133 6.3 通过网络传递加密数据 136 6.4 Hash算法与数字签名 152 6.5 习题6 153 第7章 三维设计与多媒体编程 154 7.1 简单的3D设计入门 154 7.2 DirectX基础知识 160 7.2.1 左手坐标系与右手坐标系 160 7.2.2 设备 160 7.2.3 顶点与顶点缓冲 161 7.2.4 Mesh对象 162 7.2.5 法线 162 7.2.6 纹理与纹理映射 162 7.2.7 世界矩阵、投影矩阵与视图矩阵 162 7.2.8 背面剔除 164 7.3 Primitive 164 7.4 Mesh 171 7.5 灯光与材质 177 7.6 音频与视频 187 7.7 直接使用SoundPlayer类播放WAV音频文件 191 7.8 习题7 193 第8章 上机实验指导 194 8.1 实验一 简单网络聊天系统 194 8.2 实验网络呼叫应答提醒系统 195 8.3 实验三 文件数据加密与解密 199
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值