目录
运行服务器及客户端:
./tcp_srv
./tcp_cli 192.168.164.128 20000

关闭服务器端:---客户端变成了CLOSE_WAIT状态 ,此时服务端处于FIN_WAIT2状态

关闭客户端:

此时服务器虽然已经关闭,但处于TIME_WAIT状态,需等待一段时间才可重新启动。
否则会出现:

在此场景下,为了能尽快启动服务器,可设置TIME_WAIT等待时间,代码如下面所示。结果如图:
启动服务器->启动客户端->关闭服务器->关闭客户端->重启服务器:

(虽然程序退出,但套接字资源并没有释放,它是在内核内部完成四次挥手的过程)
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){}
-
- void SetReuseAddr()
- {
- int opt = 1;
- setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(int));
- }
- //创建套接字
- 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"
- int main()
- {
- TcpSocket lst_sock;
-
- //创建套接字
- CHECK_RES(lst_sock.Socket());
- lst_sock.SetReuseAddr();
-
- //绑定地址信息
- CHECK_RES(lst_sock.Bind("192.168.164.128",20000));
- //开始监听
- CHECK_RES(lst_sock.Listen());
- while(1)
- {
- //获取新建连接
- 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:"<
":"< - //使用新建连接与客户端通信
- std::string buf;
- ret = conn_sock.Recv(&buf);
- if(ret == false)
- {
- conn_sock.Close();
- continue;
- }
- std::cout<<"client say:"<
- std::cout<<"server say:";
- fflush(stdout);
- std::cin>>buf;
- ret = conn_sock.Send(buf);
- if(ret == false)
- {
- conn_sock.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_srv tcp_cli
- tcp_cli:tcp_cli.cpp
- g++ -std=c++11 $^ -o $@
- tcp_srv:tcp_srv.cpp
- g++ -std=c++11 $^ -o $@
-
相关阅读:
【智能家居项目】裸机版本——项目介绍 | 输入子系统(按键) | 单元测试
利用Jmeter做接口测试(功能测试)全流程分析
并发之固定运行和交替运行方案
文件操作~
Oracle 的hint用法
【设计模式】桥接模式
在PyCharm中使用Jupyter Notebooks实现高效开发
mac安装navicate
小明OJ——字符串移位包含问题
关系数据库系统中的 NULL 值及其用途
-
原文地址:https://blog.csdn.net/weixin_46153828/article/details/126531466