
- Unix套接字教程
- Unix套接字 - 首页
- Unix套接字 - 什么是套接字?
- Unix套接字 - 网络地址
- Unix套接字 - 网络主机名
- Unix套接字 - 客户端服务器模型
- Unix套接字 - 结构体
- Unix套接字 - 端口和服务
- Unix套接字 - 网络字节序
- Unix套接字 - IP地址函数
- Unix套接字 - 核心函数
- Unix套接字 - 辅助函数
- Unix套接字 - 服务器示例
- Unix套接字 - 客户端示例
- Unix套接字 - 总结
- Unix套接字有用资源
- Unix套接字 - 快速指南
- Unix套接字 - 有用资源
- Unix套接字 - 讨论
Unix套接字 - 核心函数
本章描述了编写完整的TCP客户端和服务器所需的核心套接字函数。
下图显示了完整的客户端和服务器交互:

socket函数
要执行网络I/O,进程首先必须调用socket函数,指定所需的通信协议类型和协议族等。
#include <sys/types.h> #include <sys/socket.h> int socket (int family, int type, int protocol);
此调用返回一个套接字描述符,您可以在以后的系统调用中使用它,或者在出错时返回-1。
参数
family − 指定协议族,是下面所示的常量之一:
族 | 描述 |
---|---|
AF_INET | IPv4协议 |
AF_INET6 | IPv6协议 |
AF_LOCAL | Unix域协议 |
AF_ROUTE | 路由套接字 |
AF_KEY | 密钥套接字 |
本章不涵盖IPv4以外的其他协议。
type − 指定您想要的套接字类型。它可以取以下值之一:
类型 | 描述 |
---|---|
SOCK_STREAM | 流套接字 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 顺序分组套接字 |
SOCK_RAW | 原始套接字 |
protocol − 参数应设置为下面给定的特定协议类型,或设置为0以选择系统为给定的family和type组合选择的默认值:
协议 | 描述 |
---|---|
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
connect函数
TCP客户端使用connect函数与TCP服务器建立连接。
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
如果成功连接到服务器,此调用返回0;否则,在出错时返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
serv_addr − 指向包含目标IP地址和端口的sockaddr结构体的指针。
addrlen − 设置为sizeof(struct sockaddr)。
bind函数
bind函数将本地协议地址分配给套接字。对于互联网协议,协议地址是32位IPv4地址或128位IPv6地址与16位TCP或UDP端口号的组合。此函数仅由TCP服务器调用。
#include <sys/types.h> #include <sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr,int addrlen);
如果成功绑定到地址,此调用返回0;否则,在出错时返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
my_addr − 指向包含本地IP地址和端口的sockaddr结构体的指针。
addrlen − 设置为sizeof(struct sockaddr)。
您可以自动设置您的IP地址和端口。
端口号为0表示系统将选择一个随机端口,IP地址为INADDR_ANY表示将自动分配服务器的IP地址。
server.sin_port = 0; server.sin_addr.s_addr = INADDR_ANY;
注意 − 所有低于1024的端口都已预留。您可以设置1024以上、65535以下的端口,除非这些端口已被其他程序使用。
listen函数
listen函数仅由TCP服务器调用,它执行两个操作:
listen函数将未连接的套接字转换为被动套接字,指示内核应接受定向到此套接字的传入连接请求。
此函数的第二个参数指定内核应为此套接字排队的最大连接数。
#include <sys/types.h> #include <sys/socket.h> int listen(int sockfd,int backlog);
此调用在成功时返回0,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
backlog − 允许的连接数。
accept函数
TCP服务器调用accept函数以从已完成连接队列的前面返回下一个已完成的连接。调用的签名如下:
#include <sys/types.h> #include <sys/socket.h> int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
此调用在成功时返回一个非负描述符,否则返回-1。返回的描述符被认为是客户端套接字描述符,所有读写操作都将在此描述符上执行以与客户端通信。
参数
sockfd − 由socket函数返回的套接字描述符。
cliaddr − 指向包含客户端IP地址和端口的sockaddr结构体的指针。
addrlen − 设置为sizeof(struct sockaddr)。
send函数
send函数用于通过流套接字或已连接的数据报套接字发送数据。如果您想通过未连接的数据报套接字发送数据,必须使用sendto()函数。
您可以使用write()系统调用发送数据。其签名如下:
int send(int sockfd, const void *msg, int len, int flags);
此调用返回发送出的字节数,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
msg − 指向要发送数据的指针。
len − 要发送的数据长度(以字节为单位)。
flags − 设置为0。
recv函数
recv函数用于通过流套接字或已连接的数据报套接字接收数据。如果您想通过未连接的数据报套接字接收数据,必须使用recvfrom()。
您可以使用read()系统调用读取数据。此调用在辅助函数章节中进行了说明。
int recv(int sockfd, void *buf, int len, unsigned int flags);
此调用返回读取到缓冲区的字节数,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
buf − 读取信息的缓冲区。
len − 缓冲区的最大长度。
flags − 设置为0。
sendto函数
sendto函数用于通过未连接的数据报套接字发送数据。其签名如下:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
此调用返回发送的字节数,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
msg − 指向要发送数据的指针。
len − 要发送的数据长度(以字节为单位)。
flags − 设置为0。
to − 指向要发送数据的目标主机的sockaddr结构体的指针。
tolen − 设置为sizeof(struct sockaddr)。
recvfrom函数
recvfrom函数用于从未连接的数据报套接字接收数据。
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
此调用返回读取到缓冲区的字节数,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
buf − 读取信息的缓冲区。
len − 缓冲区的最大长度。
flags − 设置为0。
from − 指向要读取数据的目标主机的sockaddr结构体的指针。
fromlen − 设置为sizeof(struct sockaddr)。
close函数
close函数用于关闭客户端和服务器之间的通信。其语法如下:
int close( int sockfd );
此调用在成功时返回0,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
shutdown函数
shutdown函数用于优雅地关闭客户端和服务器之间的通信。与close函数相比,此函数提供了更多控制。以下是shutdown的语法:
int shutdown(int sockfd, int how);
此调用在成功时返回0,否则返回-1。
参数
sockfd − 由socket函数返回的套接字描述符。
how − 放入以下数字之一:
0 − 表示不允许接收,
1 − 表示不允许发送,并且
2 − 表示不允许发送和接收。当how设置为2时,它与close()相同。
select函数
select函数指示哪些指定的 文件描述符已准备好读取、已准备好写入或有错误条件待处理。
当应用程序调用recv或recvfrom时,它会被阻塞,直到该套接字有数据到达。当传入数据流为空时,应用程序可以进行其他有用的处理。另一种情况是当应用程序从多个套接字接收数据时。
在输入队列中没有数据的套接字上调用recv或recvfrom会阻止立即从其他套接字接收数据。select函数调用通过允许程序轮询所有套接字句柄来解决此问题,以查看它们是否可用于非阻塞读写操作。
以下是select的语法:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
此调用在成功时返回0,否则返回-1。
参数
nfds − 指定要测试的文件描述符范围。select()函数测试0到nfds-1范围内的文件描述符。
readfds − 指向fd_set类型的对象,该对象在输入时指定要检查是否准备好读取的文件描述符,在输出时指示哪些文件描述符准备好读取。它可以为NULL,表示空集。
writefds − 指向fd_set类型的对象,该对象在输入时指定要检查是否准备好写入的文件描述符,在输出时指示哪些文件描述符准备好写入。它可以为NULL,表示空集。
exceptfds − 指向fd_set类型的对象,该对象在输入时指定要检查是否有错误条件待处理的文件描述符,在输出时指示哪些文件描述符有错误条件待处理。它可以为NULL,表示空集。
timeout − 指向一个timeval结构体,该结构体指定select调用应轮询描述符以进行可用I/O操作的时间长度。如果超时值为0,则select将立即返回。如果超时参数为NULL,则select将阻塞,直到至少有一个文件/套接字句柄准备好进行可用I/O操作。否则,select将在超时时间过去或至少有一个文件/套接字描述符准备好进行I/O操作后返回。
select的返回值是在文件描述符集中指定准备好进行I/O操作的句柄数。如果超时字段指定的时间限制已到,select返回0。以下宏用于操作文件描述符集:
FD_CLR(fd, &fdset) − 清除文件描述符集中fdset中文件描述符fd的位。
FD_ISSET(fd, &fdset) − 如果文件描述符fd的位在fdset指向的文件描述符集中已设置,则返回非零值;否则返回0。
FD_SET(fd, &fdset) − 在文件描述符集fdset中设置文件描述符fd的位。
FD_ZERO(&fdset) − 将文件描述符集fdset初始化为所有文件描述符的位都为零。
如果 fd 参数小于 0 或大于等于 FD_SETSIZE,则这些宏的行为未定义。
示例
fd_set fds; struct timeval tv; /* do socket initialization etc. tv.tv_sec = 1; tv.tv_usec = 500000; /* tv now represents 1.5 seconds */ FD_ZERO(&fds); /* adds sock to the file descriptor set */ FD_SET(sock, &fds); /* wait 1.5 seconds for any data to be read from any single socket */ select(sock+1, &fds, NULL, NULL, &tv); if (FD_ISSET(sock, &fds)) { recvfrom(s, buffer, buffer_len, 0, &sa, &sa_len); /* do something */ } else { /* do something else */ }