嘉鱼网站建设多少钱,网站建设与管理课程标准,centos安装wordpress,网站上线流程图一、TCP服务端
创建一个TCP服务器的基本操作#xff1a;
创建一个套接字#xff08;socket#xff09;#xff1a;使用socket函数绑定套接字#xff08;socket#xff09;:将套接字绑定到一个特定的IP地址和端口号上#xff0c;这些信息要用结构体sockaddr_in来保存监…一、TCP服务端
创建一个TCP服务器的基本操作
创建一个套接字socket使用socket函数绑定套接字socket:将套接字绑定到一个特定的IP地址和端口号上这些信息要用结构体sockaddr_in来保存监听请求连接使用listen函数接受连接使用accept函数来实现发送和接受信息一旦建立了连接服务器和客户端都可以使用套接字的send()和recv()方法来发送和接收数据。发送方使用send()方法将数据发送到套接字而接收方使用recv()方法从套接字接收数据。关闭连接当通信完成后可以调用套接字的close()方法来关闭连接。这将释放套接字占用的资源并终止连接。
1.socket创建套接字
函数原型
int socket(int domain, int type, int protocol);参数说明
domain指定地址族可以是AF_INET用于IPv4或AF_INET6用于IPv6。type指定套接字类型可以是SOCK_STREAM用于TCP或SOCK_DGRAM用于UDP。protocol一般为0表示使用默认的协议。
返回值 如果成功返回一个非负整数表示套接字文件描述符socket file descriptor。如果失败返回-1并设置全局变量errno来指示错误类型。
2. sockaddr_insockaddr_in 是一个用于表示 IPv4 地址的结构体它定义在 netinet/in.h 头文件中。
定义如下
struct sockaddr_in {sa_family_t sin_family; // 地址族一般为 AF_INETin_port_t sin_port; // 端口号struct in_addr sin_addr; // IPv4 地址char sin_zero[8]; // 用于补齐一般设置为全0
};其中sa_family_t 和 in_port_t 是整数类型struct in_addr 是一个用于存储 IPv4 地址的结构体。sin_family 表示地址族一般为 AF_INET表示使用 IPv4 地址。sin_port 表示端口号用于标识网络中的应用程序。sin_addr 存储了 IPv4 地址的信息。sin_zero 是一个用于补齐的字段一般设置为全0。
in_addrin_addr 是一个用于存储 IPv4 地址的结构体它定义在 netinet/in.h 头文件中。
它的定义如下
struct in_addr {in_addr_t s_addr; // IPv4 地址
};其中in_addr_t 是一个无符号整数类型用于存储 IPv4 地址。
3. htonshtons 是一个用于将主机字节序Host Byte Order转换为网络字节序即大端存储的函数。它定义在 arpa/inet.h 头文件中并且接受一个参数表示要转换的 16 位无符号整数。
函数原型如下
uint16_t htons(uint16_t hostshort);4.bindbind 是一个用于将一个套接字socket与一个特定的地址包括 IP 地址和端口号绑定的函数。它通常用于服务器端在监听连接之前将套接字绑定到一个特定的地址上。
在 C 语言中bind 函数的原型如下
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数说明
sockfd 是要绑定的套接字的文件描述符。addr 是一个指向 sockaddr 结构体的指针用于指定要绑定的地址信息。addrlen 是 addr 所指向的结构体的长度。
返回值
如果 bind 函数执行成功返回值为 0。这意味着套接字成功地与指定的地址绑定在一起。如果 bind 函数执行失败返回值为 -1。这表示绑定操作未成功。
5.listenlisten 函数用于将一个已绑定的套接字socket设置为监听状态以便接受传入的连接请求
在 C 语言中listen 函数的原型如下
int listen(int sockfd, int backlog);参数说明
sockfd要设置为监听状态的套接字的文件描述符。backlog定义在连接队列中等待被接受的连接的最大数量。
返回值
如果 listen 函数执行成功返回值为 0。这意味着套接字已成功设置为监听状态并且可以开始接受传入的连接请求。如果 listen 函数执行失败返回值为 -1。这表示设置监听状态的操作未成功。
6.acceptaccept 函数用于从已监听的套接字中接受一个传入的连接请求并创建一个新的套接字来处理该连接。
在 C 语言中accept 函数的原型如下
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);参数说明
sockfd已监听的套接字的文件描述符。addr指向一个 struct sockaddr 结构的指针用于存储接受连接的远程地址信息。addrlen指向一个 socklen_t 类型的变量表示 addr 的大小。
返回值
如果 accept 函数执行成功返回值为新创建的套接字的文件描述符。这个新套接字用于与客户端进行通信。如果 accept 函数执行失败返回值为 -1。这表示接受连接请求的操作未成功。
7.recvrecv 函数用于从已连接的套接字接收数据。它是在已建立连接的套接字上进行数据交换的常用函数之一。
在 C 语言中recv 函数的原型如下
ssize_t recv(int sockfd, void *buf, size_t len, int flags);参数含义
sockfd已连接的套接字的文件描述符。buf指向接收数据的缓冲区的指针。len缓冲区的大小即要接收的最大字节数。flags可选的标志参数用于控制接收操作的行为。
返回值 recv 函数会阻塞程序执行直到有数据到达为止。当有数据到达时recv 函数会将数据从套接字读取到指定的缓冲区 buf 中并返回实际接收到的字节数。如果返回值为 0表示对端已关闭连接。如果返回值为 -1表示接收数据的操作未成功。
8.创建服务器实例
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include string.h
#include stdlib.h
#include unistd.h
#include netinet/in.h
#include arpa/inet.hint main()
{//创建socket,参数说明//指定协议族AF_INETIPv4和 AF_INET6IPv6//指定套接字使用的协议通常为0表示根据 domain 和 type 自动选择合适的协议。int sockfd socket(AF_INET, SOCK_STREAM, 0); if(-1 sockfd){ perror(socket);exit(1);} //绑定//sockaddr_in 是一个用于表示 IPv4 地址的结构体struct sockaddr_in server_info; //用于保存服务器的信息IP PROTbzero(server_info, sizeof(struct sockaddr_in)); //清空bzero() 是一个函数用于将指定内存区域的前几个字节设置为零server_info.sin_family AF_INET; //地址族server_info.sin_port htons(7000); //端口大于1024都行
// server_info.sin_addr.s_addr inet_addr(127.0.0.1); //表示回环IP用于测试server_info.sin_addr.s_addr inet_addr(192.168.175.129);if(bind(sockfd, (struct sockaddr *)server_info, sizeof(server_info)) -1) { perror(bind);exit(2);}//设置监听队列if(listen(sockfd, 10) -1){perror(listen);exit(3);}printf(等待客户端的连接...\n);//接受连接 struct sockaddr_in client_info;int length sizeof(client_info);int fd accept(sockfd, (struct sockaddr *)client_info, length);if(-1 fd){perror(accept);exit(4);}printf(接受客户端的连接 %d\n, fd);char buf[1024] {0};ssize_t size;while(1){size recv(fd, buf, sizeof(buf), 0);if(size -1){perror(recv);break;}else if(size 0)break;if(!strcmp(buf, bye))break;printf(%s\n, buf);bzero(buf, 1024);}close(fd); //关闭TCP连接不能在接受数据close(sockfd); //关闭socket不能再处理客户端的请求//socket用于处理客户端的连接fd用于处理客户端的消息return 0;
}
二、TCP的客户端
客户端连接服务端的基本操作
创建socket发起连接发送信息
sendsend 是一个函数用于在一个已连接的套接字上发送数据。
函数原型如下
ssize_t send(int sockfd, const void *buf, size_t len, int flags);参数说明
sockfd已连接的套接字描述符。buf指向要发送数据的缓冲区的指针。len要发送的数据的长度以字节为单位。flags可选的标志参数用于控制发送操作的行为。
返回值 函数返回一个 ssize_t 类型的值表示实际发送的字节数。如果发送失败返回值为 -1并且可以通过检查全局变量 errno 来获取错误代码。
创建客户端实例
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include stdlib.h
#include unistd.hint main()
{//创建socketint sockfd socket(AF_INET, SOCK_STREAM, 0);if(-1 sockfd){perror(socket);exit(1);}//发起连接struct sockaddr_in server_info; // 存储服务器信息bzero(server_info, 0); server_info.sin_family AF_INET; //地址族server_info.sin_port htons(7000); //绑定端口server_info.sin_addr.s_addr inet_addr(192.168.175.129); //绑定ip地址if(connect(sockfd, (struct sockaddr*)server_info, sizeof(server_info)) -1){perror(connect);exit(2);}//发送信息char buf[1024] {0};while(1){scanf(%s, buf);if(send(sockfd, buf, sizeof(buf), 0) -1){perror(send);exit(3);}if(!strcmp(buf, bye))break;bzero(buf, sizeof(buf));}close(sockfd);return 0;
}
三、TCP并发服务器
使用多线程来实现响应多个客户端
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include string.h
#include stdlib.h
#include unistd.h
#include netinet/in.h
#include arpa/inet.h
#include pthread.hvoid *client_recv(void *arg)
{int fd *(int *)arg;char buf[1024] {0};ssize_t size;while(1){ size recv(fd, buf, sizeof(buf), 0); if(size -1) {perror(recv);break;}else if(size 0)break;if(!strcmp(buf, bye))break;printf(%s\n, buf);bzero(buf, 1024);}printf(客户端 %d 退出\n, fd);close(fd);
}int main()
{//创建socket,参数说明//指定协议族AF_INETIPv4和 AF_INET6IPv6//指定套接字使用的协议通常为0表示根据 domain 和 type 自动选择合适的协议。int sockfd socket(AF_INET, SOCK_STREAM, 0);if(-1 sockfd) {perror(socket);exit(1);} //绑定//sockaddr_in 是一个用于表示 IPv4 地址的结构体struct sockaddr_in server_info; //用于保存服务器的信息IP PROTbzero(server_info, sizeof(struct sockaddr_in)); //清空bzero() 是一个函数用于将指定内存区域的前几个字节设置为零server_info.sin_family AF_INET; //地址族server_info.sin_port htons(7000); //端口大于1024都行
// server_info.sin_addr.s_addr inet_addr(127.0.0.1); //表示回环IP用于测试server_info.sin_addr.s_addr inet_addr(192.168.175.129);if(bind(sockfd, (struct sockaddr *)server_info, sizeof(server_info)) -1){perror(bind);exit(2);}//设置监听队列if(listen(sockfd, 10) -1){perror(listen);exit(3);}printf(等待客户端的连接...\n);//接受连接struct sockaddr_in client_info;int length sizeof(client_info);int fd;while(1){fd accept(sockfd, (struct sockaddr *)client_info, length);if(-1 fd){perror(accept);exit(4);}printf(接受客户端的连接 %d\n, fd);//为每个客户端创建一个线程pthread_t tid;if(pthread_create(tid, NULL, client_recv, fd) ! 0){perror(pthread_create);exit(4); }pthread_detach(tid);}// close(fd); //关闭TCP连接不能在接受数据close(sockfd); //关闭socket不能再处理客户端的请求//socket用于处理客户端的连接fd用于处理客户端的消息return 0
}四、服务器转发
服务器代码
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include string.h
#include stdlib.h
#include unistd.h
#include netinet/in.h
#include arpa/inet.h
#include pthread.htypedef struct Info
{char text[1024];int tofd;
}Info;void *client_recv(void *arg)
{int fd *(int *)arg;Info buf;ssize_t size;while(1){ size recv(fd, buf, sizeof(buf), 0); if(size -1) { perror(recv);break;} else if(size 0)break;if(!strcmp(buf.text, bye))break;//转发数据if(send(buf.tofd, buf, size, 0) -1){perror(send);break;}bzero(buf, sizeof(buf));}printf(客户端 %d 退出\n, fd);close(fd);
}int main()
{ //创建socket,参数说明//指定协议族AF_INETIPv4和 AF_INET6IPv6//指定套接字使用的协议通常为0表示根据 domain 和 type 自动选择合适的协议。int sockfd socket(AF_INET, SOCK_STREAM, 0);if(-1 sockfd){ perror(socket);exit(1);} //绑定//sockaddr_in 是一个用于表示 IPv4 地址的结构体struct sockaddr_in server_info; //用于保存服务器的信息IP PROTbzero(server_info, sizeof(struct sockaddr_in)); //清空bzero() 是一个函数用于将指定内存区域的前几个字节设置为零server_info.sin_family AF_INET; //地址族server_info.sin_port htons(7000); //端口大于1024都行
// server_info.sin_addr.s_addr inet_addr(127.0.0.1); //表示回环IP用于测试server_info.sin_addr.s_addr inet_addr(192.168.175.129);if(bind(sockfd, (struct sockaddr *)server_info, sizeof(server_info)) -1){ perror(bind);exit(2);}//设置监听队列if(listen(sockfd, 10) -1){perror(listen);exit(3);}printf(等待客户端的连接...\n);//接受连接struct sockaddr_in client_info;int length sizeof(client_info);int fd;while(1){ fd accept(sockfd, (struct sockaddr *)client_info, length);if(-1 fd){perror(accept);exit(4);}printf(接受客户端的连接 %d\n, fd);//为每个客户端创建一个线程pthread_t tid;if(pthread_create(tid, NULL, client_recv, fd) ! 0){perror(pthread_create);exit(4);}pthread_detach(tid);} // close(fd); //关闭TCP连接不能在接受数据close(sockfd); //关闭socket不能再处理客户端的请求//socket用于处理客户端的连接fd用于处理客户端的消息return 0;客户端代码
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include string.h
#include stdlib.h
#include unistd.h
#include pthread.htypedef struct Info
{char text[1024]; int tofd;
}Info;
pthread_t tid[2] {0};void *send_thread(void *arg)
{int sockfd *(int *)arg;Info buf;while(1){ scanf(%s%d, buf.text, buf.tofd);if(send(sockfd, buf, sizeof(buf), 0) -1) {perror(send);break;}if(!strcmp(buf.text, bye))break;bzero(buf, sizeof(buf));}}void *recv_thread(void *arg)
{int sockfd *(int *)arg;Info buf;ssize_t size;while(1){size recv(sockfd, buf, sizeof(buf), 0);if(size -1){perror(recv);break;}else if(size 0)break; printf(%s\n, buf.text);bzero(buf, sizeof(buf));}
} int main()
{ //创建socketint sockfd socket(AF_INET, SOCK_STREAM, 0);if(-1 sockfd){perror(socket);exit(1);}//发起连接struct sockaddr_in server_info; // 存储服务器信息bzero(server_info, 0);server_info.sin_family AF_INET; //地址族server_info.sin_port htons(7000); //绑定端口server_info.sin_addr.s_addr inet_addr(192.168.175.129); //绑定ip地址if(connect(sockfd, (struct sockaddr*)server_info, sizeof(server_info)) -1){ perror(connect);exit(2);} //启动2个线程一个负责发送一个负责接收if(pthread_create(tid[0], NULL, send_thread, sockfd) ! 0){ perror(pthread_create);exit(3);}if(pthread_create(tid[1], NULL, recv_thread, sockfd) ! 0){perror(pthread_create)exit(4);}void *status;pthread_join(tid[0], status);pthread_join(tid[1], status);close(sockfd);return 0;
}
五、UDP服务端
创建UDP服务器的步骤
创建socket绑定端口、地址等
代码
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include stdlib.h
#include string.h
#include unistd.hint main()
{//创建socketint sockfd socket(AF_INET, SOCK_DGRAM, 0); if(-1 sockfd){ perror(socket);exit(1);} //绑定struct sockaddr_in server_info;bzero(server_info, sizeof(server_info));server_info.sin_family AF_INET;server_info.sin_port htons(6000);server_info.sin_addr.s_addr inet_addr(127.0.0.1);if(bind(sockfd, (struct sockaddr *)server_info, sizeof(server_info)) -1) { perror(bind);exit(2);} ssize_t size;char buf[1024] {0};struct sockaddr_in client_info;int len sizeof(client_info);while(1){size recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)client_info, len);if(-1 len){perror(recvfrom);break;}printf(收到 %s : %d 的数据 %s\n, inet_ntoa(client_info.sin_addr), client_info.sin_port, buf);bzero(buf, 1024);}close(sockfd);return 0;
}
六、UDP客户端
#include stdio.h
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include sys/types.h
#include stdlib.h
#include string.h
#include unistd.hint main()
{int sockfd socket(AF_INET, SOCK_DGRAM, 0); if(sockfd -1) { perror(socket);exit(1);} char buf[1024] {0};struct sockaddr_in server_info;bzero(server_info, sizeof(server_info));server_info.sin_family AF_INET;server_info.sin_port htons(6000);server_info.sin_addr.s_addr inet_addr(127.0.0.1);while(1){ scanf(%s, buf);ssize_t size sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)server_info, sizeof(server_info));if(-1 size){perror(send);break;}bzero(buf, 1024);}close(sockfd);return 0;
}
七、高并发服务器selectselect 是一个在 C 语言中使用的系统调用用于实现 I/O 多路复用。它可以同时监视多个文件描述符以确定它们中是否有可读、可写或异常等事件发生。
使用 select 的一般步骤如下
创建并初始化 fd_set 集合设置要监视的文件描述符。调用 select 函数传入需要监视的文件描述符集合和超时时间。检查返回值判断哪些文件描述符就绪。根据就绪的文件描述符进行相应的操作。
select
select函数的原型
#include sys/select.h
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);参数说明
nfds监视的文件描述符的最大值加 1。readfds指向一个 fd_set包含要监视可读事件的文件描述符。如果为NULL代表不监视下2同。writefds指向一个 fd_set包含要监视可写事件的文件描述符。exceptfds指向一个 fd_set包含要监视异常事件的文件描述符。timeout指向一个 struct timeval 结构体用于设置超时时间。如果为 NULL则 select 将一直阻塞直到有事件发生。
返回值
如果有事件发生或超时select 返回就绪文件描述符的数量。如果出错返回 -1并设置 errno。
fd_set
概念 fd_set 是一个集合数据结构。它用于在 C 语言中表示一组文件描述符file descriptor的集合。 fd_set 是一个位向量bit vector用于表示文件描述符的集合。它通常是一个固定大小的数组每个元素都是一个位字段bit field用于表示一个文件描述符的状态。每个位对应一个文件描述符如果该位为 1则表示对应的文件描述符在集合中如果该位为 0则表示对应的文件描述符不在集合中。
fd_set 的一些常用操作和相关的函数
FD_ZERO(fd_set *set)将 fd_set 初始化为空集合即清除所有的位。FD_SET(int fd, fd_set *set)将指定的文件描述符 fd 添加到 fd_set 中即将对应的位设置为 1。FD_CLR(int fd, fd_set *set)从 fd_set 中移除指定的文件描述符 fd即将对应的位清除为 0。FD_ISSET(int fd, fd_set *set)检查指定的文件描述符 fd 是否在 fd_set 中即对应的位是否为 1。FD_COPY(fd_set *src, fd_set *dest)复制一个 fd_set将 src 中的位复制到 dest 中。
代码实例
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include stdlib.h
#include sys/select.h
#include sys/time.h
#include unistd.h
#include string.hint main()
{int sockfd socket(AF_INET, SOCK_STREAM, 0); if(-1 sockfd){ perror(socket);exit(1);} struct sockaddr_in server_info;bzero(server_info, sizeof(server_info));server_info.sin_family AF_INET;server_info.sin_port htons(7000);server_info.sin_addr.s_addr inet_addr(192.168.175.129);if(bind(sockfd, (struct sockaddr *)server_info, sizeof(server_info)) -1) { perror(bind);exit(2);} if(listen(sockfd, 10) -1) { perror(listen);exit(3);}printf(等待客户端的连接...\n);//定义两个集合fd_set readset, tmpset;//初始化FD_ZERO(readset);FD_ZERO(tmpset);//添加文件描述符FD_SET(sockfd, readset);int maxfd sockfd;int fd[1024] {0};int ret, i;while(1){tmpset readset;ret select(maxfd 1, tmpset, NULL, NULL, NULL);if(-1 ret){perror(select);break;}if(FD_ISSET(sockfd, tmpset)) //判断sockfd是否留在集合中是否有客户端发起连接{struct sockaddr_in client_info; //用于保存客户端信息int length sizeof(client_info);//找到一个没有分配的fdfor(i 0; i 1024; i){if(0 fd[i])break;}fd[i] accept(sockfd, (struct sockaddr *)client_info, length);if(-1 fd[i]){perror(accept);break;}printf(接受客户端的连接%d\n, fd[i]);//把新的文件描述符加入集合中FD_SET(fd[i], readset);//更新最大文件描述符if(maxfd fd[i])maxfd fd[i];}else{for(i 0; i 1024; i){if(FD_ISSET(fd[i], tmpset)){char buf[1024] {0};ssize_t size;size recv(fd[i], buf, sizeof(buf), 0);if(size -1){perror(recv);}else if(size 0){printf(客户端 %d 退出\n, fd[i]);FD_CLR(fd[i], readset); // 从集合中删掉close(fd[i]);fd[i] 0;}elseprintf(%s\n, buf);break;}}}}close(sockfd);return 0;
}
八、高并发服务器epoll
概念 epoll 是一个在 Linux 操作系统上用于高效事件通知的 I/O 多路复用机制。它是一种替代传统的 select 和 poll 函数的方法可以在大规模并发连接的网络服务器中提供更高的性能。
使用 epoll 的一般步骤如下
创建一个 epoll 实例
int epoll_create(int size);这个函数会返回一个文件描述符用于后续的 epoll 操作。
向 epoll 实例中添加文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epfd 是 epoll 实例的文件描述符op 是操作类型如添加、修改或删除fd 是要添加的文件描述符event 是一个 epoll_event 结构体用于指定事件类型和关联的数据
等待事件的发生
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);epfd 是 epoll 实例的文件描述符events 是一个数组用于存储发生的事件maxevents 是数组的大小timeout 是等待的超时时间-1 表示无限等待。 处理发生的事件 遍历 epoll_wait 返回的事件数组根据事件类型进行相应的处理。 关闭 epoll 实例
int close(int fd);1.epoll_create
概念 epoll_create 函数用于创建一个 epoll 实例并返回一个文件描述符以便后续的 epoll 操作。 它的函数原型如下
int epoll_create(int size);参数说明
size指定 epoll 实例的大小它是一个整数通常可以设置为大于 0 的任意值。这个参数在较早的内核版本中被忽略可以将其设置为 0。
返回值
如果成功返回一个非负整数表示 epoll 实例的文件描述符epoll 文件描述符。如果出错返回 -1并设置 errno。
2.epoll_event
epoll_event 是一个结构体类型在 Linux 中使用 epoll 进行 I/O 多路复用时用于表示事件的数据结构。 它的定义如下
struct epoll_event {uint32_t events; // 表示事件类型的位掩码epoll_data_t data; // 与事件相关的用户数据
};events一个无符号 32 位整数用于表示事件类型的位掩码。可以使用以下常量进行设置 EPOLLIN表示可读事件有数据可读。EPOLLOUT表示可写事件可以写入数据。EPOLLRDHUP表示对端关闭连接或关闭了写入一半的连接。EPOLLERR表示错误事件。EPOLLHUP表示挂起事件连接被挂起。EPOLLET使用边缘触发模式Edge-Triggered。EPOLLONESHOT一次性事件只会触发一次。默认水平触发 data一个 epoll_data_t 类型的联合体用于存储与事件相关的用户数据。epoll_data_t 的定义如下
typedef union epoll_data {void *ptr; // 指针类型的用户数据int fd; // 文件描述符类型的用户数据uint32_t u32; // 32 位无符号整数类型的用户数据uint64_t u64; // 64 位无符号整数类型的用户数据
} epoll_data_t;3.epoll_ctl
epoll_ctl 是 Linux 中使用 epoll 进行事件注册和控制的系统调用。它用于向 epoll 实例中添加、修改或删除事件。 epoll_ctl 的原型如下
#include sys/epoll.h
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);参数说明
epfdepoll 实例的文件描述符即 epoll 文件描述符通过 epoll_create 创建并返回。op操作类型可以是以下值之一 EPOLL_CTL_ADD向 epoll 实例中添加事件。EPOLL_CTL_MOD修改 epoll 实例中的事件。EPOLL_CTL_DEL从 epoll 实例中删除事件。 fd要操作的文件描述符用于指定要添加、修改或删除的事件所属的文件描述符。event指向 struct epoll_event 结构体的指针用于指定要添加、修改或删除的事件的详细信息。
返回值
如果成功返回 0。如果出错返回 -1并设置 errno。
4.epoll_wait
epoll_wait 是 Linux 中使用 epoll 进行事件等待和处理的系统调用。它用于等待 epoll 实例中注册的事件发生并返回就绪的事件信息。 epoll_wait 的原型如下
#include sys/epoll.h
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);参数说明
epfdepoll 实例的文件描述符即 epoll 文件描述符通过 epoll_create 创建并返回。events指向 struct epoll_event 数组的指针用于存储就绪的事件信息。maxeventsevents 数组的大小即最多可以存储多少个事件信息。timeout等待的超时时间以毫秒为单位可以设置为以下值之一 -1无限等待直到有事件发生。0立即返回不等待任何事件。大于 0等待指定的毫秒数后返回如果没有事件发生则超时。
返回值
如果成功返回就绪的事件数量。如果超时返回 0。如果出错返回 -1并设置 errno。
5.代码实例
#include stdio.h
#include sys/types.h /* See NOTES */
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include stdlib.h
#include string.h
#include unistd.h
#include sys/epoll.hint main()
{int sockfd socket(AF_INET, SOCK_STREAM, 0);if(-1 sockfd){perror(socket);exit(1);}struct sockaddr_in server_info;bzero(server_info, sizeof(server_info));server_info.sin_family AF_INET;server_info.sin_port htons(7000);server_info.sin_addr.s_addr inet_addr(192.168.175.129);if(bind(sockfd, (struct sockaddr *)server_info, sizeof(server_info)) -1){perror(bind);exit(2);}if(listen(sockfd, 10) -1){perror(listen);exit(3);}printf(等待客户端连接...\n);//创建epoll对象创建集合int epfd epoll_create(1);if(-1 epfd){perror(epoll_create);exit(4);}//把sockfd封装成事件添加到集合中struct epoll_event ev;ev.events EPOLLIN | EPOLLET; //事件属性边缘触发ev.data.fd sockfd;if(epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, ev) -1){perror(epoll_ctl);exit(5);}while(1){struct epoll_event events[10];int num epoll_wait(epfd, events, 10, -1);if(-1 num){perror(epoll_wait);break;}for(int i 0; i num; i){if(events[i].data.fd sockfd) //有客户端发起连接{struct sockaddr_in server_info;int length sizeof(server_info);int fd accept(sockfd, (struct sockaddr *)server_info, length);if(-1 fd){perror(accept);break;}printf(客户端 %d 连接成功\n, fd);ev.events EPOLLIN; //默认水平触发ev.data.fd fd;if(epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev) -1){perror(epoll_ctl);break;}}else{char buf[1024] {0};ssize_t size;size recv(events[i].data.fd, buf, sizeof(buf), 0);if(size -1)perror(recv);else if(size 0){if(epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, events[i]) -1){perror(epoll_ctl);exit(5);}close(events[i].data.fd);printf(客户端 %d 退出\n, events[i].data.fd);}elseprintf(%s\n, buf);}}}close(sockfd);return 0;
}
九、并发服务器的总结