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

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

关闭客户端:

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

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

(虽然程序退出,但套接字资源并没有释放,它是在内核内部完成四次挥手的过程)
2.代码(new_tcp)
tcp_socket.hpp:
#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#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:"<<cliip<<":"<<cliport<<std::endl;
//使用新建连接与客户端通信
std::string buf;
ret = conn_sock.Recv(&buf);
if(ret == false)
{
conn_sock.Close();
continue;
}
std::cout<<"client say:"<<buf<<std::endl;
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:"<<buf<<std::endl;
}
//关闭套接字
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 $@
本文介绍了一个简单的TCP服务器与客户端实现,重点在于如何管理TCP连接状态,包括处理TIME_WAIT状态以快速重启服务器。

被折叠的 条评论
为什么被折叠?



