青海省住房城乡建设厅网站首页,软件开发属于哪个专业,婚纱摄影手机网站模板,全屏网站设计尺寸目录
1--基于I/O复用的服务器
2--select()函数
3--基于I/O复用的回声服务器端
4--send()和recv()函数的常用可选项
5--readv()和writev()函数 1--基于I/O复用的服务器 多进程服务器端具有以下缺点#xff1a;当有多个客户端发起连接请求时#xff0c;就会创建多个进程来…目录
1--基于I/O复用的服务器
2--select()函数
3--基于I/O复用的回声服务器端
4--send()和recv()函数的常用可选项
5--readv()和writev()函数 1--基于I/O复用的服务器 多进程服务器端具有以下缺点当有多个客户端发起连接请求时就会创建多个进程来分别处理客户端的请求创建多个进程往往需要付出巨大的代价 I/O复用的服务器端可以减少进程数无论连接多少个客户端提供服务的进程都只有 1 个 2--select()函数 select() 函数可以将多个文件描述符集中到一起来统一监视监视文件描述符可以视为监视 socket集中多个文件描述符时需要按照接收、传输和异常三种情况进行区分 select() 通过 fd_set 数组变量来执行监视操作fd_set 中文件描述符索引对应的位值被设置为 1 时表明该文件描述符是监视对象 // 对 fd_set 数组的常用操作
FD_ZERO(fd_set* fd_set); // 将 fd_set 变量的所有位初始化为0
FD_SET(int fd, fd_set* fdset); // 在参数 fdset 指向的变量中注册文件描述符fd的信息
FD_CLR(int fd, fd_set* fdset); // 从参数 fdset 指向的变量中消除文件描述符fd的信息
FD_ISSET(int fd, fd_set* fdset); // 若参数 fdset 指向的变量中包含文件描述符fd的信息则返回true #include sys/select.h
#include sys/time.hint select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout);
// 成功时返回大于 0 的值值为发生事件的文件描述符数失败时返回 -1超时返回 0
// maxfd 表示监视对象文件描述符的数量
// readset 表示将所有关注“是否存在待读取数据”的文件描述符注册到fd_set型变量并传递其地址值
// writeset 表示将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set型变量并传递其地址值
// exceptset 表示将所有关注“是否发生异常”的文件描述符注册到fd_set型变量并传递其地址值
// timeout 表示调用 select() 函数后为防止陷入无限阻塞的状态传递超时信息 select() 函数只有在监视的文件描述符发生变化时才返回如果未发生变化就会进入阻塞状态通过指定超时事件可以防止无限阻塞的情况即使文件描述符中未发生变化当超过了指定事件就会从函数中返回返回值为 0 当调用 select() 函数时除了发生变化的文件描述符之外所有原来值为 1 的位均会变为 0因此可知值仍为 1 的文件描述符发生了变化 // gcc select.c -o select
// ./select
#include stdio.h
#include unistd.h
#include sys/time.h
#include sys/select.h#define BUF_SIZE 30int main(int argc, char* argv[]){fd_set reads, temps;int result, str_len;char buf[BUF_SIZE];struct timeval timeout;FD_ZERO(reads); // 初始化fd_set变量FD_SET(0, reads); // 将文件描述符 0 对应的位设置为1表示监视标准输入文件描述符0对应标准输入stdinwhile(1){temps reads; // 记录初始值新循环时重新初始化为初始值timeout.tv_sec 5; // 超时时间设置为 5stimeout.tv_usec 0;result select(1, temps, 0, 0, timeout); // 5s内监视是否有标准输入时间发生if(result -1){puts(select() error!);break;}else if(result 0){ // 返回值为0表示超时puts(Time-out!);}else{if(FD_ISSET(0, temps)){ // 验证是否是标准输入发生了变化打印标准输入的内容str_len read(0, buf, BUF_SIZE);buf[str_len] 0;printf(message from console: %s, buf);}}}return 0;
}3--基于I/O复用的回声服务器端
// gcc echo_selectserv.c -o echo_selectserv
// ./echo_selectserv 9190
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include arpa/inet.h
#include sys/socket.h
#include sys/time.h
#include sys/select.h#define BUF_SIZE 100void error_handling(char *buf){fputs(buf, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;struct timeval timeout;fd_set reads, cpy_reads;socklen_t adr_sz;int fd_max, str_len, fd_num, i;char buf[BUF_SIZE];if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}serv_sock socket(PF_INET, SOCK_STREAM, 0);memset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*) serv_adr, sizeof(serv_adr)) -1){error_handling(bind() error); } if(listen(serv_sock, 5) -1){error_handling(listen() error);}FD_ZERO(reads); // 初始化fd_set变量FD_SET(serv_sock, reads); // 监视 serv_sockfd_max serv_sock;while(1){cpy_reads reads; // 记录初始值timeout.tv_sec 5; // 设置超时时间timeout.tv_usec 5000;if((fd_num select(fd_max1, cpy_reads, 0, 0, timeout)) -1){break;}if(fd_num 0) continue; // 判断是否是超时// 真的有事件发生执行以下代码for(i 0; i fd_max 1; i){if(FD_ISSET(i, cpy_reads)){ // 查找发生状态变化的文件描述符if(i serv_sock){ // 服务器端socket有变化执行受理连接请求adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr*)clnt_adr, adr_sz);FD_SET(clnt_sock, reads); // 将客户端socket注册到fd_set变量中if(fd_max clnt_sock){fd_max clnt_sock;}printf(connected client: %d \n, clnt_sock);}else{ // 不是服务器端socket发生变化表明有要接收的数据str_len read(i, buf, BUF_SIZE);if(str_len 0){ // 接收的是 EOF表明要断开连接FD_CLR(i, reads);close(i);printf(closed client: %d \n, i);}else{ // 接收的是真实数据write(i, buf, str_len); // 将接收到的数据返回客户端实现回声功能}}}}}close(serv_sock);return 0;
}
4--send()和recv()函数的常用可选项
#include sys/socket.h
ssize_t send(int sockfd, const void* buf, size_t nbytes, int flags);
ssize_t recv(int sockfd, void* buf, size_t nbytes, int flags);
// flags 表示可选项信息 通常情况下我们会将 send() 和 recv() 的可选项参数设置为 0但其实际拥有以下可选项 ① MSG_OOB 表示用于传输带外数据send、recv ② MSG_PEEK 表示验证输入缓冲中是否存在接收的数据recv ③ MSG_DONTROUTE 表示数据传输过程中不参照路由表在本地网络中寻找目的地send ④ MSG_DONTWAIT 表示调用 I/O 函数时不阻塞用于使用非阻塞 I/Osend、recv ⑤ MSG_WAITALL 表示防止函数返回直到接收全部请求的字节数recv MSG_OOB 用于发送带外数据的紧急消息操作系统收到紧急消息时将产生 SIGURG 信号并调用注册的信号处理函数 // gcc oob_recv.c -o oob_recv
// ./oob_recv 9190#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
#include signal.h
#include sys/socket.h
#include netinet/in.h
#include fcntl.h#define BUF_SIZE 30
int acpt_sock;
int recv_sock;void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}void urg_handler(int signo){int str_len;char buf[BUF_SIZE];str_len recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB);buf[str_len] 0;printf(Urgent message: %s \n, buf);
}int main(int argc, char* argv[]){struct sockaddr_in recv_adr, serv_adr;int str_len, state;socklen_t serv_adr_sz;struct sigaction act;char buf[BUF_SIZE];if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}act.sa_handler urg_handler; //设置信号的处理函数sigemptyset(act.sa_mask);act.sa_flags 0;acpt_sock socket(PF_INET, SOCK_STREAM, 0);memset(recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family AF_INET;recv_adr.sin_addr.s_addr htonl(INADDR_ANY);recv_adr.sin_port htons(atoi(argv[1]));if(bind(acpt_sock, (struct sockaddr*) recv_adr, sizeof(recv_adr)) -1){error_handling(bind() error); }listen(acpt_sock, 5); serv_adr_sz sizeof(serv_adr);recv_sock accept(acpt_sock, (struct sockaddr*)serv_adr, serv_adr_sz);// getpid() 返回进程ID// recv_sock发生SIGUGR信号需要有确定的处理进程假设创建了多个进程来调用信号处理函数// fcntl() 将 getpid() 返回的进程作为 SIGUGR 信号的处理进程fcntl(recv_sock, F_SETOWN, getpid());state sigaction(SIGURG, act, 0); // 发生SIGURG信号时调用urg_handler()函数while((str_len recv(recv_sock, buf, sizeof(buf)-1, 0)) ! 0){if(str_len -1){continue;}buf[str_len] 0;puts(buf);}close(recv_sock);close(acpt_sock);return 0;
}
// gcc oob_send.c -o oob_send
// ./oob_send 127.0.0.1 9190#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
#include sys/socket.h
#include arpa/inet.h#define BUF_SIZE 30void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char *argv[]){int sock;struct sockaddr_in recv_adr;if(argc ! 3){printf(Usage : %s IP port\n, argv[0]);exit(1);}sock socket(PF_INET, SOCK_STREAM, 0);memset(recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family AF_INET;recv_adr.sin_addr.s_addr inet_addr(argv[1]);recv_adr.sin_port htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)recv_adr, sizeof(recv_adr)) -1){error_handling(connect() error!);}write(sock, 123, strlen(123)); send(sock, 4, strlen(4), MSG_OOB); // 紧急传输数据write(sock, 567, strlen(567));send(sock, 890, strlen(890), MSG_OOB); // 紧急传输数据close(sock);return 0;
}同时设置 MSG_PEEK 选项和 MSG_DONTWAIT 选项可以验证输入缓冲中是否存在接收的数据同时由于设置了 MSG_PEEK 选项则调用 recv 函数时即使读取了输入缓冲的数据也不会删除输入缓冲的数据也就是说下一次读取时还可以读到上一次读取的数据 // gcc peek_recv.c -o peek_recv
// ./peek_recv 9190#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
#include sys/socket.h
#include arpa/inet.h#define BUF_SIZE 30void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char* argv[]){int acpt_sock, recv_sock;struct sockaddr_in acpt_adr, recv_adr;int str_len, state;socklen_t recv_adr_sz;char buf[BUF_SIZE];if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}acpt_sock socket(PF_INET, SOCK_STREAM, 0);memset(acpt_adr, 0, sizeof(acpt_adr));acpt_adr.sin_family AF_INET;acpt_adr.sin_addr.s_addr htonl(INADDR_ANY);acpt_adr.sin_port htons(atoi(argv[1]));if(bind(acpt_sock, (struct sockaddr*) acpt_adr, sizeof(acpt_adr)) -1){error_handling(bind() error); }listen(acpt_sock, 5); recv_adr_sz sizeof(recv_adr);recv_sock accept(acpt_sock, (struct sockaddr*)recv_adr, recv_adr_sz);while(1){// 设置 MSG_PEEK|MSG_DONTWAIT 选项即使不存在待读取的数据也不会进入阻塞状态// 假设存在待读取的数据则读取且不删除输入缓冲的数据因此下次仍可以读取str_len recv(recv_sock, buf, sizeof(buf) - 1, MSG_PEEK|MSG_DONTWAIT);if(str_len 0){break;}}buf[str_len] 0;printf(Buffering %d bytes: %s \n, str_len, buf);// 读取上一次留在输入缓冲的数据str_len recv(recv_sock, buf, sizeof(buf) - 1, 0);buf[str_len] 0;printf(Read again: %s \n, buf);close(acpt_sock);close(recv_sock);return 0;
}
// gcc peek_send.c -o peek_send
// ./peek_send 127.0.0.1 9190#include stdio.h
#include unistd.h
#include stdlib.h
#include string.h
#include sys/socket.h
#include arpa/inet.hvoid error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1);
}int main(int argc, char *argv[]){int sock;struct sockaddr_in recv_adr;if(argc ! 3){printf(Usage : %s IP port\n, argv[0]);exit(1);}sock socket(PF_INET, SOCK_STREAM, 0);memset(recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family AF_INET;recv_adr.sin_addr.s_addr inet_addr(argv[1]);recv_adr.sin_port htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)recv_adr, sizeof(recv_adr)) -1){error_handling(connect() error!);}write(sock, 123, strlen(123));close(sock);return 0;
} 5--readv()和writev()函数 readv() 和 writev() 函数对数据进行整合后再进行读取和发送即通过 writev() 函数可以将分散保存在多个缓冲中的数据一并发送通过 readv() 函数可以由多个缓冲分别接收 #include sys/uio.hssize_t writev(int filedes, const struct iovec* iov, int iovcnt);
// 成功时返回发送的字节数失败时返回 -1
// filedes 表示文件描述符
// iov 表示 iovec 结构体数组的地址值
// iovcnt 表示向第二个参数传递的数组长度ssize_t readv(int filedes, const struct iovec* iov, int iovcnt);
// 成功时返回接收的字节数失败时返回 -1// iovec结构体
struct iovec{void* iov_base; // 缓冲地址size_t iov_len; // 缓冲大小
}
代码实例
// gcc writev.c -o write
// ./write#include stdio.h
#include sys/uio.hint main(int argc, char* argv[]){struct iovec vec[2];char buf1[] ABCDEFG;char buf2[] 1234567;int str_len;vec[0].iov_base buf1;vec[0].iov_len 3; vec[1].iov_base buf2;vec[1].iov_len 4;str_len writev(1, vec, 2); // 向文件描述符1写数据即向标准输出写数据puts();printf(Write bytes: %d \n, str_len);return 0;
} // gcc readv.c -o readv
// ./readv#include stdio.h
#include sys/uio.h#define BUF_SIZE 100int main(int argc, char *argv[]){struct iovec vec[2];char buf1[BUF_SIZE] {0,};char buf2[BUF_SIZE] {0,};int str_len;vec[0].iov_base buf1;vec[0].iov_len 5; // 设置最多保存5个字节vec[1].iov_base buf2;vec[1].iov_len BUF_SIZE;str_len readv(0, vec, 2); // 向标准输入文件描述符0读数据printf(Read bytes: %d \n, str_len);printf(First message: %s \n, buf1);printf(Second message: %s \n, buf2);return 0;
}