C#中的Socket编程详解
文章按照 socket 的 创建、连接、传输数据、释放资源的过程来写。给出方法、参数的详细信息。
一,网络基础
说到 socket,需要学习一下tcp/ip的知识,了解一下osi 网络模。
推荐别人的文章,可以很快地了解这些。
二,socket 对象
无论是服务器还是客户端,都要创建一个 socket 对象,创建方法一致。
以下是常见的socket对象创建实例
socket s = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); //监控 ip4 地址,套接字类型为 tcp ,协议类型为 tcp
其有三个构造函数
public socket(socketinformation socketinformation);
public socket(sockettype sockettype, protocoltype protocoltype);
public socket(addressfamily addressfamily, sockettype sockettype, protocoltype protocoltype);
第一个构造函数,socketinformation 对象保存的是
socket(sockettype, protocoltype)
实质上跟第二个构造函数是一样的。就好像你可以直接把( 一个苹果 , 一个梨)直接放进篮子,也可以先给 水果包装好 再放进篮子里。
下面将解释所有参数的意义。
sockettype
指定 socket 类的实例表示的套接字类型。
tcp 用主机的ip地址加上主机上的端口号作为 tcp 连接的端点,这种端点就叫做套接字(socket)或插口。 套接字用(ip地址:端口号)表示。
sockettype 是enum 类型,其字段如下
sockettype |
值 |
对应的protocoltype |
描述 |
---|---|---|---|
unknown |
-1 | unknown |
指定未知的 socket 类型。 |
stream(使用字节流) |
1 |
tcp |
支持可靠、双向、基于连接的字节流 |
dgram(使用数据报) |
2 |
udp |
面向无连接 |
raw |
3 |
icmp、lgmp |
支持对基础传输协议的访问 |
rdm |
4 |
|
支持无连接、面向消息、以可靠方式发送的消息, 并保留数据中的消息边界 |
seqpacket |
5 |
在网络上提供排序字节流的面向连接且可靠的双向传输 |
如需了解更详细的资料,请查阅microsoft文档
地址:https://docs.microsoft.com/zh-cn/dotnet/api/system.net.sockets.sockettype?view=netframework-4.7.2
protocoltype
表示协议类型,是一个enum 类型。
其所有字段如下
sockettype |
值 |
对应的protocoltype |
描述 |
---|---|---|---|
unknown |
-1 | unknown |
指定未知的 socket 类型。 |
stream(使用字节流) |
1 |
tcp |
支持可靠、双向、基于连接的字节流 |
dgram(使用数据报) |
2 |
udp |
面向无连接 |
raw |
3 |
icmp、lgmp |
支持对基础传输协议的访问 |
rdm |
4 |
|
支持无连接、面向消息、以可靠方式发送的消息, 并保留数据中的消息边界 |
seqpacket |
5 |
在网络上提供排序字节流的面向连接且可靠的双向传输 |
addressfamily
表示使用的网络寻址方案,是一个 enum 类型。
地址类型 | 值 | 描述 |
---|---|---|
appletalk | 16 | appletalk 地址。 |
atm | 22 | 本机 atm 服务地址。 |
banyan | 21 | banyan 地址。 |
ccitt | 10 | ccitt 协议(如 x.25)的地址。 |
chaos | 5 | mit chaos 协议的地址。 |
cluster | 24 | microsoft 群集产品的地址。 |
datakit | 9 | datakit 协议的地址。 |
datalink | 13 | 直接数据链接接口地址。 |
decnet | 12 | decnet 地址。 |
ecma | 8 | 欧洲计算机制造商协会 (ecma) 地址。 |
firefox | 19 | firefox 地址。 |
hyperchannel | 15 | nsc hyperchannel 地址。 |
ieee12844 | 25 | ieee 1284.4 工作组地址。 |
implink | 3 | arpanet imp 地址。 |
internetwork | 2 | ip 版本 4 的地址。 |
internetworkv6 | 23 | ip 版本 6 的地址。 |
ipx | 6 | ipx 或 spx 地址。 |
irda | 26 | irda 地址。 |
iso | 7 | iso 协议的地址。 |
lat | 14 | lat 地址。 |
max | 29 | max 地址。 |
netbios | 17 | netbios 地址。 |
networkdesigners | 28 | 支持网络设计器 osi 网关的协议的地址。 |
ns | 6 | xerox ns 协议的地址。 |
osi | 7 | osi 协议的地址。 |
pup | 4 | pup 协议的地址。 |
sna | 11 | ibm sna 地址。 |
unix | 1 | unix 本地到主机地址。 |
unknown | -1 | 未知的地址族。 |
unspecified | 0 | 未指定的地址族。 |
voiceview | 18 | voiceview 地址。 |
socket 官方文档地址
三,bind() 绑定与 connect() 连接
bind() 用于绑定ipendpoint 对象,在服务端使用。
connect() 在客户端使用,用于连接服务端。
创建 socket 对象后,接着绑定本地socket / 连接服务端。
bind()
public void bind (system.net.endpoint localep);
使用方法
socket serversocket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); ipaddress ip = ipaddress.parse("127.0.0.1"); //上面不重要,看下面 //ipendpoint ipendpoint = new ipendpoint(ip, 2300); //serversocket.bind(ipendpoint); serversocket.bind(new ipendpoint(ip, 2300))
你将在在本地创建ipendpoint 对象,拥有此 ip:post 的访问权限。目的是绑定本地机器的某个端口,所有经过此端口的数据就归你管了。
connect()
与远程主机建立连接。connect() 有四个重载方法,不必关注,只需知道,必需提供 ip 和 post 两个值。
使用方法
ipaddress ip = ipaddress.parse("127.0.0.1"); ipendpoint ipendpoint = new ipendpoint(ip, 2300); socket serversocket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); //创建与远程主机的连接 serversocket.connect(ipendpoint);
四,listen() 监听请求连接 和 accept() 接收连接请求
listen()
监控所有发送到此主机的、特点端口的连接请求。服务端使用,客户端不需要。
public void listen (int backlog);
使用 bind() 后,使用 listen() 方法进行监控,backlog 参数指定可排队等待接受的传入连接的数量,即挂起的连接队列的最大长度。
示例
serversocket.listen(10); //开始监听
accept()
accept() 以同步方式监听套接字,在连接请求队列中提取第一个挂起的连接请求,然后创建并返回一个新的 socket 对象。
代码示例
//创建终结点(endpoint) ipaddress ip = ipaddress.any; ipendpoint ipe = new ipendpoint(ip, 8000); //创建 socket 并开始监听 socket serversocket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp); serversocket.bind(ipe); serversocket.listen(10);//开始监听 //接受到client连接,为此连接建立新的socket,并接受信息 socket temp = serversocket.accept();//为新建连接创建新的socket
//关闭连接 temp.close();
注意的是,每次建立连接是一个 accept() 对象,如果你要进行 服务器-客户端互相通讯,应使用同一个 accept() 对象。每个 accept 对象都是 从客户端请求建立开始的,期间只要使用同一个 accept 对象,都可以进行数据传输。
五,receive() 与 send()
- receive() 接收信息
- send() 发送信息
在服务端和客户端都使用这两个方法。
receive()
使用示例
string recvstr = ""; byte[] recvbytes = new byte[1024]; int bytes; bytes = temp.receive(recvbytes, recvbytes.length, 0);//从客户端接受信息 recvstr += encoding.ascii.getstring(recvbytes, 0, bytes);
直接从微软那复制来的。
receive(byte[], int32, int32, socketflags, socketerror) | 使用指定的 socket,从绑定的 socketflags 接收数据,将数据存入接收缓冲区。 |
receive(byte[], int32, int32, socketflags) | 使用指定的 socket,从绑定的 socketflags 接收指定的字节数,存入接收缓冲区的指定偏移量位置。 |
receive(ilist<arraysegment<byte>>, socketflags, socketerror) | 使用指定的 socket,从绑定的 socketflags 接收数据,将数据存入接收缓冲区列表中。 |
receive(byte[], int32, socketflags) | 使用指定的 socket,从绑定的 socketflags 接收指定字节数的数据,并将数据存入接收缓冲区。 |
receive(byte[], socketflags) | 使用指定的 socket,从绑定的 socketflags 接收数据,将数据存入接收缓冲区。 |
receive(ilist<arraysegment<byte>>, socketflags) | 使用指定的 socket,从绑定的 socketflags 接收数据,将数据存入接收缓冲区列表中。 |
receive(ilist<arraysegment<byte>>) | 从绑定的 socket 接收数据,将数据存入接收缓冲区列表中。 |
receive(byte[]) | 从绑定的 socket 套接字接收数据,将数据存入接收缓冲区。 |
参数
byte[] buffer
byte类型的数组,它是存储接收到的数据的位置。
int32 offset
buffer
参数中的位置,用于存储所接收的数据。
int32 size
要接收的字节数。
socketflags socketflags
socketflags值的按位组合。
socketerror errorcode
一个socketerror对象,它存储套接字错误。
socketflags 默认值为0 或 none ,笔者没有搞懂socketflags 的应用场景。
返回
返回已成功读取的字节数。
send()
send()跟receive()用法相似,
示例代码如下
string str = "hello"; byte[] a = encoding.utf8.getbytes(str); send = socket.send(a, 0);
发送/接收 都是使用 byte[] 字节流,所以接收时要进行转换。
六,释放资源
有 accept 释放和 socket 的释放。
accept 是连接对象,一个 socket 可能有数十个 accept 连接。
使用 shutdown( ) 方法可以 禁止 asscpt 对象的操作(禁用某个 socket 对象 的发送和接收)。
public void shutdown (system.net.sockets.socketshutdown how);
socketshutdown 是一个 enum 类型。
实例
temp.shutdown(socketshutdown.receive); //禁止接收
值 | 使用 | 描述 |
---|---|---|
发送 | send | 禁止对此发送socket。 |
接收 | receive | 禁用对此接收socket。 |
消息和传送 | both | 禁用发送和接收对此socket。 |
close()
会直接释放资源,accept 和 socket 对象都可以使用。使用后对象将彻底释放。
七,ipaddress 和ipendpoint
//引入 using system.net;
ipaddress 用来处理ip地址、转换ip地址
ipaddress.parse() 方法可以把以小数点隔分的十进制 ip 表示转化成 ipaddress 类。
ipaddress ip = ipaddress.parse("127.0.0.1");//把ip地址字符串转换为ipaddress类型的实例
ipaddress提供4个只读字段
- any 用于代表本地系统可用的任何ip地址
- broadcase用于代表本地网络的ip广播地址
- loopback用于代表系统的回送地址
- none用于代表系统上没有网络接口
关于其类型的使用和全部方法、构造函数等,请查看文档microsoft文档。
地址https://docs.microsoft.com/zh-cn/dotnet/api/system.net.ipaddress?view=netframework-4.7.2
ipendpoint 表示ipaddress对象与端口的绑定
ipaddress ip = ipaddress.any; //把ip地址字符串转换为ipaddress类型的实例 ipendpoint ipe = new ipendpoint(ip, 8000);//用指定的端口和ip初始化ipendpoint类的新实例
上面的代码,创建一个监控点,端口是 8000,对象是 本地所有ip。
另外,此类能够获取查看端口的值范围,除此外,此类没有太大意义。
microsoft 文档地址https://docs.microsoft.com/zh-cn/dotnet/api/system.net.ipendpoint?view=netframework-4.7.2
到此这篇关于c#中的socket编程详解的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。