定义(Unofficial)

Socket是实现“open-read/write-close”的一种模式实现网络通信

图片1.png

Scoket的基本操作

Socket()

Socket.Scokett(AddressFamily addressFramily , SocketType socketType, ProtocolType protocolType)
AddressFramily:协议族。常用的协议族有InterNatworkInterNatworkV6等。协议族规定了socket的地址类型,如InterNatwork规定使用IPv4地址+端口号组合。
SocketType:指定socket的地址类型。常用的类型有STREAM、DGRAM、RAW。
ProtocolType:指定协议。常用的协议有TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。 :::warning 上述SocketType和ProtocolType非随意组合,存在一定的规定格式。当ProtocolType=0时(即类型为ProtocolType.Unspecified)时,会自动选择SocketType相对应的默认协议类型。 :::

Bind()

当我们调用Socket创建一个Socket时,返回的Socket描述字存在于addressFramily空间中,没有一个具体地址。若要给它赋值一个地址,就需要使用Bind(),否则在调用Connect()、Listen()时系统会自动分配随机端口。

通常服务器在启动的时候都会绑定一个众所周知的地址(如IP地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配自身的IP地址和一个端口号组合。这就是为什么通常服务器端在Listen之前会调用bind(),而客户端就不会调用,而是在Connect()时由系统随机生成一个。

网络字节序与主机字节序

主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:

a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。 b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。

综上: 在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于 这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再 赋给socket。

Listen()、Connect()函数

如果作为一个服务器,在调用socket()、bind()之后就会调用Listen()来监听这个socket,如果客户端这时调用Connect()发出连接请求,服务器端就会接收到这个请求。

Socket.Listen(int backlog)
Socket.Connect(EndPoint remote EP)
Listen函数的参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,Listen函数将socket变为被动类型的,等待客户的连接请求。
Connect函数的参数为服务器的socket地址。客户端通过调用Connect函数来建立与TCP服务器的连接。

accept()函数

TCP服务器端依次调用socket()、Bind()、Listen()之后,就会监听指定的socket地址了。
TCP客户端依次调用socket()、Connect()之后就想TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。

read()、write()等函数

万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()

read函数是负责从fd中读取内容。
当读成功时,read返回实际所读的字节数,如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误。如果错误为EINTR说明读是由中断引起的,如果是ECONNREST表示网络连接出了问题。
write函数将buf中的nbytes字节内容写入文件描述符fd。成功时返回写的字节数。失败时返回-1,并设置errno变量。
在网络程序中,当我们向套接字文件描述符写时有俩种可能。
1)write的返回值大于0,表示写了部分或者是 全部的数据。
2)返回的值小于0,此时出现了错误。我们要根据错误类型来处理。如果错误为EINTR表示在写的时候出现了中断错误。如果为EPIPE表示 网络连接出现了问题(对方已经关闭了连接)。

close()函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。
注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

Socket类的常用方法

方法 说明
Bind 使Socket与一个本地终结点相关联
Listen 将Socket置于侦听状态
Accept 为新建连接创建新的Socket
Connect 建立与远程主机的连接
Send 将数据发送到连接的Socket
Receive 接收来自绑定的Socket的数据
Close 关闭Socket连接并释放所有关联的资源
Shutdown 禁用某Socket上的发送和接收功能
DisConnect 关闭套接字连接并允许重用套接字
Begin Accept 开始一个异步操作来接受一个传人的连接尝试
End Accept 异步接受传人的连接尝试
Begin Connect 开始一个对远程主机连接的异步请求
End Connect 结束挂起的异步连接请求
Begin DisConnect 开始异步请求从远程终结点断开连接
End DisConnect 结束挂起的异步断开连接请求
Begin Receive 开始从连接的Socket中异步接收数据
End Receive 将数据异步发送到连接的Socket
Begin Send 开始异步发送数据
End Send 结束挂起的异步发送
Get Socket Option 返回Socket选项的值
Set Socket Option 设置Socket选项
Poll 确定Socket的状态
Select 确定一个或多个套接字的状态

Socket类的常用属性

属性 说明
Address Family 获取Socket的地址族
Available 获取已经从网络接收且可供读取的数据量
Blocking 获取或设置一个值, 该值指示Socket是否处于阻止模式
Connected 获取一个值, 该值指示Socket是否连接
Is Bound 指示Socket是否绑定到特定的本地端口
OS Supports IPv 6 指示操作系统和网络适配器是否支持IPv6
Protocol Type 获取Socket的协议类型
Send BufferSize 指定Socket发送缓冲区的大小
Send Timeout 发送数据(Send) 的超时时间
Receive BufferSize 指定Socket接收缓冲区的大小
Receive Timeout 接收数据(Receive) 的超时时间
Ttl 指定Socket发送的Internet协议(IP) 数据包的生存时间(TTL)