目录
例1:
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- fd_set rfds;
- FD_ZERO(&rfds);//初始化集合
- while (1)
- {
- struct timeval tv;
- tv.tv_usec = 0;
- tv.tv_sec = 3;
- FD_SET(0,&rfds);//因为select会修改rfds集合,因此每次要重新添加描述符到集合中
- int max_fd = 0;
- //描述符的监控会做一件事情:在返回前,将没有就绪的描述符从集合中移除
- int ret = select(max_fd + 1,&rfds,NULL,NULL,&tv);
- if(ret < 0)
- {
- perror("select error");
- usleep(1000);
- continue;
- }
- else if(ret == 0)
- {
- printf("wait timeout\n");
- usleep(1000);
- continue;
- }
- for(int i = 0;i <= max_fd;i++)//因为没有把所有的描述符保存起来,所以从0到当前最大的描述符逐个判断
- {
- if(FD_ISSET(i,&rfds))//如果i这个描述符在集合中,就表示就绪了事件
- {
- char buf[1024] = {0};
- int res = read(i,buf,1023);
- if(res <= 0)
- {
- perror("recv error");
- FD_CLR(i,&rfds);
- return -1;
- }
- printf("%d 描述符就绪了事件,读取数据:%s\n",i,buf);
- }
- }
-
-
- }
- return 0;
- }
- select1:select1.c
- gcc $^ -o $@
结果如下:

