您的位置:首页 > 其它

关于.NET中socket中的异步套接字的研究一

2009-08-29 20:34 405 查看
经过研究和实验,现在终于在这里可以说说socket的异步问题了。

代码采用MSDN里面官方的例子,这样的话,如果你想深入研究也有了一个通用的基础。

一直认为,其实任何技术的核心并不难,难就难在当该技术发展成熟以后为了各种需要而添加的枝枝蔓蔓,这些后期添加的东西一方面使技术的应用更加方便和稳定,另一方面,对于学习该技术的人来说,增加了不少的学习成本和障碍。我的学习技术的理念就是,将其枝枝蔓蔓的不断裁剪,一直到露出问题的最最核心,然后掌握这个核心,之后从该核心出发,顺着问题的发展思路去学习,一切就是理所当然了 。这篇文章里,我想尽量地贯穿这个思想,把我所理解的说明白。

一 socket的建立

1 构造socket

不管是同步还是异步套接字,都必须首先建立起socket,这是后面连接的基础。

socket构造函数如下
public Socket (
AddressFamily addressFamily,
SocketType socketType,
ProtocolType protocolType
)
可以看到,里面有三个构造函数,每个参数其实都是一个枚举值,详细可以查询MSDN或者vs的帮助文档。我们常用的构造函数如下:
socket = new Socket(AddressFamily.InterNetwork, , ProtocolType.Tcp);

[align=left]
第一个参数AddressFamily.InterNetwork说明我们采用IP 版本 4 的地址(现在都出版本6了);第二个参数SocketType.Stream是说建立的socket支持可靠、双向、基于连接的字节流,而不重复数据,也不保留边界,此类型的 Socket 与单个对方主机进行通信,并且在通信开始之前需要远程主机连接,Stream 使用传输控制协议 (Tcp) ProtocolTypeInterNetworkAddressFamily。 第三个参数表名传输控制协议采用TCP。利用这三个参数就可以构造一个socket实例。
[/align]

2 将socket与指定的IP地址和端口绑定

endPoint = new IPEndPoint(IPAddress.Parse(“127.0.0.1”), 65534);

可以看到,在这里首先构造了一个IPEndPoint对象,构造该对象的目的就是要将特定的IP地址和指定的端口号捆绑到一起,一同赋给socket使用。如下:
socket.Bind(endPoint);

这一句使用socket的bind方法将特定的IP地址和指定的端口号绑定到本身。以后如果需要连接该socket就使用该IP地址和端口号。

3 开始监听

socket.Listen(32);

listen方法将socket置于监听状态。在该状态下,当有客户端连接该socket时,socket会将请求的连接按顺序排列到请求队列里面,之后按顺序通过accept方法接收连接。listen方法后的参数表示请求队列的最大数目。

4 接收连接

当用到连接时,就涉及到同步异步的问题,下面将详细说明。

二 同步与异步

要研究socket里面的异步套接字,必须从同步套接字说起。要连接至少分为两方,一方连接,另一方被连接。我们将主动发起连接的一方成为客户端,将接收连接的一方称为服务器端。无论是客户端还是服务器端,开始构造socket的过程是一致的,只是服务器端需要将socket至于监听状态,而客户端不需要。

同步套接字的运作过程是这样的:

1 服务器端将socket置于监听状态后,客户端发起连接请求。.NET的方法是

public void Connect (
EndPoint remoteEP
)

参数里面含有服务器端的IP地址和端口号。

2 服务器端监听到有连接的请求会接收该连接。接收的方法为

public Socket Accept ()


该方法不需要参数,但是返回一个socket对象,使用该对象就可以处理与连接到的客户端的一切事宜,比如接收客户端发送到的信息和发送信息至客户端等等。

3 当服务器端与客户端的连接建立之后,就可以在两方交流数据。但客户端和服务器端的程序都会等待socket的动作,也就说在在socket发送或者接收数据过程中,两方程序都会中止,一直等到发送或者接收数据完毕才会继续往下执行。

这种情况下,最明显的表现就是:如果是winForm程序,窗口会表现出死机状态,该状态到程序往下继续执行的时候才会恢复。

4 代码示例

下面看一下给出的代码示例。

首先是服务器端。

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class SynchronousSocketListener {

public static string data = null;

public static void StartListening() {

byte[] bytes = new Byte[1024];
//首先是构造一个socket对象,构造过程就不细说了,都一样
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(10);//开始监听了
while (true) {
Console.WriteLine("Waiting for a connection...");
// 接收请求连接,handler接收该连接,之后使用handler处理收发数据的问题
Socket handler = listener.Accept();
data = null;

// 该循环将接收连接发送到的全部数据
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes,0,bytesRec);
if (data.IndexOf("<EOF>") > -1) {
break;
}
}

// 在控制台上打印接收到的数据
Console.WriteLine( "Text received : {0}", data);

// 然后下面演示服务器端发送数据至客户端,就将客户端刚发送的数据又返回给客户端了
byte[] msg = Encoding.ASCII.GetBytes(data);

handler.Send(msg);//发送的方法
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}

} catch (Exception e) {
Console.WriteLine(e.ToString());
}

Console.WriteLine("\nPress ENTER to continue...");
Console.Read();

}

public static int Main(String[] args) {
StartListening();
return 0;
}
}
这里需要注意的是,服务器端使用监听方法处于监听状态而客户端不需要监听,而下面就可以看到客户端使用connect方法连接,而服务器端用不到;再有就是服务器端会首先接收数据,之后发送数据,而客户端正好相反,次序搞错会导致程序出错。
下面是同步套接字客户端代码示例。
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SynchronousSocketClient {

public static void StartClient() {
byte[] bytes = new byte[1024];
try {
// 这里是完全一样的构造过程
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName())
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,11000);
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );

try {
//这里使用连接方法了
sender.Connect(remoteEP);

Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
// 发送数据了
int bytesSent = sender.Send(msg);
// 接收数据了
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes,0,bytesRec));
sender.Shutdown(SocketShutdown.Both);
sender.Close();

} catch (ArgumentNullException ane) {
Console.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
Console.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}

} catch (Exception e) {
Console.WriteLine( e.ToString());
}
}

public static int Main(String[] args) {
StartClient();
return 0;
}
}


关键的地方我已经注释出来了,比较一下就明了了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: