目录
1.server 服务端:
1.1 完整代码展示:
class TcpServer
{
public:
TcpServer(const uint16_t &port, const std::string &ip = defaultip) : listensock_(defaultfd), port_(port), ip_(ip)
{
}
void InitServer()
{
listensock_ = socket(AF_INET, SOCK_STREAM, 0);
if (listensock_ < 0)
{
lg(Fatal, "create socket, errno: %d, errstring: %s", errno, strerror(errno));
exit(SocketError);
}
lg(Info, "create socket success, listensock_: %d", listensock_);
int opt = 1;
setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port_);
inet_aton(ip_.c_str(), &(local.sin_addr));
// local.sin_addr.s_addr = INADDR_ANY;
if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
{
lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
exit(BindError);
}
lg(Info, "bind socket success, listensock_: %d", listensock_);
// Tcp是面向连接的,服务器一般是比较“被动的”,服务器一直处于一种,一直在等待连接到来的状态
if (listen(listensock_, backlog) < 0)
{
lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
exit(ListenError);
}
lg(Info, "listen socket success, listensock_: %d", listensock_);
}
// static void *Routine(void *args)
// {
// pthread_detach(pthread_self());
// ThreadData *td = static_cast<ThreadData *>(args);
// td->tsvr->Service(td->sockfd, td->clientip, td->clientport);//???
// delete td;
// return nullptr;
// }
void Start()
{
Daemon();
ThreadPool<Task>::GetInstance()->Start();
// for fork();
// signal(SIGCHLD, SIG_IGN);
lg(Info, "tcpServer is running....");
for (;;)
{
// 1. 获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno)); //?
continue;
}
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2. 根据新连接来进行通信
lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);
// std::cout << "hello world" << std::endl;
// version 1 -- 单进程版
// Service(sockfd, clientip, clientport);
// close(sockfd);
// version 2 -- 多进程版
// pid_t id = fork();
// if(id == 0)
// {
// // child
// close(listensock_);
// if(fork() > 0) exit(0);
// Service(sockfd, clientip, clientport); //孙子进程, system 领养
// close(sockfd);
// exit(0);
// }
// close(sockfd);
// // father
// pid_t rid = waitpid(id, nullptr, 0);
// (void)rid;
// version 3 -- 多线程版本
// ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
// pthread_t tid;
// pthread_create(&tid, nullptr, Routine, td);
// version 4 --- 线程池版本
Task t(sockfd, clientip, clientport);
ThreadPool<Task>::GetInstance()->Push(t);
}
}
// void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
// {
// // 测试代码
// char buffer[4096];
// while (true)
// {
// ssize_t n = read(sockfd, buffer, sizeof(buffer));
// if (n > 0)
// {
// buffer[n] = 0;
// std::cout << "client say# " << buffer << std::endl;
// std::string echo_string = "tcpserver echo# ";
// echo_string += buffer;
// write(sockfd, echo_string.c_str(), echo_string.size());
// }
// else if (n == 0)
// {
// lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
// break;
// }
// else
// {
// lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
// break;
// }
// }
// }
~TcpServer() {}
}
1.2 代码解析
1.2.1.创建套接字绑定和监听
void InitServer()
{
listensock_ = socket(AF_INET, SOCK_STREAM, 0);
if (listensock_ < 0)
{
lg(Fatal, "create socket, errno: %d, errstring: %s", errno, strerror(errno));
exit(SocketError);
}
lg(Info, "create socket success, listensock_: %d", listensock_);
int opt = 1;
setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启(tcp协议的时候再说)
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port_);
inet_aton(ip_.c_str(), &(local.sin_addr));
// local.sin_addr.s_addr = INADDR_ANY;
if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
{
lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
exit(BindError);
}
lg(Info, "bind socket success, listensock_: %d", listensock_);
// Tcp是面向连接的,服务器一般是比较“被动的”,服务器一直处于一种,一直在等待连接到来的状态
if (listen(listensock_, backlog) < 0)
{
lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
exit(ListenError);
}
lg(Info, "listen socket success, listensock_: %d", listensock_);
}
tcp是面向字节流传输的,也是需要绑定的套接字的,
和udp不同的是它需要 处于监听才可以进行链接
参考如下:
1.2.2 进行链接
void Start()
{
Daemon();
ThreadPool<Task>::GetInstance()->Start();
// for fork();
// signal(SIGCHLD, SIG_IGN);
lg(Info, "tcpServer is running....");
for (;;)
{
// 1. 获取新连接
struct sockaddr_in client;
socklen_t len = sizeof(client);
int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
if (sockfd < 0)
{
lg(Warning, "accept error, errno: %d, errstring: %s", errno, strerror(errno)); //?
continue;
}
uint16_t clientport = ntohs(client.sin_port);
char clientip[32];
inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));
// 2. 根据新连接来进行通信
lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);
std::cout << "hello world" << std::endl;
// version 1 -- 单进程版
Service(sockfd, clientip, clientport);
close(sockfd);
}
}
void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
{
// 测试代码
char buffer[4096];
while (true)
{
ssize_t n = read(sockfd, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
std::cout << "client say# " << buffer << std::endl;
std::string echo_string = "tcpserver echo# ";
echo_string += buffer;
write(sockfd, echo_string.c_str(), echo_string.size());
}
else if (n == 0)
{
lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
break;
}
else
{
lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
break;
}
}
}
这是一个单进程版本,
因为tcp面向字节流的,所以可以用write(),read()这些面向字节流的操作
参考如下:
1.2.3 功能拓展
1.2.3.1 多进程版本
version 2 -- 多进程版
pid_t id = fork();
if(id == 0)
{
// child
close(listensock_);
if(fork() > 0) exit(0);
Service(sockfd, clientip, clientport); //孙子进程, system 领养
close(sockfd);
exit(0);
}
close(sockfd);
// father
pid_t rid = waitpid(id, nullptr, 0);
(void)rid;
原理解析:
父进程fork(),在子进程中再 fork(), < 0 是孙子进程 ,将子进程退出。父进程直接等待,继续链接, 孙子进程怎是提供服务。
父进程和孙子进程就可以并发访问了,原理是让子进程退出,父进程直接等待完毕。
孙子进程不用管,他会被系统领养,会被回收。
1.2.3.2 多线程版本
version 3 -- 多线程版本
ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
pthread_t tid;
pthread_create(&tid, nullptr, Routine, td);
static void *Routine(void *args)
{
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData *>(args);
td->tsvr->Service(td->sockfd, td->clientip, td->clientport);//???
delete td;
return nullptr;
}
创建routine()函数实现多线程模式
1.2.3.3 线程池模式
// version 4 --- 线程池版本
Task t(sockfd, clientip, clientport);
ThreadPool<Task>::GetInstance()->Push(t);
1.3 创建task类,实现功能
#pragma once
#include <iostream>
#include <string>
#include "Log.hpp"
#include "Init.hpp"
extern Log lg;
Init init;
class Task
{
public:
Task(int sockfd, const std::string &clientip, const uint16_t &clientport)
: sockfd_(sockfd), clientip_(clientip), clientport_(clientport)
{
}
Task()
{
}
void run()
{
std::cout << "in task" << std::endl;
// 测试代码
char buffer[4096];
// Tcp是面向字节流的,你怎么保证,你读取上来的数据,是"一个" "完整" 的报文呢?
ssize_t n = read(sockfd_, buffer, sizeof(buffer)); // BUG?
if (n > 0)
{
buffer[n] = 0;
std::cout << "client key# " << buffer << std::endl;
std::string echo_string = init.translation(buffer);
// sleep(5);
// // close(sockfd_);
// lg(Warning, "close sockfd %d done", sockfd_);
// sleep(2);
n = write(sockfd_, echo_string.c_str(), echo_string.size()); // 100 fd 不存在
if(n < 0)
{
lg(Warning, "write error, errno : %d, errstring: %s", errno, strerror(errno));
}
}
else if (n == 0)
{
lg(Info, "%s:%d quit, server close sockfd: %d", clientip_.c_str(), clientport_, sockfd_);
}
else
{
lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd_, clientip_.c_str(), clientport_);
}
close(sockfd_);
}
void operator()()
{
run();
}
~Task()
{
}
private:
int sockfd_;
std::string clientip_;
uint16_t clientport_;
};