例2:
select.hpp:
- #include "tcp_socket.hpp"
- #include
- #include
- #include
- class Select
- {
- private:
- fd_set _rfds;//可读事件的描述集合的备份,每次监控都是从这个集合复制一份出来进行监控(select会修改集合)
- int _max_fd;
- public:
- Select():_max_fd(-1)
- {
- //针对成员变量的初始化
- FD_ZERO(&_rfds);
- }
-
- //将sock中的描述符fd,添加到rfds可读事件的描述符集合中
- bool Add(TcpSocket &sock)
- {
- int fd = sock.GetFd();
- FD_SET(fd,&_rfds);//将描述符添加到监控集合中
- _max_fd = _max_fd >fd ?_max_fd:fd;//重新设置最大的描述符
- return true;
- }
-
- //将sock中的描述符fd,从rfds可读事件的描述符集合中移除
- bool Del(TcpSocket &sock)
- {
- int fd = sock.GetFd();
- FD_CLR(fd,&_rfds);//从当前的监控集合中移除指定的描述符
- //如果当前移除的刚好就是最大的描述符,这时候从最大描述符开始向前判断哪个描述符还在集合中,第一个就是最大的
- for(int i = _max_fd;i >= 0;i--)
- {
- if(FD_ISSET(i,&_rfds))
- {
- _max_fd = i;
- break;
- }
- }
- return 0;
- }
-
- //开始监控,返回就绪的描述符的数组
- bool Wait(std::vector
*arry,int timeout = 3000) - {
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
- fd_set tmp = _rfds;//使用临时集合进行监控,因为select会修改监控的集合
- int ret = select(_max_fd +1,&tmp,NULL,NULL,&tv);
- if(ret < 0)
- {
- perror("select error");
- return false;
- }
- else if(ret ==0)
- {
- printf("select timeout!\n");
- return false;
- }
- for(int i=0;i <= _max_fd;i++)
- {
- if(FD_ISSET(i,&tmp))
- {
- TcpSocket sock;
- sock.SetFd(i);
- arry->push_back(sock);
- }
-
- }
- return true;
- }
- };
tcp_socket.hpp:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #define MAX_LISTEN 5
- #define CHECK_RES(q) if((q)==false) { return -1;}
- class TcpSocket{
- private:
- int _sockfd;
- public:
- TcpSocket():_sockfd(-1){}
- int GetFd()
- {
- return _sockfd;
- }
- void SetFd(int fd)
- {
- _sockfd = fd;
- }
- //创建套接字
- bool Socket()
- {
- _sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- if(_sockfd < 0)
- {
- perror("socket error");
- return false;
- }
- return true;
- }
-
- //绑定地址信息
- bool Bind(const std::string &ip,uint16_t port)
- {
- struct sockaddr_in addr;//先定义一个ipv4的地址结构
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
- int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
- if(ret<0)
- {
- perror("bind error");
- return false;
- }
- return true;
- }
-
- //向服务器发起连接
- bool Connect(const std::string &ip,uint16_t port)
- {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
- int ret = connect(_sockfd,(struct sockaddr*)&addr,len);
- if(ret < 0)
- {
- perror("connect error");
- return false;
- }
- return true;
- }
-
- //服务器开始监听
- bool Listen(int backlog = MAX_LISTEN)
- {
- int ret = listen(_sockfd,backlog);
- if(ret < 0)
- {
- perror("listen error");
- return false;
- }
- return true;
- }
-
- //获取新建连接
- bool Accept(TcpSocket *sock,std::string *ip=NULL,uint16_t *port=NULL)
- {
- struct sockaddr_in addr;
- socklen_t len = sizeof(struct sockaddr_in);
- int newfd = accept(_sockfd,(struct sockaddr*)&addr,&len);
- if(newfd<0)
- {
- perror("accept error");
- return false;
- }
- sock->_sockfd = newfd;
- if(ip != NULL)
- {
- *ip = inet_ntoa(addr.sin_addr);
- }
- if(port != NULL)
- {
- *port = ntohs(addr.sin_port);
- }
- return true;
- }
-
- //接受数据
- bool Recv(std::string *body)
- {
- char tmp[4096] = {0};
- int ret = recv(_sockfd,tmp,4096,0);
- if(ret < 0)
- {
- perror("recv error");
- return false;
- }
- else if(ret == 0)
- {
- std::cout<<"peer shutdown!\n";
- return false;
- }
- body->assign(tmp,ret);//从tmp中截取ret长度大小的数据
- return true;
- }
-
-
-
- //发送数据
- bool Send(const std::string &body)
- {
- int ret;
- ret = send(_sockfd,body.c_str(),body.size(),0);
- if(ret < 0)
- {
- perror("send error");
- return false;
- }
- return true;
- }
-
- //关闭套接字
- bool Close()
- {
- if(_sockfd!= -1)
- {
- close(_sockfd);
- }
- return true;
- }
-
-
-
-
- };
tcp_srv.cpp:
- //#include "tcp_socket.hpp"
- #include "select.hpp"
- int main()
- {
- TcpSocket lst_sock;
-
- //创建套接字
- CHECK_RES(lst_sock.Socket());
- //绑定地址信息
- CHECK_RES(lst_sock.Bind("0.0.0.0",20000));
- //开始监听
- CHECK_RES(lst_sock.Listen());
-
- Select s;
- s.Add(lst_sock);//将监听套接字添加到集合中
- while(1)
- {
- std::vector
arry; - bool ret = s.Wait(&arry);
- if(ret == false)
- {
- usleep(1000);
- continue;
- }
- for(TcpSocket &a:arry)
- {
- //array里边就是就绪的描述符,一开始只有一个监听套接字,就绪的肯定就是监听套接字代表有新连接
- if(a.GetFd()==lst_sock.GetFd())//如果就绪的套接字描述符与监听套接字描述符一样,就表示有新建连接
- {
- TcpSocket conn_sock;
- std::string cliip;
- uint16_t cliport;
- bool ret = lst_sock.Accept(&conn_sock,&cliip,&cliport);
- if(ret < 0)
- {
- continue;
- }
- std::cout<<"new connect:"<
":"< - s.Add(conn_sock);//将新建的套接字也添加监控
- }
- else
- {
- std::string buf;
- ret = a.Recv(&buf);
- if(ret == false)
- {
- s.Del(a);//出错了则移除监控
- a.Close();
- continue;
- }
- std::cout<<"client say:"<
-
- std::cout<<"server say:";
- fflush(stdout);
- std::cin>>buf;
- ret = a.Send(buf);
- if(ret == false)
- {
- s.Del(a);
- a.Close();
- continue;
- }
- }
- }
- }
- //关闭套接字
- lst_sock.Close();
- return 0;
- }
-
-
tcp_cli.cpp:
- #include "tcp_socket.hpp"
-
- int main(int argc,char* argv[])
- {
- if(argc!= 3)
- {
- std::cout<<"please input server address!\n";
- std::cout<<"USsage:./tcp_cli 192.168.164.128 20000\n";
- return -1;
- }
- std::string srv_ip = argv[1];
- uint16_t srv_port = std::stoi(argv[2]);
- TcpSocket cli_sock;
- //创建套接字
- CHECK_RES(cli_sock.Socket());
- //绑定地址信息(客户端不推荐)
- //向服务器发起连接
- CHECK_RES(cli_sock.Connect(srv_ip,srv_port));
- while(1)
- {
- //与服务器通信
- std::string buf;
- std::cout<<"client say:";
- fflush(stdout);
- std::cin>>buf;
- bool ret = cli_sock.Send(buf);
- if(ret == false)
- {
- cli_sock.Close();
- return -1;
- }
- buf.clear();
- ret = cli_sock.Recv(&buf);
- if(ret == false)
- {
- cli_sock.Close();
- return -1;
- }
- std::cout<<"server say:"<
- }
- //关闭套接字
- cli_sock.Close();
- return 0;
- }
-
-
makefile:
- all:tcp_cli tcp_srv select1
- tcp_cli:tcp_cli.cpp
- g++ -std=c++11 $^ -o $@
- tcp_srv:tcp_srv.cpp
- g++ -std=c++11 $^ -o $@
- select1:select1.c
- gcc $^ -o $@
测试结果:
服务器端:

