linux socket的IO多路复用简单例子(二)

来源:互联网 发布:本科大学java专业课程 编辑:程序博客网 时间:2024/05/22 03:24

通过select函数来编写socket通信。

原理:集合fd_set是用来存储文件描述符。每次调用select函数时,将fd集合从用户态复制拷贝到内核态。select函数运行完后,然后遍历fd集合,查看哪些描述符已经就绪,对已经就绪的fd进行相应的读写操作。


server端:

#include <iostream>#include <stdio.h>#include <sys/socket.h>#include <sys/types.h>#include <sys/select.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <sys/shm.h>using namespace std;#define port 5875#define IP "127.0.0.1"#define MAX_FD_SIZE 1024struct server_context_st{int cli_cnt;//fd sizeint clifds[MAX_FD_SIZE];//client fdint maxFd;fd_set allfds;//all fd(include client and server)};static server_context_st * s_srv_ctx = NULL;int main(){//inits_srv_ctx = (server_context_st*)malloc(sizeof(server_context_st));if (s_srv_ctx == NULL){cout << "malloc error" << endl;return 0;}memset(s_srv_ctx, 0, sizeof(s_srv_ctx));for (int i = 0; i < MAX_FD_SIZE; i++){s_srv_ctx->clifds[i] = -1;}//创建服务文件描述符int server_fd;server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd == -1){perror("create server fd error");return 0;}struct sockaddr_in sockAttr;sockAttr.sin_family = AF_INET;sockAttr.sin_port = htons(port);sockAttr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(server_fd, (struct sockaddr*)&sockAttr, sizeof(sockAttr)) == -1){perror("bind error");return 0;}if (listen(server_fd, 1024) == -1){perror("bind errpr");return 0;}struct timeval tv;fd_set *readfds = &s_srv_ctx->allfds;while (1){/*  void FD_ZERO(fd_set *fdset);//清空集合*/FD_ZERO(readfds);/*  void FD_SET(int fd, fd_set *fdset);  //将一个给定的文件描述符加入集合之中*/FD_SET(server_fd, readfds);s_srv_ctx->maxFd = server_fd;tv.tv_sec = 20;tv.tv_usec = 0;/*拷贝集合到内核态中*/for (int i = 0; i < MAX_FD_SIZE; i++){int clifd = s_srv_ctx->clifds[i];FD_SET(clifd, readfds);s_srv_ctx->maxFd = (clifd > s_srv_ctx->maxFd ? clifd : s_srv_ctx->maxFd);}/*int select (int __nfds, fd_set *__restrict __readfds,fd_set *__restrict __writefds,   fd_set *__restrict __exceptfds,struct timeval *__restrict __timeout);   __nfds:集合数量+1。   __readfds:可读文件描述符的集合即接收信息   __writefds:可写文件描述符的集合发送信息   __exceptfds:异常文件描述符   __timeout:表示在一段时间内,需要监视的文件描述符没有事件发生则放回,返回值为0*///只监控可读的文件描述符int retval = select(s_srv_ctx->maxFd + 1, readfds, NULL, NULL, &tv);if (retval == -1){cout << "select error" << endl;return 0;}if (retval == 0){cout << "select time out" << endl;continue ;}/*int FD_ISSET(int fd, fd_set *fdset);   // 检查集合中指定的文件描述符是否可以读写*///检测服务fd是否可读文件描述符集合中,可读表面有新的客户端连接请求if (FD_ISSET(server_fd, readfds)){//create new socketstruct sockaddr_in cliarrt;socklen_t len = sizeof(cliarrt);int conn = -1;while (conn == -1){conn = accept(server_fd, (struct sockaddr*)&cliarrt, &len);if (conn == -1){perror("accept client error");}}//将检测的client文件描述符,放到数组中for (int i = 0; i < MAX_FD_SIZE; i++){if (s_srv_ctx->clifds[i] < 0){s_srv_ctx->clifds[i] = conn;s_srv_ctx->cli_cnt++;break;}}}//轮询客户端fd数组char buf[1024] = {0};for (int i = 0; i < s_srv_ctx->cli_cnt; i++){int cl = s_srv_ctx->clifds[i];if (cl < 0){continue;}//存在可读集合中,读取客户端发送的信息if (FD_ISSET(cl, readfds)){int readCt = read(cl, buf, 1024);if (readCt <= 0){FD_CLR(cl, &s_srv_ctx->allfds);close(cl);s_srv_ctx->clifds[i] = -1;continue;}}//返回信息给客户端string wrtBuf = "hello,client " + to_string(i);write(cl, wrtBuf.c_str(), wrtBuf.length() + 1);}}return 0;}

参考:

http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html

0 0
原创粉丝点击