深入浅出Linux工具与编程(socket 函数)
1. 基本套接字函数
(1)socket函数原型
socket(建立一个socket文件描述符) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
建立一个socket文件描述符 |
|
函数原型 |
int socket(int domain, int type, int protocol) |
|
函数传入值 |
domain |
AF_INET:IPv4协议 |
AF_INET6:IPv6协议 |
||
AF_LOCAL:Unix域协议 |
||
AF_ROUTE:路由套接口 |
||
AF_KEY:密钥套接口 |
||
type |
SOCKET_STREAM:双向可靠数据流,对应TCP |
|
SOCKET_DGRAM:双向不可靠数据报,对应UDP |
||
SOCKET_RAW:提供传输层以下的协议,可以访问内部网络接口,例如接收和发送ICMP报文 |
||
protocol |
type为SOCKET_RAW时需要设置此值说明协议类型,其他类型设置为0即可 |
|
函数返回值 |
成功:socket文件描述符 |
|
失败:-1,失败原因存于error中 |
表18-1列出了当进行socket调用时,中协议簇(domain)与类型(type)可能产生的组合。
表18-1 socket中协议簇(domain)与类型(type)组合表
|
AF_INET |
AF_INET6 |
AF_LOCAL |
AF_ROUTE |
AF_KEY |
SOCK_STREAM |
TCP |
TCP |
Yes |
|
|
SOCK_DGRAM |
UDP |
UDP |
Yes |
|
|
SOCK_RAW |
IPv4 |
IPv6 |
|
Yes |
Yes |
(2)bind函数原型
bind(将一个本地协议地址与socket文件描述符联系起来) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
将一个协议地址与socket文件描述符联系起来 |
|
函数原型 |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) |
|
函数传入值 |
sockfd |
socket文件描述符 |
addr |
my_addr指向sockaddr结构,该结构中包含IP地址和端口等信息 |
|
addrlen |
sockaddr结构的大小,可设置为sizeof(struct sockaddr) |
|
函数返回值 |
成功:0 |
|
失败:-1,失败原因存于error中 |
利用bind函数绑定地址时,可以指定IP地址和端口号,也可以指定其中之一,甚至一个也不指定。可以使用通配地址INADDR_ANY(为宏定义,其值等于0),它通知内核选择IP地址。表18-2列出了设置socket地址结构的几种方式,但在实际中,绑定的端口号都需要指定。
表18-2 设置socket地址结构的几种方式
进程指定 |
说 明 |
|
IP地址 |
端口 |
|
通配地址INADDR_ANY |
0 |
内核自动选择IP地址和端口号 |
通配地址INADDR_ANY |
非0 |
内核自动选择IP地址,进程指定端口号 |
本地IP地址 |
0 |
进程指定IP地址,内核自动选择端口号 |
本地IP地址 |
非0 |
进程指定IP地址和端口号 |
(3)listen函数原型
listen (等待连接) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
等待连接 |
|
函数原型 |
int listen(int sockfd, int backlog) |
|
函数传入值 |
sockfd |
监听socket文件描述符 |
backlog |
套接字排队的最大连接个数 |
|
函数返回值 |
成功:0 |
|
失败:-1,失败原因存于error中 |
||
特别说明 |
对于监听socket文件描述符sockfd,内核要维护两个队列,分别为未完成连接队列和已完成连接队列,这两个队列之和不超过backlog |
(4)connect函数原型
connect(建立socket连接) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
建立socket连接 |
|
函数原型 |
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) |
|
函数传入值 |
sockfd |
socket文件描述符 |
serv_addr |
连接的网络地址和端口 |
|
addrlen |
sockaddr结构的大小,可设置为sizeof(struct sockaddr) |
|
函数返回值 |
成功:0 |
|
失败:-1,失败原因存于error中 |
||
附加说明 |
函数connect激发TCP的三路握手过程,出错返回有以下几种情况: ① 如果客户没有收到SYN分节的响应(总共75秒,这之间可能重发了若干次SYN),则返回ETIMEDOUT ② 如果对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程在等待与之相连,函数返回错误ECONNREFUSED ③ 如果客户发出的SYN在中间路由器上引发一个目的地不可达的ICMP错误,内核返回EHOSTUNREACH或ENETUNREACH错误(即ICMP错误)给进程 |
(5)accept函数原型
accept(接受socket连接) |
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
函数说明 |
接受socket连接,返回一个新的socket文件描述符,原socket文件描述符仍为listen函数所用,而新的socket文件描述符用来处理连接的读写操作 |
函数原型 |
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) |
函数传入参数 |
sockfd: socket文件描述符 |
addrlen:addr的大小,可设置为sizeof(struct sockaddr) |
|
函数传出参数 |
addr:填入远程主机的地址数据 |
函数返回值 |
成功:实际读取字节数 |
失败:-1,错误代码存放在error中 |
|
附加说明 |
① accept函数由TCP服务器调用,为阻塞函数,从已完成连接的队列中返回一个连接;如果该对列为空,则进程进入阻塞等待 ② 函数返回的套接字为已连接套接字,而监听套接字仍为listen函数所用 |
(6)close函数原型
close (关闭连接的socket文件描述符) |
|
所需头文件 |
#include <unistd.h> |
函数说明 |
关闭连接的socket文件描述符 |
函数原型 |
int close(int sockfd) |
函数传入值 |
sockfd:socket文件描述符 |
函数返回值 |
成功:0 |
失败:-1,失败原因存于error中 |
|
附加说明 |
① close函数默认功能是将套接字置为“已关闭”标记,并立即返回给进程,这个套接字不能再为该进程所用 ② 正常情况下,close将引发四个分节终止序列,但在终止前将发送已排队的数据 ③ 如果套接字描述符访问计数在调用close后大于0(多个进程共享同一个套接字的情况下),则不会引发TCP终止序列(即不会发送FIN分节) |
(7)shutdown函数原型
shutdown(终止socket通信) |
||
所需头文件 |
#include <sys/socket.h> |
|
函数说明 |
终止socket通信 |
|
函数原型 |
int shutdown(int s, int how) |
|
函数传入值 |
s |
socket文件描述符 |
how |
0(SHUT_RD):关闭socket连接的读这一半,不再接收套接字中的数据且现留在收缓冲区的数据作废 |
|
1(SHUT_WR):关闭socket连接的写这一半(半关闭),但留在套接字发送缓冲区中的数据都会被发送,后跟TCP连接终止序列,不管访问计数是否大于0,此后将不能再执行对套接字的任何写操作 |
||
2(SHUT_RDWR):socket连接的读、写都关闭 |
||
函数返回值 |
成功:0 |
|
失败:-1,失败原因存于error中 |
(8)read函数原型
read(从打开的socket文件流中读取数据) |
所需头文件 |
#include <unistd.h> |
函数说明 |
从打开的socket文件流中读取数据,这里仅说明此函数应用于socket的情况 |
函数原型 |
ssize_t read(int fd, void *buf ,size_t count) |
函数传入参数 |
fd: socket文件描述符 |
count:最大读取字节数 |
|
函数传出参数 |
buf:读取数据的首地址 |
函数返回值 |
成功:实际读取字节数 |
失败:-1,错误代码存放在error中 |
|
附加说明 |
调用函数read从socket文件流中读取数据时,有如下几种情况: ① 套接字接收缓冲区接收数据,返回接收到的字节数 ② TCP协议收到FIN数据,返回0 ③ TCP协议收到RST数据,返回-1,同时errno设置为ECONNRESET ④ 进程阻塞过程中接收到信号,返回-1,同时errno设置为EINTR |
(9)write函数原型
write(向socket文件流中写入数据) |
所需头文件 |
#include <unistd.h> |
函数说明 |
向socket文件流中写入数据,这里仅说明此函数应用于socket的情况 |
函数原型 |
ssize_t write (int fd,const void *buf,size_t count) |
函数传入参数
|
fd: socket文件描述符 |
buf:写入数据的首地址 |
|
count:最大写入字节数 |
|
函数返回值 |
成功:实际写入的字节数 |
失败:-1,错误代码存放在error中 |
|
附加说明 |
调用函数write向socket文件流写数据时,有如下几种情况: ① 套接字发送缓冲区有足够空间,返回发送的字节数 ② TCP协议接收到RST数据,返回-1,同时errno设置为ECONNRESET ③ 进程阻塞过程中接收到信号,返回-1,同时errno设置为EINTR |
2. 高级套接字函数
recv和send函数提供了和read和write差不多的功能。不过它们提供了第四个参数来控制读写操作。
(1) send函数原型
send(通过socket文件描述符发送数据到对方) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
通过socket文件描述符发送数据到对方 |
|
函数原型 |
ssize_t send(int s, const void *buf, size_t len, int flags) |
|
函数传入值 |
s |
socket文件描述符 |
buf |
发送数据的首地址 |
|
len |
发送数据的长度 |
|
flags |
0:此时功能同write,flags还可以设为以下标志的组合 |
|
MSG_OOB:发送带外数据 |
||
MSG_DONTROUTE:告诉IP协议,目的主机在本地网络,没有必要查找路由表 |
||
MSG_DONTWAIT:设置为非阻塞操作 |
||
MSG_NOSIGNAL:表示发送动作不愿被SIGPIPE信号中断 |
||
函数返回值 |
成功:实际发送的字节数 |
|
失败:-1,失败原因存于error中 |
(2) recv函数原型
recv(通过socket文件描述符从对方接收数据) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
通过socket文件描述符从对方接收数据 |
|
函数原型 |
ssize_t recv(int s, void *buf, size_t len, int flags) |
|
函数传入值 |
s |
socket文件描述符 |
len |
可接收数据的最大长度 |
|
flags |
0:此时功能同read,flags还可以设为以下标志的组合 |
|
MSG_OOB:接收带外数据 |
||
MSG_PEEK:查看数据标志,返回的数据并不在系统中删除,如果再次调用recv函数会返回相同的数据内容 |
||
MSG_DONTWAIT:设置为非阻塞操作 |
||
MSG_WAITALL:强迫接收到len大小的数据后才返回,除非有错误或有信号产生 |
||
函数传出值 |
buf |
接收数据的首地址 |
函数返回值 |
成功:实际发送的字节数 |
|
失败:-1,失败原因存于error中 |
3. 套接字属性控制函数
系统提供getsockopt、setsockopt两函数获取和修改套接字结构中一些属性,通过修改这些属性,可以调整套接字的性能,进而调整应用程序的性能。
(1) getsockopt函数原型
getsockopt(获取套接字的属性) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
获取套接字的属性 |
|
函数原型 |
int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) |
|
函数传入值 |
s |
socket文件描述符 |
level |
SOL_SOCKET:通用套接字选项 |
|
IPPROTO_IP:IP选项 |
||
IPPROTO_TCP:TCP选项 |
||
optname |
访问的选项名,具体见表18-3 |
|
optlen |
optval的长度 |
|
函数传出值 |
optval |
取得的属性值 |
函数返回值 |
成功:0 |
|
失败:-1,失败原因存于error中 |
表18-3 套接字属性表
level(级别) |
optname(选项名 |
说明 |
数据类型 |
SOL_SOCKET
|
SO_BROADCAST |
允许发送广播数据 |
int |
SO_DEBUG |
允许调试 |
int |
|
SO_DONTROUTE |
不查找路由 |
int |
|
SO_ERROR |
获得套接字错误 |
int |
|
SO_KEEPALIVE |
保持连接 |
int |
|
SO_LINGER |
延迟关闭连接 |
struct linger |
|
SO_OOBINLINE |
带外数据放入正常数据流 |
int |
|
SO_RCVBUF |
接收缓冲区大小 |
int |
|
SO_SNDBUF |
发送缓冲区大小 |
int |
|
SO_RCVLOWAT |
接收缓冲区下限 |
int |
|
SO_SNDLOWAT |
发送缓冲区下限 |
int |
|
SO_RCVTIMEO |
接收超时 |
struct timeval |
|
SO_SNDTIMEO |
发送超时 |
struct timeval |
|
SO_REUSERADDR |
允许重用本地地址和端口 |
int |
|
SO_TYPE |
获得套接字类型 |
int |
|
SO_BSDCOMPAT |
与BSD系统兼容 |
int |
|
IPPROTO_IP
|
IP_HDRINCL |
在数据包中包含IP首部 |
int |
IP_OPTINOS |
IP首部选项 |
int |
|
IP_TOS |
服务类型 |
int |
|
IP_TTL |
生存时间 |
int |
|
IPPRO_TCP |
TCP_MAXSEG |
TCP最大数据段的大小 |
int |
CP_NODELAY |
不使用Nagle算法 |
int |
(2)setsockopt函数原型
setsockopt(设置套接字的属性) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
设置套接字的属性 |
|
函数原型 |
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen) |
|
函数传入值 |
s |
socket文件描述符 |
level |
SOL_SOCKET:通用套接字选项 |
|
IPPROTO_IP:IP选项 |
||
IPPROTO_TCP:TCP选项 |
||
optname |
设置的选项名,具体见表18-3 |
|
optval |
设置的属性值 |
|
optlen |
optval的长度 |
|
函数返回值 |
成功:0 |
|
失败:-1,失败原因存于error中 |
(3)getsockopt、setsockopt函数举例
sockopt.c源代码如下:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
int main()
{
int sockfd,optval,optlen = sizeof(int);
int sndbuf = 0 ;
int rcvbuf = 0 ;
int flag;
if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket") ;
return -1 ;
}
getsockopt(sockfd,SOL_SOCKET,SO_TYPE,&optval,&optlen);
printf("optval = %d\n",optval);
optlen = sizeof(sndbuf);
flag = getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);
printf("sndbuf=%d\n",sndbuf) ;
printf("flag=%d\n",flag) ;
sndbuf = 51200;
flag = setsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sndbuf, optlen);
sndbuf=0 ;
flag = getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);
printf("sndbuf=%d\n",sndbuf) ;
printf("flag=%d\n",flag) ;
close(sockfd);
return 0 ;
}
编译 gcc sockopt.c -o sockopt。
执行 ./sockopt, 执行结果如下:
optval = 1
sndbuf=16384
flag=0
sndbuf=102400
flag=0
4. UDP读写函数
UDP套接字是无连接协议,必须使用sendto函数发送数据,必须使用recvfrom函数接收数据,发送时需指明目的地址。sendto函数与send功能基本相同,recvfrom与recv功能基本相同,只不过sendto函数和recvfrom函数参数中都带有对方地址信息,这两个函数是专门为UDP协议提供的。
(1)sendto函数原型
sendto(通过socket文件描述符发送数据到对方) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
通过socket文件描述符发送数据到对方,用于UDP协议 |
|
函数原型 |
ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) |
|
函数传入值 |
s |
socket文件描述符 |
buf |
发送数据的首地址 |
|
len |
发送数据的长度 |
|
flags |
0:默认方式发送数据,flags还可以设为以下标志的组合 |
|
MSG_OOB:发送带外数据 |
||
MSG_DONTROUTE:告诉IP协议,目的主机在本地网络,没有必要查找路由表 |
||
MSG_DONTWAIT:设置为非阻塞操作 |
||
MSG_NOSIGNAL:表示发送动作不愿被SIGPIPE信号中断 |
||
to |
存放目的主机IP地址和端口信息 |
|
tolen |
to的长度,可设置为sizeof(struct sockaddr) |
|
函数返回值 |
成功:实际发送的字节数 |
|
失败:-1,失败原因存于error中 |
(2) recvfrom函数
recv(通过socket文件描述符从对方接收数据) |
||
所需头文件 |
#include <sys/types.h> #include <sys/socket.h> |
|
函数说明 |
通过socket文件描述符从对方接收数据,用于UDP协议 |
|
函数原型 |
ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) |
|
函数传入值 |
s |
socket文件描述符 |
len |
可接收数据的最大长度 |
|
flags |
0:默认方式接收数据,flags还可以设为以下标志的组合 |
|
MSG_OOB:接收带外数据 |
||
MSG_PEEK:查看数据标志,返回的数据并不在系统中删除,如果再次调用recv函数会返回相同的数据内容 |
||
MSG_DONTWAIT:设置为非阻塞操作 |
||
MSG_WAITALL:强迫接收到len大小的数据后才返回,除非有错误或有信号产生 |
||
fromlen |
from的长度,可设置为sizeof(struct sockaddr) |
|
函数传出值 |
buf |
接收数据的首地址 |
from |
存放发送方的IP地址和端口 |
|
函数返回值 |
成功:实际发送的字节数 |
|
失败:-1,失败原因存于error中 |
摘录自《深入浅出Linux工具与编程》
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。