在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket。
在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成 的socket pair就唯一标识一个连接。
socket本⾝身有“插座”的意思,因此用来描述网络连接的一 对一关系。
网络数据流的地址规定:先发出的数据是低地址,后发出的数据是高地址。TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
在socket中几个比较重要的函数:
(1)网络字节序和主机字节序的转换:
#include <arpa/inet.h>
//h表示host, n表示network, l表示32位长整数,s表示16位短整数
uint32_t htonl(uint32_t hostlong);
//表示将32位的长整数从主机字节序转换为网络字节序
//例如将IP地址转换后准备发送,如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
sockaddr数据结构:
struct sockaddr_in
{
sa_family_t sin_family; /* Address family*/
__be16 sin_port; /* Port number*/
struct in_addr sin_addr; /* Internet address */
}
/* Internet address. */
struct in_addr
{
__be32 s_addr;
};
(2) 创建套接字:
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序 可以像读写⽂文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对 于 IPv4,family参数指定为AF_INET;
对于TCP协议,type参数指定为SOCK_STREAM,表示⾯面向 流的传输协议。
如果是UDP协议,则type参数指定为SOCK_DGRAM,表示⾯面向数据报的传输 协 议。
protocol参数指定为0即可。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
(3) 绑定套接字:
服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址 和端口号。bind()成功返回0,失败返回-1。
bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号。前面讲过,struct sockaddr *是一个通用指针类型,myaddr参 数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参 数 addrlen指定结构体的长度
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
(4)listen监听状态:
服务器调用的accept() 返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept 的客户 端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略。listen()成功返回0,失败返回-1。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
> (5)接受套接字
三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接 请求,就阻塞等待直到有客户端连接上来。
cliaddr是一个输出型参数,accept()返回时传出客户端
的地址和端口号。
addrlen参数是一个输入输出型参数,传入的是调用者提 供的 缓冲区cliaddr 的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调⽤用者提供的缓冲区)。如果给cliaddr 参数传NULL,表⽰示不关心客户端的地 址。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
> (6)连接套接字
客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是 自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1。
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int</