在实现多路io之前,首先要创建套接字
int socket(int domain, int type, int protocol)
domain:指定使用的协议族。
常见选项包括:
AF_INET:IPv4协议(用于互联网通信)。
AF_INET6:IPv6协议。
AF_UNIX:本地通信(同一台主机上的进程间通信)。
type(套接字类型):指定套接字的类型。
常见选项包括:SOCK_STREAM:提供面向连接的可靠数据传输服务,使用TCP协议。
SOCK_DGRAM:提供无连接的、不可靠的数据传输服务,使用UDP协议。
SOCK_RAW:提供原始套接字,允许直接操作网络层。
protocol(协议):指定使用的协议。通常可以设置为 0 以自动选择合适的协议。
protocol: 通常为0,表示使用默认协议
返回值就是一个套接字
这个套接字可以用于服务端接收客户端的连接
之后套接字需要bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:套接字文件描述符,通常是由 socket() 函数创建的套接字。
addr:一个指向 sockaddr 结构体的指针,包含要绑定的地址信息(如 IP 地址和端口号)。在 IPv4 中,通常使用的是 sockaddr_in 结构体。
addrlen:地址长度,通常使用 sizeof(struct sockaddr_in) 来设置。
sockaddr:
这个是通用的,而·sockaddr_in是专门为ipv4实现的,传入时可以通过(struct sockaddr*)强制转换传入
struct sockaddr_in {
sa_family_t sin_family; // 地址族(AF_INET,IPv4协议)
in_port_t sin_port; // 端口号(需要用 htons()(传入数字,最好大于1024) 转换为网络字节序)
struct in_addr sin_addr; // IPv4地址
char sin_zero[8]; // 填充字段,使结构体大小与 sockaddr
一致 };
最后listen开启
listen(sockfd,10);
sockfd为前面创建的socket,10就是能排队等待处理的最大数量
select:
函数详解:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds: 所有文件描述符中最大值加 1。
(因为for(i=0;i<nfds;i++),历史保留原因)
readfds: 用于检测可读事件的文件描述符集合。
writefds: 用于检测可写事件的文件描述符集合。
exceptfds: 用于检测异常事件的文件描述符集合。
timeout: 超时时间,可以指定等待的时间长度。若为 NULL,select() 将会无限等待,达到阻塞的效果。
返回值:select() 返回就绪的文件描述符数量,若返回 0 表示超时。
fd_set:套接字的集合
FD_ZERO(fd_set *set):将 fd_set 结构体清空,初始化为空集合(注意里面参数是一个指针)
int FD_ISSET(int fd, fd_set *set):检查一个文件描述符是否在 fd_set 集合中处于就绪状态,返回非 0 表示文件描述符在集合中。(注意里面参数是一个指针)(不是 是否在里面,而是 是否就绪,即有任务)
FD_SET(int fd, fd_set *set):将一个文件描述符加入到 fd_set 集合中。
当有客户端连接时,前面创建的sockfd就会处于活跃状态,此时要是select最后一个参数为null,select就会停止阻塞,通过if(FD_ISSET(sockfd,fd))判断.
之后通过accept函数接受连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd: 监听套接字文件描述符(通常是通过 socket() 和 bind() 创建并调用 listen() 后得到的)。
addr: 指向 sockaddr 结构体的指针,用于存储客户端的地址信息。该结构体会保存客户端的 IP 地址和端口信息。
addrlen: 指定 addr 的大小,返回时会填充客户端地址的大小。
返回值:返回新套接字的文件描述符,该文件描述符用于与客户端通信。返回 -1 表示失败,并且设置 errno 错误码。
未accept的sockfd,即未处理消息,select会显示一直有任务就不会等待
accept返回的新值就可以使用FD_SET将其加入到fd集合当中,一般来说,accept返回的套接字是依次递增的
通过recv接收消息
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd: 套接字文件描述符,表示从哪个套接字接收数据。该套接字必须是已经通过 connect()(客户端)或者 accept()(服务器)建立的连接。
buf: 指向存储接收数据的缓冲区的指针。接收到的数据将存储在该缓冲区中。(可以为char数组)
len: 缓冲区的大小,指定最多可以接收的字节数。
flags: 操作标志,用来控制函数的行为。常用的标志包括:
0: 默认标志,阻塞接收数据。
MSG_DONTWAIT: 非阻塞模式,立即返回,无论是否有数据可读取。
MSG_PEEK: 检查数据但不从队列中删除数据,之后的 recv 调用仍然可以读取相同的数据。
MSG_WAITALL: 等待所有请求的字节数被接收到,除非遇到错误或连接关闭。
返回值
成功时: 返回接收的字节数。
失败时: 返回 -1,并设置 errno 变量来标记具体的错误原因。
连接关闭时: 如果对方关闭了连接,返回 0,表示已没有数据可接收
poll
poll为select的改进版,但还是和select一样需要每次遍历所有已经建立连接的sockfd.
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds:指向 struct pollfd 结构体数组的指针,包含需要监控的文件描述符及其事件。
nfds:表示数组中元素的个数,即需要监控的文件描述符的数量。
timeout:表示超时时间(毫秒),若为 -1 则会无限等待,直到有事件发生。
poll使用struct pollfd数组来监听套接字sockfd,创建一个数组后,每次有新连接就让其在数组的下标与本身的值对应,方便操作
struct pollfd {
int fd; // 文件描述符
short events; // 监控的事件
short revents; // 套接字激活后会返回事件 };
fd:套接字
events 和 revents:
POLLIN:表示文件描述符上有数据可读。
POLLOUT:表示文件描述符上可以写入数据。
POLLERR:表示发生错误。
POLLHUP:表示挂起(通常表示对端关闭连接)。
建立新连接即sockfd(用于监听是否有新连接)对应的struct pollfd的revent为POLLIN;
accept和recv和上面一样
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。