一.socket()
函数原型
socket()函数的原型如下,这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。
如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
raw_socket = socket(AF_INET, SOCK_RAW, protocol);
domain
函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h中定义。
表1 domain的值及含义
名称 | 含义 |
---|---|
PF_UNIX,PF_LOCAL | -本地通信 |
PF_X25 ITU-T X25 | -ISO-8208协议 |
AF_INET,PF_INET IPv4 | Internet协议 |
PF_AX25 | Amateur radio AX.25 |
PF_INET6 | IPv6 Internet协议 |
PF_ATMPVC | 原始ATM PVC访问 |
PF_IPX | IPX-Novell协议 |
PF_APPLETALK | Appletalk |
PF_NETLINK | 内核用户界面设备 |
PF_PACKET | 底层包访问 |
- 类型为SOCK_STREAM的套接字表示一个双向的字节流,与管道类似。流式的套接字在进行数据收发之前必须已经连接,连接使用connect()函数进行。一旦连接,可以使用read()或者write()函数进行数据的传输。流式通信方式保证数据不会丢失或者重复接收,当数据在一段时间内任然没有接受完毕,可以将这个连接人为已经死掉。
- SOCK_DGRAM和SOCK_RAW 这个两种套接字可以使用函数sendto()来发送数据,使用recvfrom()函数接受数据,recvfrom()接受来自制定IP地址的发送方的数据。
- SOCK_PACKET是一种专用的数据包,它直接从设备驱动接受数据。
type
名称 | 含义 |
---|---|
SOCK_STREAM | Tcp连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输 |
SOCK_DGRAM | 支持UDP连接(无连接状态的消息) |
SOCK_SEQPACKET | 序列化包,提供一个序列化的、可靠的、双向的基本连接的数据传输通道,数据长度定常。每次调用读系统调用时数据需要将全部数据读出 |
SOCK_RAW | RAW类型,提供原始网络协议访问 |
SOCK_RDM | 提供可靠的数据报文,不过可能数据会有乱序 |
SOCK_PACKET | 这是一个专用类型,不能呢过在通用程序中使用并不是所有的协议族都实现了这些协议类型,例如,AF_INET协议族就没有实现SOCK_SEQPACKET协议类型。 |
protocol
函数socket()的第3个参数protocol用于制定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
errno
函数socket()并不总是执行成功,有可能会出现错误,错误的产生有多种原因,可以通过errno获得:
名称 | 含义 |
---|---|
EACCES | 没有权限建立制定的domain的type的socket |
EAFNOSUPPORT | 不支持所给的地址类型 |
EINVAL | 不支持此协议或者协议不可用 |
EMFILE | 进程文件表溢出 |
ENFILE | 已经达到系统允许打开的文件数量,打开文件过多 |
ENOBUFS/ENOMEM | 内存不足。socket只有到资源足够或者有进程释放内存 |
EPROTONOSUPPORT | 制定的协议type在domain中不存在 |
地址格式
IP套接字地址定义为IP接口地址和16位端口号的组合。基本IP协议不提供端口号,它们由更高级别的协议实现,如udp和tcp。在原始套接字上sin_port设置为IPprotocol。
struct sockaddr_in {
sa_family_t sin_family; / *地址系列:AF_INET * /
in_port_t sin_port; / *端口* /
struct in_addr sin_addr; / *ip地址* /
};
/ *ip地址。 * /
struct in_addr {
uint32_t s_addr; s/ *地址* /
};
-
sin_family始终设置为AF_INET。在Linux 2.2中,当缺少此设置时,大多数网络功能都会返回EINVAL。
-
sin_port为端口号。低于1024的端口号称为特权端口(有时也称为保留端口)。只有特权进程(在Linux上:在管理其网络名称空间的用户命名空间中具有CAP_NET_BIND_SERVICE功能的进程)可以将(2)绑定到这些套接字。请注意,原始IPv4协议本身没有端口的概念,它们仅由更高的协议(如tcp和udp)实现。
-
sin_addr是IP主机地址。 struct in_addr的s_addr成员包含网络字节顺序的主机接口地址。应*使用htonl为in_addr分配一个INADDR_ 值(例如,INADDR_LOOPBACK),或者使用inet_aton,inet_addr,inet_makeaddr库函数或直接使用名称解析器。
IPv4地址分为单播,广播和多播地址。单播地址指定主机的单个接口,广播地址指定网络上的所有主机,多播地址指定多播组中的所有主机。只有在设置了SO_BROAD-CAST套接字标志时,才能发送或接收广播地址的数据报。在当前实现中,允许面向连接的套接字仅使用单播地址。
请注意,ip地址和端口始终以网络字节顺序存储。特别是,这意味着您需要在分配端口号时调用htons。标准库中的所有地址/端口操作函数都以网络字节顺序工作。
有几个特殊地址:
- **INADDR_LOOPBACK(127.0.0.1)总是通过环回设备引用本地主机; **
- INADDR_ANY(0.0.0.0)表示绑定的任何地址;
- INADDR_BROADCAST(255.255.255.255)表示任何主机,并且由于历史原因对绑定具有与INADDR_ANY相同的效果。
二.setsockopt()
在TCP连接中,recv等函数默认为阻塞模式(block),即直到有数据到来之前函数不会返回,而我们有时则需要一种超时机制使其在一定时间后返回而不管是否有数据到来,这里我们就会用到setsockopt()函数:
int setsockopt(int s, int level, int optname, void* optval, socklen_t* optlen);
这里我们要涉及到一个结构:
struct timeval
{
time_t tv_sec; //单位为秒
time_t tv_usec; //单位为微秒
};
struct timeval tv_out;
tv_out.tv_sec = 1;
tv_out.tv_usec = 0;
填充这个结构后,我们就可以以如下的方式调用这个函数:
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));(具体参数可以man一下,或查看MSDN)
这样我们就设定了recv()函数的超时机制,当超过tv_out设定的时间而没有数据到来时recv()就会返回0值。
设置套接口的选项。
#include <winsock.h>
int PASCAL FAR setsockopt( SOCKET s, int level, int optname,
const char FAR* optval, int optlen);
- s:标识一个套接口的描述字。
- level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
- optname:需设置的选项。
- optval:指针,指向存放选项值的缓冲区。
- optlen:optval缓冲区的长度。
注释:
setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
有两种套接口的选项:
- 一种是布尔型选项,允许或禁止一种特性;
- 另一种是整形或结构选项。
setsockopt()支持下列选项。其中“类型”表明optval所指数据的类型。
选项 | 类型 | 意义 |
---|---|---|
SO_BROADCAST | BOOL | 允许套接口传送广播信息。 |
SO_DEBUG | BOOL | 记录调试信息。 |
SO_DONTLINER | BOOL | 不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。 |
SO_DONTROUTE | BOOL | 禁止选径;直接传送。 |
SO_KEEPALIVE | BOOL | 发送“保持活动”包。 |
SO_LINGER | struct linger FAR* | 如关闭时有未发送数据,则逗留。 |
SO_OOBINLINE | BOOL | 在常规数据流中接收带外数据。 |
SO_RCVBUF | int | 为接收确定缓冲区大小。 |
SO_REUSEADDR | BOOL | 允许套接口和一个已在使用中的地址捆绑(参见bind())。 |
SO_SNDBUF | int | 指定发送缓冲区大小。 |
TCP_NODELAY | BOOL | 禁止发送合并的Nagle算法(防止TCP粘包)。 |
允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。
1.SO_LINGER
SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相应的操作特性:
struct linger {
int l_onoff;
int l_linger;
};
为了允许SO_LINGER,应用程序应将l_onoff设为非零,将l_linger设为零或需要的超时值(以秒为单位),然后调用setsockopt()。
2.SO_DONTLINGER
为了允许SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff应设为零,然后调用setsockopt()。
缺省条件下,一个套接口不能与一个已在使用中的本地地址捆绑(参见bind())。但有时会需要“重用”地址。因为每一个连接都由本地地址和远端地址的组合唯一确定,所以只要远端地址不同,两个套接口与一个地址捆绑并无大碍。为了通知WINDOWS套接口实现不要因为一个地址已被一个套接口使用就不让它与另一个套接口捆绑,应用程序可在bind()调用前先设置SO_REUSEADDR选项。请注意仅在bind()调用时该选项才被解释;故此无需(但也无害)将一个不会共用地址的套接口设置该选项,或者在bind()对这个或其他套接口无影响情况下设置或清除这一选项。
3.SO_KEEPALIVE
一个应用程序可以通过打开SO_KEEPALIVE选项,使得WINDOWS套接口实现在TCP连接情况下允许使用“保持活动”包。一个WINDOWS套接口实现并不是必需支持“保持活动”,但是如果支持的话,具体的语义将与实现有关,应遵守RFC1122“Internet主机要求-通讯层”中第4.2.3.6节的规范。如果有关连接由于“保持活动”而失效,则进行中的任何对该套接口的调用都将以WSAENETRESET错误返回,后续的任何调用将以WSAENOTCONN错误返回。
4.TCP_NODELAY
TCP_NODELAY选项禁止Nagle算法。Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,来减少主机发送的零碎小数据包的数目。但对于某些应用来说,这种算法将降低系统性能。所以TCP_NODELAY可用来将此算法关闭。应用程序编写者只有在确切了解它的效果并确实需要的情况下,才设置TCP_NODELAY选项,因为设置后对网络性能有明显的负面影响。TCP_NODELAY是唯一使用IPPROTO_TCP层的选项,其他所有选项都使用SOL_SOCKET层。
5.SO_DEBUG
如果设置了SO_DEBUG选项,WINDOWS套接口供应商被鼓励(但不是必需)提供输出相应的调试信息。但产生调试信息的机制以及调试信息的形式已超出本规范的讨论范围。
setsockopt()不支持的BSD选项有:
选项 | 类型 | 意义 |
---|---|---|
SO_ACCEPTCONN | BOOL | 套接口在监听。 |
SO_ERROR | int | 获取错误状态并清除。 |
SO_RCVLOWAT | int | 接收低级水印。 |
SO_RCVTIMEO | int | 接收超时。 |
SO_SNDLOWAT | int | 发送低级水印。 |
SO_SNDTIMEO | int | 发送超时。 |
SO_TYPE | int | 套接口类型。 |
IP_OPTIONS | 在IP头中设置选项。 |
返回值:
若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
错误代码:
WSANOTINITIALISED | 在使用此API之前应首先成功地调用WSAStartup()。 |
WSAENETDOWN | WINDOWS套接口实现检测到网络子系统失效。 |
WSAEFAULT | optval不是进程地址空间中的一个有效部分。 |
WSAEINPROGRESS | 一个阻塞的WINDOWS套接口调用正在运行中。 |
WSAEINVAL | level值非法,或optval中的信息非法。 |
WSAENETRESET | 当SO_KEEPALIVE设置后连接超时。 |
WSAENOPROTOOPT | 未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持 |
WSAENOTCONN | 当设置SO_KEEPALIVE后连接被复位。 |
WSAENOTSOCK | 描述字不是一个套接口。 |
参考
https://blog.csdn.net/xc_tsao/article/details/44123331
https://blog.csdn.net/a493203176/article/details/65438182