客户端1:

客户端2:

服务器处于一直监听的状态,客户端1请求服务器,与服务器连接,可进行多次循环通信,此时客户端2请求建立连接后,也可和服务器进行多次循环通信,注意服务器端绑定的地址以及客户端执行代码输入的地址。
3.poll
poll.c:
- #include
- #include
- #include
- #include
- int main()
- {
- struct pollfd pfds[10];
- int poll_count = 0;
- pfds[poll_count].fd = 0;//要监控的描述符是标准输入
- pfds[poll_count].events = POLLIN;//针对标准输入要监控的是可读事件
- poll_count++;
- while(1)
- {
- int ret = poll(pfds,poll_count,3000);
- if(ret < 0)
- {
- perror("poll error");
- usleep(1000);
- continue;
- }
- else if(ret == 0)
- {
- printf("poll timeout!\n");
- usleep(1000);
- continue;
- }
- for(int i=0;i < poll_count;i++)
- {
- if(pfds[i].revents & POLLIN)
- {
- //pfds[i].fd就绪了可读事件
- char buf[1024] = {0};
- read(pfds[i].fd,buf,1023);
- printf("%d 描述符就绪,读取数据:%s\n",pfds[i].fd,buf);
- }
- else if(pfds[i].revents & POLLOUT)
- {
- //就绪的是可写事件
- }
- }
-
- }
- return 0;
- }
makefile:
- poll:poll.c
- gcc $^ -o $@
结果:

