一、单执行流(服务完了一个才能回应另一个客户端)
tcp_client.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
class TcpClient
{
private:
std::string svr_ip;
int svr_port;
int sock;
public:
TcpClient(std::string _ip,int _port):svr_ip(_ip),svr_port(_port),sozk(-1)
{}
bool InitTcpClient()
[
sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0) //创建失败
{
std::cerr << "socket error" << std::endl;
return false;
}
//不需要绑定,不需要监听
return true;
}
//需要连接服务器
void Start()
{
struct sockaddr_in peer;
memset(&peer,0,sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(svr_port);
peer.sin_addr.s_addr = inet_addr(svr_ip.c_str());
if(connect(sock,(struct sockaddr*)&peer,sizeof(peer)) == 0)
{
//success
std::cout << "connect success..." << std::endl;
Request(sock);
}
else
{
//fail
std::cout << "connect fail..." << std::endl;
}
}
void Request(int sock)
{
std::string message;
char buffer[1024];
while(true)
{
std::cout << "Please Enter#";
std::cin >> message; //输入消息
write(sock,message.c_str(),message.size()); //发过去
ssize_t s = read(sock,buffer,sizeof(buffer)-1); //读取发送过来的数据
if(s > 0)
{
buffer[s] = 0;
std::cout << "server echo#" << buffer << std::endl;
}
}
}
~TcpClient()
{
if(sock >= 0)
close(sock);
}
};
tcp_client.cc
#include "tcp_client.hpp"
void Usage(std::string proc)
{
std::cout << "Usage: " << proc << "server_ip server_port" << std:;endl;
}
// 3个参数 :./client server_ip server_port
int main(int argc, char *argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
TcpClient tcli(argv[1],atoi(argv[2]));
tcli.InitTcpClient();
tcli.Start();
return 0;
}
tcp_server.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define BACKLOG 5 //全连接队列长度
class TcpServer
{
private:
int port;
int listen_sock; //tcp是面向连接的,需要socket,监听套接字
public:
TcpServer(int _port):port(_port),listen_sock(-1)
{}
bool InitTcpServer()
{
listen_sock = socket(AF_INET,SOCK_STREAM,0);
if(listen_sock < 0) //创建套接字失败
{
std::cerr << "socket error" << std::endl;
return false;
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local)); //初始化
local.sin_family = AE_INET;
local.sin_port = hons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0) //绑定失败
{
std::cerr << "bind error" << std::endl;
return false;
}
//有连接到来,需要监听
if(listen(listen_sock,BACKLOG) < 0) //监听失败
{
std::cerr << "listen error" << std::endl;
return false;
}
return true;
}
void Loop()
{
for(;;)
{
//获取连接,知道连接的scoket与内容
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len); //返回值是一个文件描述符
if(sock < 0)
{
std::cerr << "accept error" << std:;endl;
continue; //没有获取新连接成功,则继续获取
}
//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;
//处理请求
std::string ip = inet_ntoa(peer.sin_addr);
int port = ntohs(peer.sin_port);
std::cout << "get a new link [" << ip << "]" << std::endl;
Service(sock,ip,port);
}
}
void Service(int sock,std::string ip,int port)
{
char buffer[1024];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer)-1);
if(size > 0)
{
buffer[size] = 0;
std::cout << ip <<":" << port <<"#" << buffer << std::endl;
write(sock,buffer,size);
}
else if(size == 0)
{
std::cout << ip <<":" << port <<"close" << std::endl;
break;
}
else
{
std::cerr << sock << "read error" << std::endl;
break;
}
}
close(sock);
std:;cout << "serice done" << std::endl;
}
~TcpServer()
{
if(listen_sock >= 0)
close(listen_sock);
}
};
tcp_server.cc
#include "tcp_server.hpp"
void Usage(std::string proc)
{
std::cout << "Usage: " << proc << " port" << std::endl;
}
int main(int argc,char *argv[])
{
if(argc != 2)
{
Usage(argv[0]);
return 1;
}
//TcpServer tsvr(8081);
TcpServer tsvr(atoi(argv[1])); //argv[1]为程序名称
tsvr.InitTcpServer();
tsvr.Loop();
}
注意:
(1)accept函数 返回的是一个文件描述符,sock与listen_sock的联系,listen_sock相当于拉客的店小二,sock相当于进入饭店后为客人服务的服务员
listen_sock:获取新连接
sock:服务新连接(读取数据、分析数据、写入数据)
(2)ssize_t read(int fd,coid *buf,size_t count)
ssize > 0 :实际读取的字节
ssize_t == 0: 说明对端把链接关闭
ssize_t < 0:说明读取时遇到了错误
(3)
二、多执行流
创建fork(),但是进程是阻塞的,解决方法如下:
法一:利用signal,
tcp_server.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#define BACKLOG 5 //全连接队列长度
class TcpServer
{
private:
int port;
int listen_sock; //tcp是面向连接的,需要socket,监听套接字
public:
TcpServer(int _port):port(_port),listen_sock(-1)
{}
bool InitTcpServer()
{
listen_sock = socket(AF_INET,SOCK_STREAM,0);
if(listen_sock < 0) //创建套接字失败
{
std::cerr << "socket error" << std::endl;
return false;
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local)); //初始化
local.sin_family = AE_INET;
local.sin_port = hons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0) //绑定失败
{
std::cerr << "bind error" << std::endl;
return false;
}
//有连接到来,需要监听
if(listen(listen_sock,BACKLOG) < 0) //监听失败
{
std::cerr << "listen error" << std::endl;
return false;
}
return true;
}
void Loop()
{
signal(SIGCHLD,SIG_IGN); //会自动释放退出进程
//获取连接,知道连接的scoket与内容
struct sockaddr_in peer;
for( ; ; )
{
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len); //返回值是一个文件描述符
if(sock < 0)
{
std::cerr << "accept error" << std:;endl;
continue; //没有获取新连接成功,则继续获取
}
//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;
//处理请求
std::string ip = inet_ntoa(peer.sin_addr);
int port = ntohs(peer.sin_port);
std::cout << "get a new link [" << ip << "]" << std::endl;
pid_t id = fork(); //让子进程提供服务,子进程会继承父进程文件描述符
if(id == 0)
{
//child 管道
Service(sock,ip,port);
}
//waitpid(); //阻塞的,非阻塞的话需要保存所有进程信息
}
}
void Service(int sock,std::string ip,int port)
{
char buffer[1024];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer)-1);
if(size > 0)
{
buffer[size] = 0;
std::cout << ip <<":" << port <<"#" << buffer << std::endl;
write(sock,buffer,size);
}
else if(size == 0)
{
std::cout << ip <<":" << port <<"close" << std::endl;
break;
}
else
{
std::cerr << sock << "read error" << std::endl;
break;
}
}
close(sock);
std:;cout << "serice done" << std::endl;
}
~TcpServer()
{
if(listen_sock >= 0)
close(listen_sock);
}
};
法二:利用孤儿进程由OS回收
tcp_server.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#include<sys/wait.h>
#define BACKLOG 5 //全连接队列长度
class TcpServer
{
private:
int port;
int listen_sock; //tcp是面向连接的,需要socket,监听套接字
public:
TcpServer(int _port):port(_port),listen_sock(-1)
{}
bool InitTcpServer()
{
listen_sock = socket(AF_INET,SOCK_STREAM,0);
if(listen_sock < 0) //创建套接字失败
{
std::cerr << "socket error" << std::endl;
return false;
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local)); //初始化
local.sin_family = AE_INET;
local.sin_port = hons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0) //绑定失败
{
std::cerr << "bind error" << std::endl;
return false;
}
//有连接到来,需要监听
if(listen(listen_sock,BACKLOG) < 0) //监听失败
{
std::cerr << "listen error" << std::endl;
return false;
}
return true;
}
void Loop()
{
//第一种方法:signal(SIGCHLD,SIG_IGN); //会自动释放退出进程
//获取连接,知道连接的scoket与内容
struct sockaddr_in peer;
for( ; ; )
{
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len); //返回值是一个文件描述符
if(sock < 0)
{
std::cerr << "accept error" << std:;endl;
continue; //没有获取新连接成功,则继续获取
}
//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;
//处理请求
pid_t id = fork(); //爷爷 爸爸 孙子
if(id == 0)
{
//child 管道
if(fork()> 0)
{
exit(0); //父进程直接终止,提供服务的是孙子进程,成为孤儿进程,直接由OS回收
}
std::string ip = inet_ntoa(peer.sin_addr);
int port = ntohs(peer.sin_port);
std::cout << "get a new link [" << ip << "]" << std::endl;
Service(sock,ip,port); //孙子进程提供服务
exit(0);
}
//waitpid(); //阻塞的
//father
close(sock);
waitpid(id,nullptr,0); //把爸爸回收
}
}
void Service(int sock,std::string ip,int port)
{
char buffer[1024];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer)-1);
if(size > 0)
{
buffer[size] = 0;
std::cout << ip <<":" << port <<"#" << buffer << std::endl;
write(sock,buffer,size);
}
else if(size == 0)
{
std::cout << ip <<":" << port <<"close" << std::endl;
break;
}
else
{
std::cerr << sock << "read error" << std::endl;
break;
}
}
close(sock);
std:;cout << "serice done" << std::endl;
}
~TcpServer()
{
if(listen_sock >= 0)
close(listen_sock);
}
};
tcp_client.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
class TcpClient
{
private:
std::string svr_ip;
int svr_port;
int sock;
public:
TcpClient(std::string _ip,int _port):svr_ip(_ip),svr_port(_port),sozk(-1)
{}
bool InitTcpClient()
[
sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0) //创建失败
{
std::cerr << "socket error" << std::endl;
return false;
}
//不需要绑定,不需要监听
return true;
}
//需要连接服务器
void Start()
{
struct sockaddr_in peer;
memset(&peer,0,sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(svr_port);
peer.sin_addr.s_addr = inet_addr(svr_ip.c_str());
if(connect(sock,(struct sockaddr*)&peer,sizeof(peer)) == 0)
{
//success
std::cout << "connect success..." << std::endl;
Request(sock);
}
else
{
//fail
std::cout << "connect fail..." << std::endl;
}
}
void Request(int sock)
{
std::string message;
char buffer[1024];
while(true)
{
std::cout << "Please Enter#";
std::cin >> message; //输入消息
write(sock,message.c_str(),message.size()); //发过去
ssize_t s = read(sock,buffer,sizeof(buffer)-1); //读取发送过来的数据
if(s > 0)
{
buffer[s] = 0;
std::cout << "server echo#" << buffer << std::endl;
}
}
}
~TcpClient()
{
if(sock >= 0)
close(sock);
}
};
tcp_client.cc
#include "tcp_client.hpp"
void Usage(std::string proc)
{
std::cout << "Usage: " << proc << "server_ip server_port" << std:;endl;
}
// 3个参数 :./client server_ip server_port
int main(int argc, char *argv[])
{
if(argc != 3)
{
Usage(argv[0]);
return 1;
}
TcpClient tcli(argv[1],atoi(argv[2]));
tcli.InitTcpClient();
tcli.Start();
return 0;
}
tcp_server.hpp
#pragma once
#include<iostream>
#include<string>
#include<cstring>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#define BACKLOG 5 //全连接队列长度
class TcpServer
{
private:
int port;
int listen_sock; //tcp是面向连接的,需要socket,监听套接字
public:
TcpServer(int _port):port(_port),listen_sock(-1)
{}
bool InitTcpServer()
{
listen_sock = socket(AF_INET,SOCK_STREAM,0);
if(listen_sock < 0) //创建套接字失败
{
std::cerr << "socket error" << std::endl;
return false;
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local)); //初始化
local.sin_family = AE_INET;
local.sin_port = hons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0) //绑定失败
{
std::cerr << "bind error" << std::endl;
return false;
}
//有连接到来,需要监听
if(listen(listen_sock,BACKLOG) < 0) //监听失败
{
std::cerr << "listen error" << std::endl;
return false;
}
return true;
}
void Loop()
{
//第一种方法:signal(SIGCHLD,SIG_IGN); //会自动释放退出进程
//获取连接,知道连接的scoket与内容
struct sockaddr_in peer;
for( ; ; )
{
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len); //返回值是一个文件描述符
if(sock < 0)
{
std::cerr << "accept error" << std:;endl;
continue; //没有获取新连接成功,则继续获取
}
//inet_ntoa(peer.sin_addr):客户端IP地址(4字节IP转化成字符串点分十进制IP)
//ntohs(peer.sin_port):客户端端口号,网络序列转为主机序列
std::cout << "get a new link[" << inet_ntoa(peer.sin_addr) << "]" << ntohs(peer.sin_port) << std::endl;
//处理请求
std::string ip = inet_ntoa(peer.sin_addr);
int port = ntohs(peer.sin_port);
std::cout << "get a new link [" << ip << "]" << std::endl;
pid_t id = fork(); //爷爷 爸爸 孙子
if(id == 0)
{
//child 管道
if(fork()> 0)
{
}
Service(sock,ip,port);
}
//waitpid(); //阻塞的
}
}
void Service(int sock,std::string ip,int port)
{
char buffer[1024];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer)-1);
if(size > 0)
{
buffer[size] = 0;
std::cout << ip <<":" << port <<"#" << buffer << std::endl;
write(sock,buffer,size);
}
else if(size == 0)
{
std::cout << ip <<":" << port <<"close" << std::endl;
break;
}
else
{
std::cerr << sock << "read error" << std::endl;
break;
}
}
close(sock);
std:;cout << "serice done" << std::endl;
}
~TcpServer()
{
if(listen_sock >= 0)
close(listen_sock);
}
};
tcp_server.cc
#include "tcp_server.hpp"
void Usage(std::string proc)
{
std::cout << "Usage: " << proc << " port" << std::endl;
}
int main(int argc,char *argv[])
{
if(argc != 2)
{
Usage(argv[0]);
return 1;
}
//TcpServer tsvr(8081);
TcpServer tsvr(atoi(argv[1])); //argv[1]为程序名称
tsvr.InitTcpServer();
tsvr.Loop();
}
三、
static void* HandlerRequest(void* arg)
{
int sock = *(int*)arg;
delete (int*)arg;
pthread_detach(pthread_self());
Service(int sock,std::string ip,int port)
}