select 实现 tcp demo
- 回忆TCP的连接过程
- select
-
- 程序
- cli.cpp 客户端建立连接
- SelectSvr.hpp 服务器的头文件
- Tcpsvr.hpp
- main.cpp 主函数
- makefile
- 程序执行效果
源码地址
https://github.com/duchenlong/linux-text/tree/master/network/IO/SelectTcp
回忆TCP的连接过程
select
关于select 的介绍,可以参考上一篇博客 https://blog.csdn.net/duchenlong/article/details/106758718
我们使用select
函数的地方,是我们服务端所在的地方。
利用select可以监控可读事件的特性,将客户端所发起的连接产生的新的套接字(也就是一个文件描述符),添加到可读事件的集合中。
由于select中涉及到的处理有点多,我们可以对这些功能进行封装,构造一个selectSvr的类
select 的封装
tcp类的封装
程序流程
程序
cli.cpp 客户端建立连接
#include "Tcpsvr.hpp"
#include <cstdlib>
int main(int argc,char* argv[])
{
if(argc != 3)
{
cout<<"请输入正确的参数 [./client] [ip] [port]"<<endl;
return 0;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
Tcpsvr tcp;
if(!tcp.CreateSocket())
{
return 0;
}
if(!tcp.Connect(ip,port))
{
return 0;
}
while(1)
{
cout<<"请输入想给服务端说的话 : ";
fflush(stdout);
string buf;
cin>>buf;
tcp.Send(buf);
buf.clear();
if(!tcp.Recv(buf))
{
cout<<"我方程序退出"<<endl;
break;
}
cout<<"服务端说 : "<<buf<<endl;
}
tcp.Close();
return 0;
}
SelectSvr.hpp 服务器的头文件
#pragma once
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <vector>
#include <cstdio>
#include "Tcpsvr.hpp"
using namespace std;
class SelectSvr
{
public:
SelectSvr()
{
_maxFd = -1;
FD_ZERO(&_readfds);
}
void AddFd(int fd)
{
FD_SET(fd,&_readfds);
if(fd > _maxFd)
_maxFd = fd;
}
void DeleteFd(int fd)
{
FD_CLR(fd,&_readfds);
for(int i = _maxFd; i >= 0; i--)
if(FD_ISSET(i,&_readfds) == 1)
{
_maxFd = i;
break;
}
}
bool SelectWait(vector<Tcpsvr>& vec)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 3000;
fd_set tmp = _readfds;
int ret = select(_maxFd + 1,&tmp,NULL,NULL,&tv);
if(ret < 0)
{
perror("select");
return false;
}
else if(ret == 0)
{
return false;
}
for(int i = 0; i <= _maxFd; i++)
if(FD_ISSET(i,&tmp))
{
Tcpsvr ts;
ts.SetFd(i);
vec.push_back(ts);
}
return true;
}
private:
int _maxFd;
fd_set _readfds;
};
Tcpsvr.hpp
#pragma once
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdio>
#include <iostream>
#include <string>
using namespace std;
class Tcpsvr
{
public:
Tcpsvr()
:_sockfd(-1)
{}
bool CreateSocket()
{
int ret = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(ret < 0)
{
perror("socket");
return false;
}
_sockfd = ret;
int i = 0;
setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
return true;
}
bool Bind(const 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());
int ret = bind(_sockfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
if(ret < 0)
{
perror("bind");
return false;
}
return true;
}
bool Listen(int backlog = 5)
{
int ret = listen(_sockfd,backlog);
if(ret < 0)
{
perror("listen");
return false;
}
return true;
}
bool Accept(struct sockaddr_in* addr,Tcpsvr& ts)
{
socklen_t addrLen = sizeof(struct sockaddr_in);
int serFd = accept(_sockfd,(struct sockaddr*)addr,&addrLen);
if(serFd < 0)
{
perror("accept");
return false;
}
ts._sockfd = serFd;
return true;
}
bool Connect(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());
int ret = connect(_sockfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0)
{
perror("connect");
return false;
}
return true;
}
bool Send(string& data)
{
ssize_t ret = send(_sockfd,data.c_str(),data.size(),0);
if(ret == 0)
{
perror("send");
return false;
}
return true;
}
bool Recv(string& data)
{
char buf[1024] = {'\0'};
ssize_t ret = recv(_sockfd,buf,sizeof(buf) - 1,0);
if(ret < 0)
{
perror("recv");
return false;
}
else if (ret == 0)
{
cout<<"对方关闭了连接"<<endl;
return false;
}
data.assign(buf,ret);
return true;
}
void Close()
{
close(_sockfd);
_sockfd = -1;
}
inline void SetFd(int fd)
{
_sockfd = fd;
}
inline int GetFd()
{
return _sockfd;
}
private:
int _sockfd;
};
main.cpp 主函数
#include "SelectSvr.hpp"
#define CHECK_RET(p) if(p != true) {return -1;}
int main()
{
Tcpsvr listen_ts;
CHECK_RET(listen_ts.CreateSocket());
CHECK_RET(listen_ts.Bind("0.0.0.0",19998));
CHECK_RET(listen_ts.Listen());
SelectSvr ss;
ss.AddFd(listen_ts.GetFd());
while(1)
{
vector<Tcpsvr> vec;
if(!ss.SelectWait(vec))
continue;
for(size_t i = 0; i < vec.size(); i++)
{
if(listen_ts.GetFd() == vec[i].GetFd())
{
struct sockaddr_in addr;
Tcpsvr peerts;
listen_ts.Accept(&addr,peerts);
cout<<"有一个新连接 : ip = [" << inet_ntoa(addr.sin_addr)<<"] ";
cout<<"port = ["<<ntohs(addr.sin_port)<<" ]"<<endl;
ss.AddFd(peerts.GetFd());
}
else
{
string data;
bool ret = vec[i].Recv(data);
if(!ret)
{
ss.DeleteFd(vec[i].GetFd());
vec[i].Close();
continue;
}
cout<<"客户端["<<vec[i].GetFd()<< "]发送的数据是 : "<<data<<endl;
fflush(stdout);
cout<<"回复客户端数据 : ";
cin>>data;
vec[i].Send(data);
}
}
}
return 0;
}
makefile
all:main cli
cli:cli.cpp
g++ $^ -o $@ -g
main:main.cpp
g++ $^ -o $@ -g
程序执行效果
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)