最近需要socket 类的编程,就找了一些资料:
一 最简单的:
首先从原理上解释一下采用Socket接口的网络通讯,这里以最常用的C/S模式作为范例,首先,服务端有一个进程(或多个进程)在指定的端口等待客户来连接,服务程序等待客户的连接信息,一旦连接上之后,就可以按设计的数据交换方法和格式进行数据传输。客户端在需要的时刻发出向服务端的连接请求。这里为了便于理解,提到了一些调用及其大致的功能。使用socket调用后,仅产生了一个可以使用的socket描述符,这时还不能进行通信,还要使用其他的调用,以使得socket所指的结构中使用的信息被填写完。
在使用TCP协议时,一般服务端进程先使用socket调用得到一个描述符,然后使用bind调用将一个名字与socket描述符连接起来,对于Internet域就是将Internet地址联编到socket。之后,服务端使用listen调用指出等待服务请求队列的长度。然后就可以使用accept调用等待客户端发起连接,一般是阻塞等待连接,一旦有客户端发出连接,accept返回客户的地址信息,并返回一个新的socket描述符,该描述符与原先的socket有相同的特性,这时服务端就可以使用这个新的socket进行读写操作了。一般服务端可能在accept返回后创建一个新的进程进行与客户的通信,父进程则再到accept调用处等待另一个连接。客户端进程一般先使用socket调用得到一个socket描述符,然后使用connect向指定的服务器上的指定端口发起连接,一旦连接成功返回,就说明已经建立了与服务器的连接,这时就可以通过socket描述符进行读写操作了。
.NetFrameWork为Socket通讯提供了System.Net.Socket命名空间,在这个命名空间里面有以下几个常用的重要类分别是:
·Socket类这个低层的类用于管理连接,WebRequest,TcpClient和UdpClient在内部使用这个类。
·NetworkStream类这个类是从Stream派生出来的,它表示来自网络的数据流
·TcpClient类允许创建和使用TCP连接
·TcpListener类允许监听传入的TCP连接请求
·UdpClient类用于UDP客户创建连接(UDP是另外一种TCP协议,但没有得到广泛的使用,主要用于本地网络)
下面我们来看一个基于Socket的双机通信代码的C#版本
首先创建Socket对象的实例,这可以通过Socket类的构造方法来实现:
publicSocket(AddressFamilyaddressFamily,SocketTypesocketType,ProtocolTypeprotocolType);
其中,addressFamily参数指定Socket使用的寻址方案,socketType参数指定Socket的类型,protocolType参数指定Socket使用的协议。
下面的示例语句创建一个Socket,它可用于在基于TCP/IP的网络(如Internet)上通讯。
Sockettemp=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
若要使用UDP而不是TCP,需要更改协议类型,如下面的示例所示:
Sockettemp=newSocket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
一旦创建Socket,在客户端,你将可以通过Connect方法连接到指定的服务器(你可以在Connect方法前Bind端口,就是以指定的端口发起连接,如果不事先Bind端口号的话,系统会默认在1024到5000随机绑定一个端口号),并通过Send方法向远程服务器发送数据,而后可以通过Receive从服务端接收数据;而在服务器端,你需要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并通过Listen方法侦听该接口上的请求,当侦听到用户端的连接时,调用Accept完成连接的操作,创建新的Socket以处理传入的连接请求。使用完Socket后,使用Close方法关闭Socket。
可以看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP使用一个网络地址和一个服务端口号来唯一标识设备。网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。网络地址和服务端口的组合称为终结点,在.NET框架中正是由EndPoint类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每个受支持的地址族定义了EndPoint的子代;对于IP地址族,该类为IPEndPoint。IPEndPoint 类包含应用程序连接到主机上的服务所需的主机和端口信息,通过组合服务的主机IP地址和端口号,IPEndPoint类形成到服务的连接点。
用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,System.Net命名空间中有两种类可以得到IP地址实例:
·IPAddress类:IPAddress类包含计算机在IP网络上的地址。其Parse方法可将IP地址字符串转换为IPAddress实例。下面的语句创建一个IPAddress实例:
双击代码全选
1
|
IPAddressmyIP=IPAddress.Parse(
"192.168.0.1"
);
|
需要知道的是:Socket类支持两种基本模式:同步和异步。其区别在于:在同步模式中,按块传输,对执行网络操作的函数(如Send和 Receive)的调用一直等到所有内容传送操作完成后才将控制返回给调用程序。在异步模式中,是按位传输,需要指定发送的开始和结束。同步模式是最常用的模式,我们这里的例子也是使用同步模式。
下面看一个完整的例子,client向server发送一段测试字符串,server接收并显示出来,给予client成功响应。
双击代码全选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
usingSystem;
usingSystem.Text;
usingSystem.IO;
usingSystem.Net;
usingSystem.Net.Sockets;
namespacesocketsample
{
classClass1
{
staticvoidMain()
{
try
{
intport=2000;
stringhost=
"127.0.0.1"
;
IPAddressip=IPAddress.Parse(host);
IPEndPointipe=newIPEndPoint(ip,port);
Socketc=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
Console.WriteLine(
"Conneting..."
);
c.Connect(ipe);
stringsendStr=
"hello!Thisisasockettest"
;
byte
[]bs=Encoding.ASCII.GetBytes(sendStr);
Console.WriteLine(
"SendMessage"
);
c.Send(bs,bs.Length,0);
stringrecvStr=
""
;
byte
[]recvBytes=newbyte[1024];
intbytes;
bytes=c.Receive(recvBytes,recvBytes.Length,0);
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
Console.WriteLine(
"ClientGetMessage:{0}"
,recvStr);
c.Close();
}
catch
(ArgumentNullExceptione)
{
Console.WriteLine(
"ArgumentNullException:{0}"
,e);
}
catch
(SocketExceptione)
{
Console.WriteLine(
"SocketException:{0}"
,e);
}
Console.WriteLine(
"PressEntertoExit"
);
Console.ReadLine();
}
}
}
usingSystem;
usingSystem.Text;
usingSystem.IO;
usingSystem.Net;
usingSystem.Net.Sockets;
namespaceProject1
{
classClass2
{
staticvoidMain()
{
try
{
intport=2000;
stringhost=
"127.0.0.1"
;
IPAddressip=IPAddress.Parse(host);
IPEndPointipe=newIPEndPoint(ip,port);
Sockets=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
s.Bind(ipe);
s.Listen(0);
Console.WriteLine(
"Waitforconnect"
);
Sockettemp=s.Accept();
Console.WriteLine(
"Getaconnect"
);
stringrecvStr=
""
;
byte
[]recvBytes=newbyte[1024];
intbytes;
bytes=temp.Receive(recvBytes,recvBytes.Length,0);
recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes);
Console.WriteLine(
"ServerGetMessage:{0}"
,recvStr);
stringsendStr=
"Ok!ClientSendMessageSucessful!"
;
byte
[]bs=Encoding.ASCII.GetBytes(sendStr);
temp.Send(bs,bs.Length,0);
temp.Close();
s.Close();
}
catch
(ArgumentNullExceptione)
{
Console.WriteLine(
"ArgumentNullException:{0}"
,e);
}
catch
(SocketExceptione)
{
Console.WriteLine(
"SocketException:{0}"
,e);
}
Console.WriteLine(
"PressEntertoExit"
);
Console.ReadLine();
}
}
}
|
上面的例子是用的Socket类,System.Net.Socket命名空间还提供了两个抽象高级类TCPClient和UDPClient和用于通讯流处理的NetWorkStream,让我们看下例子
这个可能简单了点,异步的可以看下面的例子:
http://kb.cnblogs.com/a/2098632/
写在最前:以前在C\C++控制台,简单MFC和C#程序里简单接触了网络套接字编程。了解了网络套接字是这样一个流程 打开 -> 发送/接收->关闭 的简单过程。由于网络传输速度影响,在网络套接字的同步调用时会对程序的使用性产生影响(程序界面被卡死),后来知道可以使用异步编程的概念——通过开线程来达到不阻塞用户界面的效果。后来在接触网络套接字编程的次数多了以后,在C#.NET环境中发现Socket竟然多出了两类方法 XXXXAsync 和 BeginXXXX / EndXXXX。接下来就用代码演示一下这两类方法如何使用~
接下来的页面会很长,能扯一点的现在这扯一点。这两类方法分别对应SocketAsyncEventArgs 、 IAsyncResult。我暂时也讲不出什么,有问题问吧还是。文章最后有如何在vs一个解决方案中调试启动多个项目(这可能是很多人没有注意到的操作)。
So long,Coding
首先是XXXXAsync的,
Server
02 | using System.Collections.Generic; |
04 | using System.Net.Sockets; |
11 | static void Main( string [] args) |
13 | Socket serverSk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
15 | serverSk.Bind( new IPEndPoint(IPAddress.Parse( "127.0.0.1" ), 8596)); |
18 | SocketAsyncEventArgs AcceptSAE = new SocketAsyncEventArgs(); |
19 | AcceptSAE.Completed += new EventHandler<socketasynceventargs>(AcceptSAE_Completed); |
20 | serverSk.AcceptAsync(AcceptSAE); |
25 | static void AcceptSAE_Completed( object sender, SocketAsyncEventArgs e) |
27 | Socket serverSk = sender as Socket; |
28 | if (e.SocketError == SocketError.Success) |
30 | serverSk = e.AcceptSocket; |
31 | SocketAsyncEventArgs SendSAE = new SocketAsyncEventArgs(); |
32 | byte [] data = System.Text.Encoding.UTF8.GetBytes( "OK,Just Put IT!" ); |
33 | SendSAE.SetBuffer(data, 0, data.Length); |
34 | SendSAE.Completed += new EventHandler<socketasynceventargs>(SendSAE_Completed); |
36 | SocketAsyncEventArgs RecieveSAE = new SocketAsyncEventArgs(); |
37 | byte [] buffer = new byte [2048]; |
38 | RecieveSAE.SetBuffer(buffer, 0, buffer.Length); |
39 | RecieveSAE.Completed += new EventHandler<socketasynceventargs>(RecieveSAE_Completed); |
41 | serverSk.ReceiveAsync(RecieveSAE); |
42 | serverSk.SendAsync(SendSAE); |
45 | Console.WriteLine( "接受网络套接字连接请求失败!具体原因请自己调试!" ); |
49 | static void RecieveSAE_Completed( object sender, SocketAsyncEventArgs e) |
51 | Socket sk = sender as Socket; |
52 | byte [] data = e.Buffer; |
53 | string msg = System.Text.Encoding.UTF8.GetString(data); |
54 | Console.WriteLine( "Message received: " + msg); |
59 | static void SendSAE_Completed( object sender, SocketAsyncEventArgs e) |
61 | Socket sk = sender as Socket; |
62 | if (e.SocketError == SocketError.Success) |
64 | Console.WriteLine( "Send complete!" ); |
67 | byte [] data = e.Buffer; |
68 | string msg = System.Text.Encoding.UTF8.GetString(data); |
69 | Console.WriteLine( "What you sent: " + msg); |
73 | }</socketasynceventargs></socketasynceventargs></socketasynceventargs> |
Client:
02 | using System.Collections.Generic; |
04 | using System.Net.Sockets; |
07 | namespace SocketTestTwo |
11 | static void Main( string [] args) |
13 | Socket clientSk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
15 | SocketAsyncEventArgs ConnectSAE = new SocketAsyncEventArgs(); |
16 | ConnectSAE.RemoteEndPoint = new IPEndPoint(IPAddress.Parse( "127.0.0.1" ), 8596); |
17 | ConnectSAE.Completed += new EventHandler<socketasynceventargs>(ConnectSAE_Completed); |
18 | clientSk.ConnectAsync(ConnectSAE); |
22 | static void ConnectSAE_Completed( object sender, SocketAsyncEventArgs e) |
24 | Socket clientSk = sender as Socket; |
25 | if (e.SocketError == SocketError.Success && clientSk.Connected) |
27 | SocketAsyncEventArgs SendSAE = new SocketAsyncEventArgs(); |
28 | byte [] data = System.Text.Encoding.UTF8.GetBytes( "I want put all funy things together!" ); |
29 | SendSAE.SetBuffer(data, 0, data.Length); |
30 | SendSAE.Completed += new EventHandler<socketasynceventargs>(SendSAE_Completed); |
32 | SocketAsyncEventArgs RecieveSAE = new SocketAsyncEventArgs(); |
33 | byte [] buffer = new byte [2048]; |
34 | RecieveSAE.SetBuffer(buffer, 0, buffer.Length); |
35 | RecieveSAE.Completed += new EventHandler<socketasynceventargs>(RecieveSAE_Completed); |
38 | clientSk.ReceiveAsync(RecieveSAE); |
39 | clientSk.SendAsync(SendSAE); |
43 | static void RecieveSAE_Completed( object sender, SocketAsyncEventArgs e) |
45 | Socket sk = sender as Socket; |
46 | byte [] data = e.Buffer; |
47 | string msg = System.Text.Encoding.UTF8.GetString(data); |
48 | Console.WriteLine( "Message received: " +msg); |
53 | static void SendSAE_Completed( object sender, SocketAsyncEventArgs e) |
55 | Socket sk = sender as Socket; |
56 | if (e.SocketError == SocketError.Success) |
58 | Console.WriteLine( "Send complete!" ); |
64 | }</socketasynceventargs></socketasynceventargs></socketasynceventargs> |
然后是BenginXXXX 和 EndXXXX的
Server
002 | using System.Collections.Generic; |
004 | using System.Net.Sockets; |
009 | public class MyObject |
013 | private Socket _Socket; |
014 | private string _MyName = "" ; |
015 | private byte [] _Buffer; |
024 | get { return _Socket; } |
025 | set { _Socket = value; } |
031 | get { return _Buffer; } |
032 | set { _Buffer = value; } |
038 | get { return _MyName; } |
039 | set { _MyName = value; } |
046 | public MyObject(Socket socket, string myName) |
051 | public MyObject(Socket socket, string myName, byte [] buffer) |
063 | static void Main( string [] args) |
065 | Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
067 | server.Bind( new IPEndPoint(IPAddress.Parse( "127.0.0.1" ), 8596)); |
070 | server.BeginAccept( new AsyncCallback(AcceptComplete), new MyObject(server, "开始连接了" )); |
077 | static void AcceptComplete(IAsyncResult ar) |
079 | MyObject myObj = ar.AsyncState as MyObject; |
080 | Socket serverSk = myObj.Socket.EndAccept(ar); |
082 | byte [] buffer = new byte [2048]; |
084 | serverSk.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(RecievedComplete), new MyObject(serverSk, "开始接收了" , buffer)); |
086 | byte [] datas = System.Text.Encoding.UTF8.GetBytes( "I know , Just put it!" ); |
087 | serverSk.BeginSend(datas, 0, datas.Length, SocketFlags.None, new AsyncCallback(SendComplete), new MyObject(serverSk, "开始发送了" , datas)); |
091 | static void SendComplete(IAsyncResult ar) |
093 | MyObject myObj = ar.AsyncState as MyObject; |
094 | Socket sk = myObj.Socket; |
096 | int sended = sk.EndSend(ar); |
098 | Console.WriteLine(System.Text.Encoding.UTF8.GetString(myObj.Buffer)); |
101 | static void RecievedComplete(IAsyncResult ar) |
104 | MyObject myObj = ar.AsyncState as MyObject; |
105 | Socket sk = myObj.Socket; |
107 | int recieved = sk.EndReceive(ar); |
109 | Console.WriteLine(System.Text.Encoding.UTF8.GetString(myObj.Buffer)); |
Client
002 | using System.Collections.Generic; |
004 | using System.Net.Sockets; |
009 | public class MyObject |
013 | private Socket _Socket; |
014 | private string _MyName = "" ; |
015 | private byte [] _Buffer; |
024 | get { return _Socket; } |
025 | set { _Socket = value; } |
031 | get { return _Buffer; } |
032 | set { _Buffer = value; } |
038 | get { return _MyName; } |
039 | set { _MyName = value; } |
046 | public MyObject(Socket socket, string myName) |
051 | public MyObject(Socket socket, string myName, byte [] buffer) |
063 | static void Main( string [] args) |
065 | Socket clientSk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
067 | clientSk.BeginConnect( new IPEndPoint(IPAddress.Parse( "127.0.0.1" ), 8596), new AsyncCallback(ConnectComplete), new MyObject(clientSk, "开始连接了" )); |
073 | static void ConnectComplete(IAsyncResult ar) |
075 | MyObject myObj = ar.AsyncState as MyObject; |
076 | Socket clientSk = myObj.Socket; |
078 | byte [] datas = System.Text.Encoding.UTF8.GetBytes( "I always want put things together!" ); |
079 | clientSk.BeginSend(datas, 0, datas.Length, SocketFlags.None, new AsyncCallback(SendComplete), new MyObject(clientSk, "开始发送了" , datas)); |
082 | byte [] buffer = new byte [2048]; |
084 | clientSk.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(RecievedComplete), new MyObject(clientSk, "开始接收了" , buffer)); |
088 | static void SendComplete(IAsyncResult ar) |
090 | MyObject myObj = ar.AsyncState as MyObject; |
091 | Socket sk = myObj.Socket; |
093 | int sended = sk.EndSend(ar); |
095 | Console.WriteLine(System.Text.Encoding.UTF8.GetString(myObj.Buffer)); |
098 | static void RecievedComplete(IAsyncResult ar) |
101 | MyObject myObj = ar.AsyncState as MyObject; |
102 | Socket sk = myObj.Socket; |
104 | int recieved = sk.EndReceive(ar); |
106 | Console.WriteLine(System.Text.Encoding.UTF8.GetString(myObj.Buffer)); |
The end.
附:
启动第一个项目
启动第二个...第N个
代码下载: