当客户端输入为quit时当前客户端退出。
(作业要求:做作业的时候不要再翻看视频上的教程,对函数理解不明白的全部通过man手册去查看,
自己思考框架,使用makefile编译,然后将测试的记录和结果添加到readme.txt文件中提交上来,代码实现完成测试通过后
再提交作业,网络部分学习不写代码不测试看不出问题的,良好的习惯帮助你们快速成长。)
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/select.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" int func_recall(int arg); char botton[64]; int main() { int fd = -1; int fdbox[1024], fd_count = 0;; int result = 0, i = 0; struct sockaddr_in mysockaddr; struct sockaddr_in clientscokaddr; socklen_t clientaddrlen; static pthread_t nptd; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*优化4: 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); /*2. 绑定 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号 #if 1 //mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY); #else result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr); if (result != 1) { perror("inet_pton"); return -1; } #endif /*2.2 绑定 */ result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("bind"); return -1; } /*3. 调用listen()把主动套接字变成被动套接字 */ result = listen(fd, 5); if (result != 0) { perror("listen"); return -1; } /*4. 等待客户端连接请求 */ /* 优化3. 采用多路复用的方式防止阻塞*/ fd_set rset; int fd1; fd1 = fd+1; fd_count = 1; fdbox[0] = fd; struct timeval tm; //非阻塞状态 采用轮询方式 tm.tv_sec = 0; tm.tv_usec = 0; int fd_client; while(1){ //清零rset FD_ZERO(&rset); //置位rset 标识出要监控的文件标识符 for (i = 0; i < fd_count; i++) { FD_SET(fdbox[i], &rset); } //查询是否有标识符被激活 result = select(fd1, &rset, NULL, NULL, &tm); if (result < 0) { perror("select"); return 0; } else if (result > 0) { //socket fd被激活 接收到新的客户端连接请求 if (FD_ISSET(fdbox[0], &rset)) { bzero((void*)&clientscokaddr, sizeof(clientscokaddr)); fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen); if (-1 == result) { perror("accpet"); return 0; } char ipv4_addr[16]; if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) { perror("inet_ntop"); return -1; } printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port)); //将新建立连接的fd写入数组 以供select调用 fdbox[fd_count] = fd_client; fd_count++; fd1++; } //客户端fd被激活 处理客户端发来信息 for (i = 1; i < fd_count; i++) { if (FD_ISSET(fdbox[i], &rset)){ result = func_recall(fdbox[i]); if (result != 0) { printf("error:func_recall"); return -1; } } } } sleep(1); } close(fd); return 0; } int func_recall(int arg) { int result = 0; int newfd = arg; char Quit[] = "quit"; printf("handler thread: newfd =%d\n", newfd); //..和newfd进行数据读写 char buf[BUFSIZ]; bzero(buf, BUFSIZ); do { result = read(newfd, buf, BUFSIZ - 1); } while (result < 0 && EINTR == errno); if (result < 0) { perror("read"); return -1; } if (!result) return 0; if (!strcmp(Quit, buf)) { printf("client quit...close socket.\n "); close(newfd); } else { printf("message is:%s\n", buf); } return 0; }
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; char MESSAGE[] = "hELLo wOLd...\n"; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT); #if 0 mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); #else result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr); if (result != 1) { perror("inet_pton"); return -1; } #endif result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("connect"); return -1; } printf("Client staring...OK!\n"); while (1) { do { result = write(fd, (void*)MESSAGE, sizeof(MESSAGE)); } while (result < 0 && EINTR == errno); sleep(10); } close(fd); return 0; }
根据以下链接,整理出epoll的原理和优缺点,写一个测试代码;
多路复用大体上有三种解决方式:1.select 2.poll 3.epoll。 随着linux内核的发展,poll已经被淘汰基本不在使用了,取而代之的是epoll。
select有很多缺点和不足,为了弥补这些缺点产生出了epoll。
epoll的优点有如下几个方面:
同样,epoll也有缺点
epoll的使用维护需要调用3个函数,分别是 创建 控制 等待,如果连接数很少的情况下,显得十分繁冗。
#include <sys/epoll.h> int epoll_create ( int size ); #include <sys/epoll.h> int epoll_ctl ( int epfd, int op, int fd, struct epoll_event *event ); #include <sys/epoll.h> int epoll_wait ( int epfd, struct epoll_event* events, int maxevents, int timeout );
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/wait.h> #include <sys/epoll.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" void* func_recall(void* arg); void handler(int signo) { if (signo == SIGCHLD) { printf("Recall SIGINT\n"); waitpid(-1, NULL, WNOHANG); } } int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; struct sockaddr_in clientscokaddr; socklen_t clientaddrlen; static pthread_t nptd; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*优化4: 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); /*2. 绑定 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号 mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*2.2 绑定 */ result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("bind"); return -1; } /*3. 调用listen()把主动套接字变成被动套接字 */ result = listen(fd, 5); if (result != 0) { perror("listen"); return -1; } /*4. 阻塞等待客户端连接请求 */ /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/ int fd_client; pid_t pid; signal(SIGCHLD, handler); int epfd, nfds; struct epoll_event ev, events[5]; //ev用于注册事件,数组用于返回要处理的事件 epfd = epoll_create(1); //只需要监听一个描述符——socket fd ev.data.fd = fd; ev.events = EPOLLIN; //监听读状态同时设置LT模式 epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); //注册epoll事件 while (1) { nfds = epoll_wait(epfd, events, 5, -1); for (int i = 0; i < nfds; i++) { if (events[i].data.fd == fd) { printf("welcome to epoll's word!\n"); bzero((void*)&clientscokaddr, sizeof(clientscokaddr)); fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen); if (-1 == result) { perror("accpet"); return 0; } char ipv4_addr[16]; if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) { perror("inet_ntop"); return -1; } printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port)); pid = fork(); if (pid < 0) { perror("fork"); return -1; } else if (pid > 0) { //父进程 close(fd_client); } else { //子进程 close(fd); func_recall((void*)&fd_client); return 0; } ev.data.fd = fd; ev.events = EPOLLIN | EPOLLET; //设置ET模式 epoll_ctl(epfd, EPOLL_CTL_MOD, STDIN_FILENO, &ev); //重置epoll事件(ADD无效) } } } while (1) { } close(fd); close(fd_client); return 0; } void* func_recall(void* arg) { int result = 0; int newfd = *(int*)arg; printf("handler thread: newfd =%d\n", newfd); //..和newfd进行数据读写 char buf[BUFSIZ]; while (1) { bzero(buf, BUFSIZ); do { result = read(newfd, buf, BUFSIZ - 1); } while (result < 0 && EINTR == errno); if (result < 0) { perror("read"); break; } if (!result) break; printf("message is:%s\n", buf); } close(newfd); return 0; }
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; char MESSAGE[] = "hELLo wOLd...\n"; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT); #if 0 mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); #else result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr); if (result != 1) { perror("inet_pton"); return -1; } #endif result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("connect"); return -1; } printf("Client staring...OK!\n"); while (1) { do { result = write(fd, (void*)MESSAGE, sizeof(MESSAGE)); } while (result < 0 && EINTR == errno); sleep(10); } close(fd); return 0; }
参考直播: http://www.makeru.com.cn/live/5413_1937.html
参考博客: https://segmentfault.com/a/1190000003063859
参考播客: http://blog.csdn.net/davidsguo008/article/details/73556811
(注:poll现在用的比较少了,如果感兴趣,可以自己了解下)