在IP数据报头部,有两个IP地址,分别叫做源IP(从哪来)和目的IP地址(到哪去)
端口号(port)是传输协议的内容
socket通信,本质是进程间通信,跨网络的进程间通信
任何的网络服务与网络客户端,如果要进行正常的数据通信,必须要有端口号,来唯一标识自身
-》 在同一个OS内,一个进程可以与一个端口号进行绑定,该端口号就在网络层面唯一标识一台主机上唯一一个进程
公网ip:唯一标识全网内唯一的一台主机
端口号port:标识一台主机上的唯一一个进程
-》ip+port:标识全网内唯一的一个进程(socket通信)
port本质是是网络级的概念
进程本质是系统的概念
一台机器上,可能存在大量的进程,但不是所有的进程都要对外进行网络数据请求。
传输层协议(TCP/UDP)的数据段有两个端口号,分别叫做源端口号(数据是谁发的)和目的端口号(要发给谁)
传输层协议:
传输层协议:
内存中的多字节数据相对于内存地址有大小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大小端之分,网络数据流同样有大小端之分。
那么该如何帝国一网络数据流的地址?
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
常见API
//创建socket文件描述符(TCP/UDP,客户端+服务器)
int socket(int domain, int type, int protocol);
//绑定端口号(TCP/UDP,服务器)
int bind(int socket,const struct sockaddr *address
,socklen_t address_len);
//开始监听socket(TCP,服务器)
int listen(int sockfd, int backlog);
//接受请求(TCP,服务器)
int accept(int sockfd, struct sockaddr *addr
,socklen_t *addrlen);
//建立连接(TCP,客户端)
int connect(int sockfd, const struct sockaddr *addr
,socklen_t addrlen);
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,eg:IPV4,IPV6以及UNIX Domain Socket,然而,各种网络协议的地址格式并不相同
struct sockaddr
{
_SOCKADDR_COMMON(sa_);
//Common data:address family and length
char sa_data[14];//address data
}
struct sockaddr_in
{
_SOCKADDR_COMMON(sin_);
in_port_t sin_port;//port number
struct in_addr sin_addr;//Internet address
unsigned char sin_zero[sizeof(struct sockaddr) -
_SOCKADDR_COMMON_SIZE - sizeof(in_port_t) -
sizeof(struct in_addr)];
}
该结构里主要有三部分信息:地址类型,端口号,ip地址
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
}
#include
int inet_aton(const char* strptr,struct in_addr* addrptr);
in_addr_t inet_addr(const char* strptr);
int inet_pton(int family,const char* strptr,void *addrptr);
char* inet_ntoa(struct in_addr inaddr);
const char* inet_ntop(int family,const void* addrptr,char* strptr);
其中inet_pton和inet_ntop不仅可以转换ipv4的in_addr,还可以转换ipv6的in6_addr,因此函数接口是void* addrptr;
inet_ntoa函数返回了一个char*,是该函式在内部申请了一块内存来保存ip,是把返回结果放到静态存储区,不需要我们手动释放。
因为inet_ntoa把结果放到自己内部的一个静态存储区,这样第二次调用时的结果会覆盖掉上一次的结果
netstat -nltp/-nlup
#pragma once
#include
#include
#include
#include
#include
#include
#include
#define DEFAULT 8081
class UdpServer
{
private:
int port;
int sockfd;
std::string ip;
public:
UdpServer(std::string _ip,int _port = DEFAULT):port(_port),sockfd(-1),ip(_ip)
{}
bool InitUdpserver()
{
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0)
{
std::cerr<<"socket error"<<std::endl;
return false;
}
std::cout<<"socket create success,sockfd:"<<sockfd<<std::endl;
struct sockaddr_in local;
memset(&local,'\0',sizeof(local));
local.sin_family = AF_INET;//协议家族
local.sin_port = htons(port);//port需要发送到网络中,需要设置为网络序列
local.sin_addr.s_addr =inet_addr(ip.c_str()) ;//把字符串ip转化成整数ip
if(bind(sockfd,(struct sockaddr*)&local,sizeof(local)) < 0){
//绑定套接字
std::cerr <<"bind error" <<std::endl;
return false;
}
std::cout<<"bind success"<<std::endl;
return true;
}
void Start()
{
#define SIZE 128
char buffer[SIZE];//定义缓冲区
for(;;){
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//读取数据
if(size > 0)//读取成功
{
buffer[size] = 0;
int _port = ntohs(peer.sin_port);//把网络序列转换成主机序列
std::string _ip = inet_ntoa(peer.sin_addr);
std::cout<<_ip<<":"<<_port<<"#"<<buffer<<std::endl;
}
else{
std::cerr<<"recvfrom error"<<std::endl;
}
}
}
~UdpServer(){}
};
#include "udp_server.hpp"
//udp_server port
int main(int argc,char *argv[])
{
if(argc !=2){
std::cerr<<"Usage: "<<argv[0]<<"port"<<std::endl;
return 1;
}
std::string ip = "127.0.0.1";//127.0.0.1=localhost:表示本地主机-》本地环回
int port = atoi(argv[1]);
UdpServer *srv = new UdpServer(ip,port);
srv->InitUdpserver();
srv->Start();
return 0;
}
#pragma once
#include
#include
#include
#include
#include
#include
#include
class UdpClient
{
private:
int sockfd;
std::string server_ip;
int server_port;
public:
UdpClient(std::string _ip,int _port)
:server_ip(_ip),server_port(_port)
{}
bool InitUdpclient()
{
sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
if(sockfd < 0)
{
std::cerr<<"sockfd create error"<<std::endl;
return false;
}
//客户端不需要绑定吗?
//客户端不需要port吗?
return true;
}
void Start()
{
struct sockaddr_in peer;
memset(&peer,0,sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(server_port);
peer.sin_addr.s_addr = inet_addr(server_ip.c_str());
std::string msg;
for(;;)
{
std::cout<<"please Enter# ";
std::cin >> msg;
sendto(sockfd,msg.c_str(),msg.size(),0,(struct sockaddr*)&peer,sizeof(peer));//发送数据
}
}
~UdpClient(){}
};
#include "udp_client.hpp"
// ./udp_client server_ip server_port
int main(int argc,char* argv[])
{
if(argc !=3)
{
std::cerr<<"Usage"<<argv[0]<<"server_ip server_port"<<std::endl;
return 1;
}
std::string ip = argv[1];
int port = atoi(argv[2]);
UdpClient *ucli = new UdpClient(ip,port);
ucli->InitUdpclient();
ucli->Start();
return 0;
}
云服务器的ip是由对应的云厂商提供的,这个ip不能直接绑定,如果需要bind,需要让外网放访问-》bind 0(意味着服务器可以接受来自任何客户端的请求)
如果想直接绑定:虚拟机或自定义安装的Linux
//INADDR_ANY ->0
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#define DEFAULT 8081
class UdpServer
{
private:
int port;
int sockfd;
//std::string ip;
public:
//UdpServer(std::string _ip,int _port = DEFAULT):port(_port),sockfd(-1),ip(_ip){}
UdpServer(int _port = DEFAULT):port(_port),sockfd(-1)
{}
bool InitUdpserver()
{
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0)
{
std::cerr<<"socket error"<<std::endl;
return false;
}
std::cout<<"socket create success,sockfd:"<<sockfd<<std::endl;
struct sockaddr_in local;
memset(&local,'\0',sizeof(local));
local.sin_family = AF_INET;//协议家族
local.sin_port = htons(port);//port需要发送到网络中,需要设置为网络序列
//local.sin_addr.s_addr =inet_addr(ip.c_str()) ;//把字符串ip转化成整数ip
local.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd,(struct sockaddr*)&local,sizeof(local)) < 0){
//绑定套接字
std::cerr <<"bind error" <<std::endl;
return false;
}
std::cout<<"bind success"<<std::endl;
return true;
}
void Start()
{
#define SIZE 128
char buffer[SIZE];//定义缓冲区
for(;;){
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//读取数据
if(size > 0)//读取成功
{
buffer[size] = 0;
int _port = ntohs(peer.sin_port);//把网络序列转换成主机序列
std::string _ip = inet_ntoa(peer.sin_addr);
std::cout<<_ip<<":"<<_port<<"#"<<buffer<<std::endl;
// 服务器发送信息
std::string echo_msg = "sever get->";
echo_msg += buffer;
sendto(sockfd,echo_msg.c_str(),echo_msg.size(),0,(struct sockaddr*)&peer,len);
}
else{
std::cerr<<"recvfrom error"<<std::endl;
}
}
}
~UdpServer()
{
if(sockfd >= 0)
{
close(sockfd);
}
}
};
#include "udp_server.hpp"
//udp_server port
int main(int argc,char *argv[])
{
if(argc !=2){
std::cerr<<"Usage: "<<argv[0]<<"port"<<std::endl;
return 1;
}
//std::string ip = "127.0.0.1";//127.0.0.1=localhost:表示本地主机-》本地环回
int port = atoi(argv[1]);
UdpServer *srv = new UdpServer(port);
srv->InitUdpserver();
srv->Start();
return 0;
}
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
class UdpClient
{
private:
int sockfd;
std::string server_ip;
int server_port;
public:
UdpClient(std::string _ip,int _port)
:server_ip(_ip),server_port(_port)
{}
bool InitUdpclient()
{
sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建套接字
if(sockfd < 0)
{
std::cerr<<"sockfd create error"<<std::endl;
return false;
}
//客户端不需要绑定吗?
//客户端不需要port吗?
return true;
}
void Start()
{
struct sockaddr_in peer;
memset(&peer,0,sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(server_port);
peer.sin_addr.s_addr = inet_addr(server_ip.c_str());
std::string msg;
for(;;)
{
std::cout<<"please Enter# ";
std::cin >> msg;
sendto(sockfd,msg.c_str(),msg.size(),0,(struct sockaddr*)&peer,sizeof(peer));//发送数据
char buffer[128];
struct sockaddr_in temp;
socklen_t len = sizeof(temp);
ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&len);
if(size > 0)
{
buffer[size] = 0;
std::cout<<buffer<<std::endl;
}
}
}
~UdpClient()
{
if(sockfd >= 0)
{
close(sockfd);
}
}
};
// ./udp_client server_ip server_port
int main(int argc,char* argv[])
{
if(argc !=3)
{
std::cerr<<"Usage"<<argv[0]<<"server_ip server_port"<<std::endl;
return 1;
}
std::string ip = argv[1];
int port = atoi(argv[2]);
UdpClient *ucli = new UdpClient(ip,port);
ucli->InitUdpclient();
ucli->Start();
return 0;
}
udp_server.hpp
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEFAULT 8081
class UdpServer
{
private:
int port;
int sockfd;
//std::string ip;
public:
//UdpServer(std::string _ip,int _port = DEFAULT):port(_port),sockfd(-1),ip(_ip){}
UdpServer(int _port = DEFAULT):port(_port),sockfd(-1)
{}
bool InitUdpserver()
{
sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0)
{
std::cerr<<"socket error"<<std::endl;
return false;
}
std::cout<<"socket create success,sockfd:"<<sockfd<<std::endl;
struct sockaddr_in local;
memset(&local,'\0',sizeof(local));
local.sin_family = AF_INET;//协议家族
local.sin_port = htons(port);//port需要发送到网络中,需要设置为网络序列
//local.sin_addr.s_addr =inet_addr(ip.c_str()) ;//把字符串ip转化成整数ip
local.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd,(struct sockaddr*)&local,sizeof(local)) < 0){
//绑定套接字
std::cerr <<"bind error" <<std::endl;
return false;
}
std::cout<<"bind success"<<std::endl;
return true;
}
void Start()
{
#define SIZE 128
char buffer[SIZE];//定义缓冲区
//输出的结果往网络里打印
//dup2(sockfd,1);//
for(;;){
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
ssize_t size = recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//读取数据
if(size > 0)//读取成功
{
buffer[size] = 0;
int _port = ntohs(peer.sin_port);//把网络序列转换成主机序列
std::string _ip = inet_ntoa(peer.sin_addr);
std::cout<<_ip<<":"<<_port<<"#"<<buffer<<std::endl;
std::string cmd = buffer;
std::string reslut;
if(cmd == "ls")
{
int pipes[2];
pipe(pipes);//创建匿名管道
pid_t id = fork();
if(id == 0)
{
//child
close(pipes[0]);
dup2(pipes[1],1);
execl("/usr/bin/ls","ls","-a","-l","-i",nullptr);
exit(1);
}
close(pipes[1]);
char c;
while(1)
{
if(read(pipes[0],&c,1) > 0)
{
reslut.push_back(c);
}
else
{
break;
}
}
wait(nullptr);
}
std::string echo_msg ;
if(reslut.empty())
{
// 服务器发送信息
echo_msg += buffer;
echo_msg = "sever get->";
}
else
{
echo_msg = reslut;
}
sendto(sockfd,echo_msg.c_str(),echo_msg.size(),0,(struct sockaddr*)&peer,len);
}
else{
std::cerr<<"recvfrom error"<<std::endl;
}
}
}
~UdpServer()
{
if(sockfd >= 0)
{
close(sockfd);
}
}
};
为何一定要断开链接?
答:维护链接是有成本的(空间和时间),因为OS需要管理链接(先描述再组织)
#include /* See NOTES */
#include
int socket(int domain, int type, int protocol);
#include /* See NOTES */
#include
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
我们程序中对myaddr参数的初始化如下:
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
#include /* See NOTES */
#include
int listen(int sockfd, int backlog);
#include /* See NOTES */
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#define BACKLOG 5
#define DFL_PORT 8081
class Tcpserver
{
//TCP是面向连接的,所以需要在正式发送数据之前,先要建立链接
private:
int port;
int listen_sock;
public:
Tcpserver(int _port = DFL_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;
exit(2);
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
std::cerr<<"bind error"<<std::endl;
exit(3);
}
if(listen(listen_sock,BACKLOG) < 0)//backlog:链接队列
{
std::cerr<<"listen error"<<std::endl;
exit(4);
}
return true;
}
void Loop()
{
for(;;)
{
//获取链接
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
//listen_sock:获取新链接
//sock:服务 新链接(读取数据,分析处理数据,写入数据)
if(sock < 0)
{
std::cerr<<"accept error,continue"<<std::endl;
continue;
}
std::string ip = inet_ntoa(peer.sin_addr);
int sport = ntohs(peer.sin_port);
Service(sock,ip,sport);
std::cout<<"get a new link ["<< ip << "]"<< sport <<std::endl;
}
}
void Service(int sock,std::string ip,int sport)
{
char buffer[64];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer) - 1);//返回值>0实际读取了多少字节;==0:说明对端关闭链接;<0:读取错误
if(size > 0)
{
//成功
// std::cout << "get a new link["<< atoi(ip) "]"<
buffer[size] = 0;
std::cout<< ip << ":" << sport <<"# "<< buffer <<std::endl;
write(sock,buffer,size);//tcp socket中,读写都是一个文件描述符:sock(fd)
//全双工通信的体现
}
else if(size == 0)
{
//对端把链接关闭
std::cout<< ip << ":" << sport <<"close! " <<std::endl;
break;
}
else
{
//出错
std::cout << sock << "read error" << std::endl;
break;
}
}
close(sock);
std::cout << "service done!"<< std::endl;
}
~Tcpserver()
{
if(listen_sock >= 0)
{
close(listen_sock);
}
}
};
#include "tcp_server.hpp"
void Usage(std::string proc)
{
std::cout<< "Usage: "<< proc << "port" << std::endl;
}
// ./server port
int main(int argc,char* argv[])
{
if(argc != 2)
{
Usage(argv[0]);
exit(1);
}
Tcpserver ts(atoi(argv[1]));
ts.InitTcpserver();
ts.Loop();
return 0;
}
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
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),sock(-1)
{}
void InitTcpclient()
{
sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
std::cerr << "socket error" << std::endl;
exit(2);
}
}
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)
{
//成功
std::cout << "connect success" << std::endl;
Request(sock);//与服务器端进行交互
}
else
{
//连接失败
std::cout << "connect false" << std::endl;
}
}
void Request(int sock)
{
std::string msg;
char buffer[1024];
while(true)
{
std::cout << "please enter# ";
std::cin >> msg;
//写入数据
write(sock,msg.c_str(),msg.size());
size_t size = read(sock,buffer,sizeof(buffer) - 1);
if(size > 0)
{
buffer[size] = 0;
std::cout << "server echo# " << buffer << std::endl;
}
}
}
~Tcpclient()
{
if(sock >= 0)
{
close(sock);
}
}
};
#include "tcp_client.hpp"
void Usage(std::string proc)
{
std::cout << "Usage: "<<proc <<"server_ip server_port"<<std::endl;
}
// ./client server_ip srever_port
int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage(argv[0]);
exit(1);
}
Tcpclient tc(argv[1],atoi(argv[2]));
tc.InitTcpclient();
tc.Start();
return 0;
}
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BACKLOG 5
#define DFL_PORT 8081
class Tcpserver
{
//TCP是面向连接的,所以需要在正式发送数据之前,先要建立链接
private:
int port;
int listen_sock;
public:
Tcpserver(int _port = DFL_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;
exit(2);
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
std::cerr<<"bind error"<<std::endl;
exit(3);
}
if(listen(listen_sock,BACKLOG) < 0)//backlog:链接队列
{
std::cerr<<"listen error"<<std::endl;
exit(4);
}
return true;
}
void Loop()
{
//signal(SIGCHLD,SIG_IGN);
struct sockaddr_in peer;
for(;;)
{
//获取链接
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
//listen_sock:获取新链接
//sock:服务 新链接(读取数据,分析处理数据,写入数据)
if(sock < 0)
{
std::cerr<<"accept error,continue"<<std::endl;
continue;
}
int sport = ntohs(peer.sin_port);
std::string ip = inet_ntoa(peer.sin_addr);
std::cout<<"get a new link-> "<< sock <<"["<< ip << "]"<< sport <<std::endl;
pid_t id = fork();
if(id == 0)
{
//child
close(listen_sock);
if(fork() > 0)
{
exit(0);
}
Service(sock,ip,sport);
exit(0);
}
close(sock);
waitpid(id,nullptr,0);
}
}
void Service(int sock,std::string ip,int sport)
{
char buffer[64];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer) - 1);//返回值>0实际读取了多少字节;==0:说明对端关闭链接;<0:读取错误
if(size > 0)
{
//成功
// std::cout << "get a new link["<< atoi(ip) "]"<
buffer[size] = 0;
std::cout<< ip << ":" << sport <<"# "<< buffer <<std::endl;
write(sock,buffer,size);//tcp socket中,读写都是一个文件描述符:sock(fd)
//全双工通信的体现
}
else if(size == 0)
{
//对端把链接关闭
std::cout<< ip << ":" << sport <<"close! " <<std::endl;
break;
}
else
{
//出错
std::cout << sock << "read error" << std::endl;
break;
}
}
close(sock);
std::cout << "service done!"<< std::endl;
}
~Tcpserver()
{
if(listen_sock >= 0)
{
close(listen_sock);
}
}
};
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BACKLOG 5
#define DFL_PORT 8081
class prama
{
public:
int sock;
std::string ip;
int port;
prama(int _sock,std::string _ip,int _port)
:sock(_sock),ip(_ip),port(_port)
{}
~prama()
{}
};
class Tcpserver
{
//TCP是面向连接的,所以需要在正式发送数据之前,先要建立链接
private:
int port;
int listen_sock;
public:
Tcpserver(int _port = DFL_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;
exit(2);
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
std::cerr<<"bind error"<<std::endl;
exit(3);
}
if(listen(listen_sock,BACKLOG) < 0)//backlog:链接队列
{
std::cerr<<"listen error"<<std::endl;
exit(4);
}
return true;
}
static void* HandlerRequesr(void *arg)
{
//int sock = *(int*)arg;
prama *p = (prama*)arg;
pthread_detach(pthread_self());
Service(p->sock,p->ip,p->port);
close(p->sock);
delete p;
return nullptr;
}
void Loop()
{
//signal(SIGCHLD,SIG_IGN);
struct sockaddr_in peer;
for(;;)
{
//获取链接
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
//listen_sock:获取新链接
//sock:服务 新链接(读取数据,分析处理数据,写入数据)
if(sock < 0)
{
std::cerr<<"accept error,continue"<<std::endl;
continue;
}
pthread_t tid;
//int *p = new int(sock);
std::string ip = inet_ntoa(peer.sin_addr);
int port = ntohs(peer.sin_port);
prama *p = new prama(sock,ip,port);
pthread_create(&tid,nullptr,HandlerRequesr,p);
}
}
static void Service(int sock,std::string ip,int sport)
{
char buffer[64];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer) - 1);//返回值>0实际读取了多少字节;==0:说明对端关闭链接;<0:读取错误
if(size > 0)
{
//成功
// std::cout << "get a new link["<< atoi(ip) "]"<
buffer[size] = 0;
std::cout<< ip << ":" << sport <<"# "<< buffer <<std::endl;
write(sock,buffer,size);//tcp socket中,读写都是一个文件描述符:sock(fd)
//全双工通信的体现
}
else if(size == 0)
{
//对端把链接关闭
std::cout<< ip << ":" << sport <<"close! " <<std::endl;
break;
}
else
{
//出错
std::cout << sock << "read error" << std::endl;
break;
}
}
//close(sock);
std::cout << "service done!"<< std::endl;
}
~Tcpserver()
{
if(listen_sock >= 0)
{
close(listen_sock);
}
}
};
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Threadpool.hpp"
#include "task.hpp"
#define BACKLOG 5
#define DFL_PORT 8081
class prama
{
public:
int sock;
std::string ip;
int port;
prama(int _sock,std::string _ip,int _port)
:sock(_sock),ip(_ip),port(_port)
{}
~prama()
{}
};
class Tcpserver
{
//TCP是面向连接的,所以需要在正式发送数据之前,先要建立链接
private:
int port;
int listen_sock;
Threadpool<Task> *tp;
public:
Tcpserver(int _port = DFL_PORT):port(_port),listen_sock(-1),tp(nullptr)
{}
bool InitTcpserver()
{
listen_sock = socket(AF_INET,SOCK_STREAM,0);
if(listen_sock < 0)
{
std::cerr<<"socket error"<<std::endl;
exit(2);
}
//绑定
struct sockaddr_in local;
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
std::cerr<<"bind error"<<std::endl;
exit(3);
}
if(listen(listen_sock,BACKLOG) < 0)//backlog:链接队列
{
std::cerr<<"listen error"<<std::endl;
exit(4);
}
tp = new Threadpool<Task>();
}
void Loop()
{
//signal(SIGCHLD,SIG_IGN);
tp->InitThreadpool();
struct sockaddr_in peer;
for(;;)
{
//获取链接
socklen_t len = sizeof(peer);
int sock = accept(listen_sock,(struct sockaddr*)&peer,&len);
//listen_sock:获取新链接
//sock:服务 新链接(读取数据,分析处理数据,写入数据)
if(sock < 0)
{
std::cerr<<"accept error,continue"<<std::endl;
continue;
}
int port = ntohs(peer.sin_port);
std::string ip = inet_ntoa(peer.sin_addr);
Task t(sock,ip,port);
tp->Push(t);
}
~Tcpserver()
{
if(listen_sock >= 0)
{
close(listen_sock);
}
}
};
#pragma once
#define NUM 5
#include
#include
#include
template <typename T>
class Threadpool{
private:
int thread_num;
std::queue<T> task_queue;
pthread_mutex_t lock;
pthread_cond_t cond;
public:
Threadpool(int _num = NUM):thread_num(_num)
{
pthread_mutex_init(&lock,nullptr);
pthread_cond_init(&cond,nullptr);
}
void Lockqueue()
{
pthread_mutex_lock(&lock);
}
void Unlockqueue()
{
pthread_mutex_unlock(&lock);
}
bool IsqueueEmpty()
{
return task_queue.size() == 0 ? true : false;
}
void Wait()
{
pthread_cond_wait(&cond,&lock);
}
void Wakeup()
{
pthread_cond_signal(&cond);
}
static void *Routine(void *arg)
{
pthread_detach(pthread_self());//线程分离
Threadpool *self = (Threadpool*)arg;
while(true){
self->Lockqueue();
while(self->IsqueueEmpty()){
//wait
self->Wait();
}
//任务队列有任务
T t;
self->Pop(t);
self->Unlockqueue();
//处理任务
t.Run();
}
}
void Push(const T& in)
{
Lockqueue();
task_queue.push(in);
Unlockqueue();
Wakeup();
}
void Pop(T& out)
{
out = task_queue.front();
task_queue.pop();
}
void InitThreadpool()
{
pthread_t tid;
for(int i = 0;i < thread_num;i++){
pthread_create(&tid,nullptr,Routine,this);//无法访问static 修饰的函数,所以传 this
}
}
~Threadpool()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);
}
};
#pragma once
#include
#include
#include
class Handler
{
public:
Handler()
{}
void operator()(int sock,std::string ip,int port)
{
char buffer[64];
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer) - 1);//返回值>0实际读取了多少字节;==0:说明对端关闭链接;<0:读取错误
if(size > 0)
{
//成功
// std::cout << "get a new link["<< atoi(ip) "]"<
buffer[size] = 0;
std::cout<< ip << ":" << port <<"# "<< buffer <<std::endl;
write(sock,buffer,size);//tcp socket中,读写都是一个文件描述符:sock(fd)
//全双工通信的体现
}
else if(size == 0)
{
//对端把链接关闭
std::cout<< ip << ":" << port <<"close! " <<std::endl;
break;
}
else
{
//出错
std::cout << sock << "read error" << std::endl;
break;
}
}
//close(sock);
std::cout << "service done!"<< std::endl;
close(sock);
}
~Handler()
{}
};
class Task
{
private:
int sock;
std::string ip;
int port;
Handler handler;
public:
Task()
{}
Task(int _sock,std::string _ip,int _port)
:sock(_sock),ip(_ip),port(_port)
{}
void Run()
{
handler(sock,ip,port);
}
~Task()
{}
};
#pragma once
#include
#include
#include
#include
class Handler
{
public:
Handler()
{}
void operator()(int sock,std::string ip,int port)
{
std::unordered_map<std::string,std::string> dict;
dict.insert({"apple","苹果"});
dict.insert({"banana","香蕉"});
dict.insert({"insert","插入"});
dict.insert({"left","左边"});
dict.insert({"right","右边"});
char buffer[64];
std::string value;
while(true)
{
ssize_t size = read(sock,buffer,sizeof(buffer) - 1);//返回值>0实际读取了多少字节;==0:说明对端关闭链接;<0:读取错误
if(size > 0)
{
//成功
// std::cout << "get a new link["<< atoi(ip) "]"<
buffer[size] = 0;
std::cout<< ip << ":" << port <<"# "<< buffer <<std::endl;
std::string key = buffer;
auto it = dict.find(key);
if(it != dict.end())
{
value = it->second;
}
else
{
//没找到
value = buffer;
}
write(sock,value.c_str(),value.size());//tcp socket中,读写都是一个文件描述符:sock(fd)
//全双工通信的体现
}
else if(size == 0)
{
//对端把链接关闭
std::cout<< ip << ":" << port <<"close! " <<std::endl;
break;
}
else
{
//出错
std::cout << sock << "read error" << std::endl;
break;
}
}
//close(sock);
std::cout << "service done!"<< std::endl;
close(sock);
}
~Handler()
{}
};
class Task
{
private:
int sock;
std::string ip;
int port;
Handler handler;
public:
Task()
{}
Task(int _sock,std::string _ip,int _port)
:sock(_sock),ip(_ip),port(_port)
{}
void Run()
{
handler(sock,ip,port);
}
~Task()
{}
};