4.epoll
tcp_socket.hpp:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
-
- #define MAX_LISTEN 5
- #define CHECK_RES(q) if((q)==false) { return -1;}
- class TcpSocket{
- private:
- int _sockfd;
- public:
- TcpSocket():_sockfd(-1){}
- int GetFd()
- {
- return _sockfd;
- }
- void SetFd(int fd)
- {
- _sockfd = fd;
- }
- void SetNonBlock()
- {
- int flag = fcntl(_sockfd,F_GETFL,0);
- fcntl(_sockfd,F_SETFL,flag | O_NONBLOCK);//在原有属性基础上新增非阻塞属性
- }
- //创建套接字
- bool Socket()
- {
- _sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- if(_sockfd < 0)
- {
- perror("socket error");
- return false;
- }
- return true;
- }
-
- //绑定地址信息
- bool Bind(const std::string &ip,uint16_t port)
- {
- struct sockaddr_in addr;//先定义一个ipv4的地址结构
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
- int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
- if(ret<0)
- {
- perror("bind error");
- return false;
- }
- return true;
- }
-
- //向服务器发起连接
- bool Connect(const std::string &ip,uint16_t port)
- {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
- int ret = connect(_sockfd,(struct sockaddr*)&addr,len);
- if(ret < 0)
- {
- perror("connect error");
- return false;
- }
- return true;
- }
-
- //服务器开始监听
- bool Listen(int backlog = MAX_LISTEN)
- {
- int ret = listen(_sockfd,backlog);
- if(ret < 0)
- {
- perror("listen error");
- return false;
- }
- return true;
- }
-
- //获取新建连接
- bool Accept(TcpSocket *sock,std::string *ip=NULL,uint16_t *port=NULL)
- {
- struct sockaddr_in addr;
- socklen_t len = sizeof(struct sockaddr_in);
- int newfd = accept(_sockfd,(struct sockaddr*)&addr,&len);
- if(newfd<0)
- {
- perror("accept error");
- return false;
- }
- sock->_sockfd = newfd;
- if(ip != NULL)
- {
- *ip = inet_ntoa(addr.sin_addr);
- }
- if(port != NULL)
- {
- *port = ntohs(addr.sin_port);
- }
- return true;
- }
-
- //接收数据
- bool Recv(std::string *body)
- {
- char tmp[4096] = {0};
- int ret = recv(_sockfd,tmp,4096,0);
- if(ret < 0)
- {
- perror("recv error");
- return false;
- }
- else if(ret == 0)
- {
- std::cout<<"peer shutdown!\n";
- return false;
- }
- body->assign(tmp,ret);//从tmp中截取ret长度大小的数据
- return true;
- }
-
- //接受数据
- /*
- bool Recv(std::string *body)
- {
- while(1)
- {
- char tmp[6] = {0};
- int ret = recv(_sockfd,tmp,5,0);
- if(ret < 0)
- {
- if(errno == EAGAIN)//EAGAIN这个错误表示当前缓冲区没有数据了,资源还没有准备好
- {
- return true;
- }
- perror("recv error");
- return false;
- }
- else if(ret == 0)
- {
- std::cout<<"peer shutdown!\n";
- return false;
- }
- *body += tmp;
- }
- return true;
- }
- */
-
- //发送数据
- bool Send(const std::string &body)
- {
- int ret;
- ret = send(_sockfd,body.c_str(),body.size(),0);
- if(ret < 0)
- {
- perror("send error");
- return false;
- }
- return true;
- }
-
- //关闭套接字
- bool Close()
- {
- if(_sockfd!= -1)
- {
- close(_sockfd);
- }
- return true;
- }
-
-
-
-
- };
epoll.hpp:
- #include "tcp_socket.hpp"
- #include
- #include
- #include
- #include
- #define EPOLL_MAX 100
- class Epoll
- {
- private:
- int _epfd;//epoll的操作句柄
- public:
- Epoll():_epfd(-1)
- {
- //针对成员变量的初始化
- _epfd = epoll_create(1);
- if(_epfd < 0)
- {
- perror("epoll error");
- exit(0);
- }
- }
-
-
- bool Add(TcpSocket &sock)
- {
- struct epoll_event ev;
- ev.events = EPOLLIN;
- ev.data.fd = sock.GetFd();
- int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,sock.GetFd(),&ev);
- if(ret < 0)
- {
- perror("epoll_ctl error");
- return false;
- }
- return true;
- }
-
-
- bool Del(TcpSocket &sock)
- {
- int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,sock.GetFd(),NULL);
- if(ret < 0)
- {
- perror("epoll_ctl error");
- return false;
- }
- return true;
- }
-
-
- bool Wait(std::vector
*arry,int timeout = 3000) - {
- struct epoll_event evs[EPOLL_MAX];
- int ret = epoll_wait(_epfd,evs,EPOLL_MAX,timeout);
- if(ret < 0)
- {
- perror("epoll_wait error");
- return false;
- }
- else if(ret == 0)
- {
- printf("epoll timeout!\n");
- return false;
- }
- for(int i = 0;i < ret;i++)
- {
- if(evs[i].events & EPOLLIN)//目前只管可读事件
- {
- TcpSocket sock;
- sock.SetFd(evs[i].data.fd);
- arry->push_back(sock);
- }
- }
- return true;
- }
- };
tcp_srv.cpp:
- /#include "tcp_socket.hpp"
- //#include "select.hpp"
- #include "epoll.hpp"
- int main()
- {
- TcpSocket lst_sock;
-
- //创建套接字
- CHECK_RES(lst_sock.Socket());
- //lst_sock.SetNonBlock();
- //绑定地址信息
- CHECK_RES(lst_sock.Bind("0.0.0.0",20000));
- //开始监听
- CHECK_RES(lst_sock.Listen());
-
- //Select s;
- Epoll s;
- s.Add(lst_sock);//将监听套接字添加到集合中
- while(1)
- {
- std::vector
arry; - bool ret = s.Wait(&arry);
- if(ret == false)
- {
- usleep(1000);
- continue;
- }
- for(TcpSocket &a:arry)
- {
- //array里边就是就绪的描述符,一开始只有一个监听套接字,就绪的肯定就是监听套接字代表有新连接
- if(a.GetFd()==lst_sock.GetFd())//如果就绪的套接字描述符与监听套接字描述符一样,就表示有新建连接
- {
- TcpSocket conn_sock;
- std::string cliip;
- uint16_t cliport;
- bool ret = lst_sock.Accept(&conn_sock,&cliip,&cliport);
- if(ret < 0)
- {
- continue;
- }
- std::cout<<"new connect:"<
":"< - //conn_sock.SetNonBlock();
- s.Add(conn_sock);//将新建的套接字也添加监控
- }
- else
- {
- std::string buf;
- ret = a.Recv(&buf);
- if(ret == false)
- {
- s.Del(a);//出错了则移除监控
- a.Close();
- continue;
- }
- std::cout<<"client say:"<
-
- std::cout<<"server say:";
- fflush(stdout);
- std::cin>>buf;
- ret = a.Send(buf);
- if(ret == false)
- {
- s.Del(a);
- a.Close();
- continue;
- }
- }
- }
- }
- //关闭套接字
- lst_sock.Close();
- return 0;
- }
-
tcp_cli.cpp:
- #include "tcp_socket.hpp"
-
- int main(int argc,char* argv[])
- {
- if(argc!= 3)
- {
- std::cout<<"please input server address!\n";
- std::cout<<"USsage:./tcp_cli 192.168.164.128 20000\n";
- return -1;
- }
- std::string srv_ip = argv[1];
- uint16_t srv_port = std::stoi(argv[2]);
- TcpSocket cli_sock;
- //创建套接字
- CHECK_RES(cli_sock.Socket());
- //绑定地址信息(客户端不推荐)
- //向服务器发起连接
- CHECK_RES(cli_sock.Connect(srv_ip,srv_port));
- while(1)
- {
- //与服务器通信
- std::string buf;
- std::cout<<"client say:";
- fflush(stdout);
- std::cin>>buf;
- bool ret = cli_sock.Send(buf);
- if(ret == false)
- {
- cli_sock.Close();
- return -1;
- }
- buf.clear();
- ret = cli_sock.Recv(&buf);
- if(ret == false)
- {
- cli_sock.Close();
- return -1;
- }
- std::cout<<"server say:"<
- }
- //关闭套接字
- cli_sock.Close();
- return 0;
- }
-
-
makefile:
- all:tcp_cli tcp_srv
- tcp_cli:tcp_cli.cpp
- g++ -std=c++11 $^ -o $@
- tcp_srv:tcp_srv.cpp
- g++ -std=c++11 $^ -o $@
结果:

客户端1:

客户端2:

修改tcp_socket.hpp如下:

此时客户端发送helloalaotie,客户端只收到了hello


以上代码只改动两处:①修改epoll.hpp如下:

② 修改tcp_socket.hpp如下:

则结果为:


加了EPOLLET的作用:只有在新数据到来时才会触发事件。
客户端发送nihaoalaotie,服务器只收到nihao,如果不用EPOLLET,则服务器回复客户端后会直接出现缓冲区的剩余数据(eg alaot),如果用了EPOLLET,则只有在客户端发送数据时,才会在缓冲区依次拿数据。---两者都修改了tcp_socket.hpp中的recv函数(如上面所示)。
修改tcp_socket.hpp如下: (增加SetNonBlock方法以及Recv方法、增加头文件)
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
-
- #define MAX_LISTEN 5
- #define CHECK_RES(q) if((q)==false) { return -1;}
- class TcpSocket{
- private:
- int _sockfd;
- public:
- TcpSocket():_sockfd(-1){}
- int GetFd()
- {
- return _sockfd;
- }
- void SetFd(int fd)
- {
- _sockfd = fd;
- }
- void SetNonBlock()
- {
- int flag = fcntl(_sockfd,F_GETFL,0);
- fcntl(_sockfd,F_SETFL,flag | O_NONBLOCK);//在原有属性基础上新增非阻塞属性
- }
- //创建套接字
- bool Socket()
- {
- _sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- if(_sockfd < 0)
- {
- perror("socket error");
- return false;
- }
- return true;
- }
-
- //绑定地址信息
- bool Bind(const std::string &ip,uint16_t port)
- {
- struct sockaddr_in addr;//先定义一个ipv4的地址结构
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
- int ret = bind(_sockfd,(struct sockaddr*)&addr,len);
- if(ret<0)
- {
- perror("bind error");
- return false;
- }
- return true;
- }
-
- //向服务器发起连接
- bool Connect(const std::string &ip,uint16_t port)
- {
- struct sockaddr_in addr;
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- addr.sin_addr.s_addr = inet_addr(ip.c_str());
- socklen_t len = sizeof(struct sockaddr_in);
- int ret = connect(_sockfd,(struct sockaddr*)&addr,len);
- if(ret < 0)
- {
- perror("connect error");
- return false;
- }
- return true;
- }
-
- //服务器开始监听
- bool Listen(int backlog = MAX_LISTEN)
- {
- int ret = listen(_sockfd,backlog);
- if(ret < 0)
- {
- perror("listen error");
- return false;
- }
- return true;
- }
-
- //获取新建连接
- bool Accept(TcpSocket *sock,std::string *ip=NULL,uint16_t *port=NULL)
- {
- struct sockaddr_in addr;
- socklen_t len = sizeof(struct sockaddr_in);
- int newfd = accept(_sockfd,(struct sockaddr*)&addr,&len);
- if(newfd<0)
- {
- perror("accept error");
- return false;
- }
- sock->_sockfd = newfd;
- if(ip != NULL)
- {
- *ip = inet_ntoa(addr.sin_addr);
- }
- if(port != NULL)
- {
- *port = ntohs(addr.sin_port);
- }
- return true;
- }
-
- //接收数据
-
- bool RecvCli(std::string *body)
- {
- char tmp[4096] = {0};
- int ret = recv(_sockfd,tmp,4096,0);
- if(ret < 0)
- {
- perror("recv error");
- return false;
- }
- else if(ret == 0)
- {
- std::cout<<"peer shutdown!\n";
- return false;
- }
- body->assign(tmp,ret);//从tmp中截取ret长度大小的数据
- return true;
- }
-
-
-
- //接受数据
-
- bool Recv(std::string *body)
- {
- while(1)
- {
- char tmp[6] = {0};
- int ret = recv(_sockfd,tmp,5,0);
- if(ret < 0)
- {
- if(errno == EAGAIN)//EAGAIN这个错误表示当前缓冲区没有数据了,资源还没有准备好
- {
- return true;
- }
- perror("recv error");
- return false;
- }
- else if(ret == 0)
- {
- std::cout<<"peer shutdown!\n";
- return false;
- }
- *body += tmp;
- }
- return true;
- }
-
-
-
- //发送数据
- bool Send(const std::string &body)
- {
- int ret;
- ret = send(_sockfd,body.c_str(),body.size(),0);
- if(ret < 0)
- {
- perror("send error");
- return false;
- }
- return true;
- }
-
- //关闭套接字
- bool Close()
- {
- if(_sockfd!= -1)
- {
- close(_sockfd);
- }
- return true;
- }
-
-
-
-
- };
修改tcp_srv.cpp:(创建套接字后设置非阻塞---两处)
- //#include "tcp_socket.hpp"
- //#include "select.hpp"
- #include "epoll.hpp"
- int main()
- {
- TcpSocket lst_sock;
-
- //创建套接字
- CHECK_RES(lst_sock.Socket());
- lst_sock.SetNonBlock();
- //绑定地址信息
- CHECK_RES(lst_sock.Bind("0.0.0.0",20000));
- //开始监听
- CHECK_RES(lst_sock.Listen());
-
- //Select s;
- Epoll s;
- s.Add(lst_sock);//将监听套接字添加到集合中
- while(1)
- {
- std::vector
arry; - bool ret = s.Wait(&arry);
- if(ret == false)
- {
- usleep(1000);
- continue;
- }
- for(TcpSocket &a:arry)
- {
- //array里边就是就绪的描述符,一开始只有一个监听套接字,就绪的肯定就是监听套接字代表有新连接
- if(a.GetFd()==lst_sock.GetFd())//如果就绪的套接字描述符与监听套接字描述符一样,就表示有新建连接
- {
- TcpSocket conn_sock;
- std::string cliip;
- uint16_t cliport;
- bool ret = lst_sock.Accept(&conn_sock,&cliip,&cliport);
- if(ret < 0)
- {
- continue;
- }
- std::cout<<"new connect:"<
":"< - conn_sock.SetNonBlock();
- s.Add(conn_sock);//将新建的套接字也添加监控
- }
- else
- {
- std::string buf;
- ret = a.Recv(&buf);
- if(ret == false)
- {
- s.Del(a);//出错了则移除监控
- a.Close();
- continue;
- }
- std::cout<<"client say:"<
-
- std::cout<<"server say:";
- fflush(stdout);
- std::cin>>buf;
- ret = a.Send(buf);
- if(ret == false)
- {
- s.Del(a);
- a.Close();
- continue;
- }
- }
- }
- }
- //关闭套接字
- lst_sock.Close();
- return 0;
- }
-
修改客户端tcp_cli.cpp---仍用原来的Recv方法,将其重命名为RecvCli:
- #include "tcp_socket.hpp"
-
- int main(int argc,char* argv[])
- {
- if(argc!= 3)
- {
- std::cout<<"please input server address!\n";
- std::cout<<"USsage:./tcp_cli 192.168.164.128 20000\n";
- return -1;
- }
- std::string srv_ip = argv[1];
- uint16_t srv_port = std::stoi(argv[2]);
- TcpSocket cli_sock;
- //创建套接字
- CHECK_RES(cli_sock.Socket());
-
- //绑定地址信息(客户端不推荐)
- //向服务器发起连接
- CHECK_RES(cli_sock.Connect(srv_ip,srv_port));
- cli_sock.SetNonBlock();
- while(1)
- {
- //与服务器通信
- std::string buf;
- std::cout<<"client say:";
- fflush(stdout);
- std::cin>>buf;
- bool ret = cli_sock.Send(buf);
- if(ret == false)
- {
- cli_sock.Close();
- return -1;
- }
- buf.clear();
- ret = cli_sock.RecvCli(&buf);
- if(ret == false)
- {
- cli_sock.Close();
- return -1;
- }
- std::cout<<"server say:"<
- }
- //关闭套接字
- cli_sock.Close();
- return 0;
- }
测试结果:


客户端1:
客户端2:
问题:如果客户端在连接后设置非阻塞,用了while循环以及新的Recv方法,则会出现:客户端输入数据会直接显示server say client say,在服务器端输入数据在客户端是收不到的,原因有待挖掘。结果如下图:


-
相关阅读:
c语言单元测试构建
P2607 [ZJOI2008] 骑士 (树形dp
spring boot中shiro使用自定义注解屏蔽接口鉴权
算法 旋转数组最小数字-(二分查找+反向双指针)
CCNA-NAT协议 静态NAT 动态NAT(多对多) PAT(多对一) 内网与外网之间的通信 GNS3实验验证
高斯判别分析(GDA)公式推导
centos7安装mysql8
【Nacos篇】Nacos基本操作及配置
openEuler快速入门(二)-openEuler命令行基础操作
rust函数
-
原文地址:https://blog.csdn.net/weixin_46153828/article/details/126576432