Linux篇:网络基础1

一、网络基础:网络本质就是在获取和传输数据,而系统的本质是在加工和处理数据。

1、应用问题:
①如何处理发来的数据?—https/http/ftp/smtp
②长距离传输的数据丢失的问题?——TCP协议
③如何定位的主机的问题?——IP协议
④怎么保证数据能准确到达下一个设备?——数据链路层

2、协议:
①协议是双方的约定,最终的表现形式就是具有特定字段的结构体对象。
②协议分层:软件分层最典型的表现是:在结构体或类的设计上,做数据结构层面的分离/用回调函数的方式,在软件的逻辑上进行分层。
③通过高内聚,低耦合,大大降低软件维护成本。

3、
①OSI七层模型(略)。
②TCP/IP五层(或四层)模型:(物理层、)数据链路层、网络层、传输层、应用层(像这样的网络协议栈命名为TCP/IP协议栈)。
③其中,传输层和网络层是Linux内核中的模块,也就是操作系统。
④网络通信的本质就是贯穿协议栈的过程。
⑤网络协议栈的层状结构中,每一层都有协议。

4、报文=报头+有效载荷
①通信的过程,本质就是不断的封装和解包的过程!
②扩展(为封装和解包提供前提,是大部分协议的共性,未来我们学习具体协议的时候都会围绕这两个问题):
a.几乎任何层的协议都要提供一种能将报头和有效载荷分离的能力。
b.几乎任何层的协议都要在报头中提供决定将自己的有效载荷交付给上层的哪一个协议的能力(报文的分用)。

5、以太网通信(每台主机在局域网上都要有一个自己唯一的编号):
①以太网(局域网)通信原理。
②Mac地址:能够标识特定一台主机在局域网中的唯一性。
③以太网发生数据碰撞问题,发送主机都要执行的碰撞避免算法(延迟发送,错峰处理)(碰撞域)。
④网卡的工作模式:正常模式、混杂模式(抓包原理)。
⑤交换机:划分碰撞域,防止报文在更大的局域网中扩散,有效降低数据在局域网中碰撞的概率。
⑥如何看待局域网:一个时刻只允许一个主机向局域网中投递数据。使用这个共享资源(临界资源)时,要保证该共享资源的互斥访问。

6、IP地址:
①IP地址存在的意义:能够标识特定一台主机在全网当中的唯一性。能够指导我们进行路径规划。
②网络可以根据目的IP中当前主机和目标主机是否在同一网段来决定是否将数据交给路由器(本质就是局域网通信)。
③IP报文在全球子网自由通信的原因:路由器会根据目标子网重新解包封装一个符合当前局域网的新报头。
④IP协议通过工作在IP层的路由器屏蔽了底层网络的差异化。
⑤IP实现了全球主机的软件虚拟层,一切皆是IP报文。
⑥IP地址和MAC地址的区别:
IP地址,尤其是目的IP,一般都是不会改变的,协助我们进行路径选择。
MAC地址,出局域网之后,源和目都要被丢弃,让路由器重新封装。
⑦在应用层一般把数据包叫做请求与响应,在传输层一般叫做数据段/数据包,在网络层一般叫做数据报,在链路层一般叫做数据帧。

7、网络通信:
①网络协议中的下三层主要解决的是数据安全可靠的送到远端机器。
②用户使用应用层软件,完成数据发送和接受。
③我们日常网络通信的本质就是进程间通信。
④通过网络协议栈,利用网络资源,让不同的进程看到同一份资源。

8、端口号:
①端口号:无论对于client和server,都能唯一的标识该主机上的一个网络应用层的进程。
②在公网上,IP地址能表示唯一的一台主机。
③所以,IP:Port能够标识全网唯一的一个进程。
④源IP:源端口   client_ip:client_port
目的IP:目的端口   server_ip:server_port
这种基于IP加端口的方式称为socket。
⑤端口号vs进程PID(PID已经能够标识一台主机上进程的唯一性了,为什么还要搞一个端口号?):
a.不是所有的进程都要网络通信,但是所有进程都要有PID。
b.系统网络功能解耦。
⑥我们的客户端如何知道服务器的端口号是多少?
每一个服务的端口号必须是众所周知,精心设计,被客户端知晓的。
⑦一个进程可以绑定多个端口号吗?可以。
一个端口号可以被多个进程绑定吗?不行。

9、
TCP协议(传输控制协议):
①传输层协议。
②有连接。
③可靠传输。
④面向字节流。

UDP协议(用户数据报协议):
①传输层协议。
②无连接。
③不可靠传输。
④面向数据报。

二、Socket编程接口:

套接字编程的种类:
①域间套接字编程:另一个主机内。
②原始套接字编程:网络工具。
③网络套接字编程:用户间的网络通信。
想将网络接口统一抽象化(参数的类型必须是统一的)。

三、UDP协议实现:

##Makefile.PHONY:all
all:udpserver udpclient
udpserver:Main.ccg++ -o $@ $^ -std=c++11
udpclient:UdpClient.ccg++ -o $@ $^ -lpthread -std=c++11
.PHONY:clean
clean:rm -f udpserver udpclient
// Main.cc#include "UdpServer.hpp"
#include <memory>
#include <cstdio>
#include <vector>// "123.60.161.111" 点分十进制字符串风格的IP地址void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}// std::string Handler(const std::string &info, const std::string &clientip, uint16_t clientport)
// {
//     std::cout << "[" << clientip << ":" << clientport << "]#" << info << std::endl;
//     std::string res = "Server get a message: ";
//     res += info;
//     std::cout << res << std::endl;//     return res;
// }// bool SafeCheck(const std::string &cmd)
// {
//     int safe = false;
//     std::vector<std::string> key_word = {
//         "rm",
//         "mv",
//         "cp",
//         "kill",
//         "sudo",
//         "unlink",
//         "uninstall",
//         "yum",
//         "top",
//         "while"
//     };//     for(auto &word : key_word)
//     {
//         auto pos = cmd.find(word);
//         if(pos != std::string::npos) return false;
//     }
//     return true;
// }// std::string ExcuteCommand(const std::string &cmd)
// {
//     std::cout << "get a request cmd: " << cmd << std::endl;
//     if(!SafeCheck(cmd)) return "Bad man";//     FILE *fp = popen(cmd.c_str(), "r");
//     if(nullptr == fp)
//     {
//         perror("popen");
//         return "error";
//     }//     std::string result;
//     char buffer[4096];
//     while(true)
//     {
//         char *ok = fgets(buffer, sizeof(buffer), fp);
//         if(ok == nullptr) break;
//         result += buffer;
//     }
//     pclose(fp);//     return result;
// }// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);// std::unique_ptr<UdpServer> svr(new UdpServer(8080, "123.60.161.111")); // 如果是虚拟机,这段代码是可以执行的。但是云服务器禁止bind公网IP。bind(IP:0),凡是发给我这台主机的数据,我们都要根据端口号向上交付(任意地址bind)。// std::unique_ptr<UdpServer> svr(new UdpServer(8080));std::unique_ptr<UdpServer> svr(new UdpServer(port, "127.0.0.1")); // 本地环回地址,只能用于本地进程间通信,通常用它来进行cs的测试。svr->Init();svr->Run();return 0;
}
// UdpServer.hpp#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "Log.hpp"// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string &, const std::string &, uint16_t)> func_t;Log lg;enum{SOCKET_ERR=1,BIND_ERR
};uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;class UdpServer
{
public:UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip), isrunning_(false){}void Init(){// 1、创建udp socket(Udp的socket是全双工的,允许同时被读写)sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INETif(sockfd_ < 0){lg(Fatal, "socket create error, sockfd: %d", sockfd_);exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2、bind socketstruct sockaddr_in local; // 结构提可以用{}初始化,不能用{}赋值bzero(&local, sizeof(local)); // 将一段内存初始化为0local.sin_family = AF_INET; // 表明自身结构体类型local.sin_port = htons(port_); // 需要保证该端口号是网络字节序列,因为该端口号是要给对方发送的// local.sin_addr.s_addr = inet_addr(ip_.c_str()); // 1、string -> uint32_t 2、uint32_t必须是网络序列的local.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind errno , error: %d, err string: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));}void CheckUser(const struct sockaddr_in &client, const std::string clientip, uint16_t port){auto iter = online_user_.find(clientip);if(iter == online_user_.end()){online_user_.insert({clientip, client});std::cout << "[" << clientip << ":" << port << "] add to online user." << std::endl;}}void Broadcast(const std::string &info, const std::string clientip, uint16_t clientport){for(const auto &user : online_user_){std::string message = "[";message += clientip;message += ":";message += std::to_string(clientport);message += "]#";message += info;socklen_t len = sizeof(user.second);sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr*)(&user.second), len);}}// void Run(func_t func) // 对代码进行分层void Run(){isrunning_ = true;char inbuffer[size];while(isrunning_){struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}uint16_t clientport = ntohs(client.sin_port);std::string clientip = inet_ntoa(client.sin_addr);CheckUser(client, clientip, clientport);std::string info = inbuffer;Broadcast(info, clientip, clientport);}}~UdpServer(){if(sockfd_ > 0) close(sockfd_);}
private:int sockfd_; // 网络文件描述符std::string ip_; // 任意地址binduint16_t port_; // 表明服务器进程的端口号bool isrunning_;std::unordered_map<std::string, struct sockaddr_in> online_user_;
};
// UdpClient.cc#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n" << std::endl;
}struct ThreadData
{struct sockaddr_in server;int sockfd;std::string serverip;
};void *recv_message(void *args)
{ThreadData *td = static_cast<ThreadData *>(args);char buffer[1024];while(true){struct sockaddr_in temp;socklen_t len = sizeof(temp);ssize_t s = recvfrom(td->sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);if(s > 0){buffer[s] = 0;std::cerr << buffer << std::endl;}}
}void *send_message(void *args)
{ThreadData *td = static_cast<ThreadData *>(args);std::string message;socklen_t len = sizeof(td->server);std::string welcome = td->serverip;welcome += " coming...";sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&td->server, len);while(true){std::cout << "Please Enter@ ";std::getline(std::cin, message);// std::cout << message << std::endl;// 1、数据 2、给谁发sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&td->server, len);}
}// 多线程
// ./udpclient serverip serverport
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);struct ThreadData td;bzero(&td.server, sizeof(td.server)); // 将一段内存初始化为0td.server.sin_family = AF_INET;td.server.sin_port = htons(serverport); td.server.sin_addr.s_addr = inet_addr(serverip.c_str());td.sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(td.sockfd < 0){std::cout << "socket error" << std::endl;return 1;}td.serverip = serverip;pthread_t recvr, sender;pthread_create(&recvr, nullptr, recv_message, &td);pthread_create(&sender, nullptr, send_message, &td);// client也要bind,只不过不需要用户显示bind。一般由OS自由随机选择。// 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此。// 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以。// 系统在首次发送数据的时候bind。pthread_join(recvr, nullptr);pthread_join(sender, nullptr);close(td.sockfd);return 0;
}
// Log.hpp#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch(level){case Info: return "Info";case Debug: return "Debug";case Warning: return "Warning";case Error: return "Error";case Fatal: return "Fatal";default: return "None";}}void printLog(int level, const std::string &logtxt){switch(printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666); // "log.txt"if(fd < 0) return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(), ctime->tm_year+1900, ctime->tm_mon+1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);//格式:默认部分+自定义部分char logtxt[SIZE*2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt);//暂时打印printLog(level, logtxt);}
private:int printMethod;std::string path;
};

 四、TCP协议实现:

// Makefile.PHONY:all
all:tcpserverd tcpclient
tcpserverd:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
tcpclient:TcpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f tcpserverd tcpclient
// Main.cc#include "TcpServer.hpp"
#include <iostream>
#include <memory>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}// ./tcpserver 8080
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(UsageError);}uint16_t port = std::stoi(argv[1]);lg.Enable(Classfile);std::unique_ptr<TcpServer> tcp_svr(new TcpServer(port));tcp_svr->InitServer();tcp_svr->Start();return 0;
}
// TcpServer.hpp#pragma once#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 但是一般不要设置的太大
extern Log lg;enum
{UsageError = 1,SocketError,BindError,ListenError,
};class TcpServer;class ThreadData
{
public:ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t): sockfd(fd), clientip(ip), clientport(p), tsvr(t){}
public:int sockfd;std::string clientip;uint16_t clientport;TcpServer *tsvr;
};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, "socket create error, listensock: %d", listensock_);exit(SocketError);}lg(Info, "socket create success, : %d", listensock_);int opt = 1;setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启struct sockaddr_in local;bzero(&local, 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 errno , error: %d, err string: %s", errno, strerror(errno));exit(BindError);}lg(Info, "bind socket success, listensock: %d", listensock_);// Tcp是面向连接的,服务器一般是比较“被动的”,服务器一直处于等待连接的状态if(listen(listensock_, backlog) < 0){lg(Fatal, "listen errno , error: %d, err string: %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();// signal(SIGPIPE, SIG_IGN);ThreadPool<Task>::GetInstance()->Start();// 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 errno , error: %d, err string: %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\n", 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); // 孙子进程由系统领养//     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(), sockfd, clientport);//             break;//         }//         else//         {//             lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);//             break;//         }//     }// }~TcpServer(){}
private:int listensock_; // 监听套接字std::string ip_;uint16_t port_; 
};
// TcpClient.cc#include <iostream>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>void Usage(const std::string &proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n" << std::endl;
}// ./tcpclient serverip serport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));// local.sin_addr.s_addr = INADDR_ANY;while (true){int cnt = 20;int isreconnect = false;int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return 1;}do{// tcp客户端要bind,但不需要显示bind。// 客户端发起connect的时候,进行自动随机bind。int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){isreconnect = true;cnt--;std::cerr << "connect error..., reconnect: " << cnt << std::endl;sleep(1);}else{break;}} while (cnt && isreconnect);if (cnt == 0){std::cerr << "user offline..." << std::endl;break;}// while (true)// {std::string message;std::cout << "Please Enter# ";std::getline(std::cin, message);int n = write(sockfd, message.c_str(), message.size());if (n < 0){std::cerr << "write error..." << std::endl;// break;}char inbuffer[4096];n = read(sockfd, inbuffer, sizeof(inbuffer));if (n > 0){inbuffer[n] = 0;std::cout << inbuffer << std::endl;}// else{//     break;// }// }close(sockfd);}return 0;
}
// Task.hpp#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(){// 测试代码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_;
};
// ThreadPool.hpp#pragma once#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>struct ThreadInfo
{pthread_t tid;std::string name;
};static const int defalutnum = 10;template <class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(pthread_t tid){for (const auto &ti : threads_){if (ti.tid == tid)return ti.name;}return "None";}public:static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}T Pop(){T t = tasks_.front();tasks_.pop();return t;}void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}static ThreadPool<T> *GetInstance(){if (nullptr == tp_) // ???{pthread_mutex_lock(&lock_);if (nullptr == tp_){std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:ThreadPool(int num = defalutnum) : threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:std::vector<ThreadInfo> threads_;std::queue<T> tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
// Init.hpp#pragma once#include <iostream>
#include <string.h>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"const std::string dictname = "./dict.txt";
const std::string sep = ":";static bool Split(std::string &s, std::string *part1, std::string *part2)
{auto pos = s.find(sep);if(pos == std::string::npos) return false;*part1 = s.substr(0, pos);*part2 = s.substr(pos+1);return true;
}class Init
{
public:Init(){std::ifstream in(dictname);if(!in.is_open()){lg(Fatal, "ifstream open %s error", dictname.c_str());exit(1);}std::string line;while(std::getline(in, line)){std::string part1, part2;Split(line, &part1, &part2);dict.insert({part1, part2});}in.close();}std::string translation(const std::string &key){auto iter = dict.find(key);if(iter == dict.end()) return "Unknown";else return iter->second;}
private:std::unordered_map<std::string, std::string> dict;
};

守护进程化:

// Daemon.hpp#pragma once#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string nullfile = "/dev/null";void Daemon(const std::string &cwd = "")
{// 1、忽略其他异常信号signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2、将自己变成独立的回话if(fork() > 0) exit(0);setsid();// 3、更改当前调用进程的工作目录if(!cwd.empty()) chdir(cwd.c_str());// 4、标准输入、标准输出、标准错误重定向至/dev/nullint fd = open(nullfile.c_str(), O_RDWR);if(fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}
}
// Log.hpp#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch(level){case Info: return "Info";case Debug: return "Debug";case Warning: return "Warning";case Error: return "Error";case Fatal: return "Fatal";default: return "None";}}void printLog(int level, const std::string &logtxt){switch(printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY|O_CREAT|O_APPEND, 0666); // "log.txt"if(fd < 0) return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(), ctime->tm_year+1900, ctime->tm_mon+1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);//格式:默认部分+自定义部分char logtxt[SIZE*2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt);//暂时打印printLog(level, logtxt);}
private:int printMethod;std::string path;
};Log lg;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/680974.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis集中管理Session和系统初始化参数详解

Redis 是一个开源的、基于内存的键值存储系统&#xff0c;通常用作数据库、缓存或消息传递系统。在 Web 应用程序中&#xff0c;Redis 常用于集中管理 Session 数据和系统初始化参数。 Redis 管理 Session Session 是 Web 应用程序中用于保持用户状态的一种机制…

鸿蒙小案例-你画我猜

鸿蒙小案例-你画我猜 1.准备组件(组件布局) 2.实现跟随鼠标画笔画出图案功能 3.实现复制上面的画笔的图案功能 4.其他小功能1.组件的准备 画布的组件官方给的API是Canvas&#xff0c;需要传递一个参数CanvasRenderingContext2D 直接搜索API 使用官方案例 private settings: …

L2-1 点赞狂魔——pta(题解)

微博上有个“点赞”功能&#xff0c;你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签&#xff0c;而你点赞的博文的类型&#xff0c;也间接刻画了你的特性。然而有这么一种人&#xff0c;他们会通过给自己看到的一切内容点赞来狂刷存在感&#xff0c;这…

【蓝桥杯Python】试题 算法训练 藏匿的刺客

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 强大的kAc建立了强大的帝国&#xff0c;但人民深受其学霸及23文化的压迫&#xff0c;于是勇敢的鹏决心反抗。   kAc帝国防守…

C语言——求 1+12+123+……前 n 项之和,其中n 是从键盘输入的一个数字(1~9)。

一、问题 求 112123……前 n 项之和&#xff0c;其中n 是从键盘输入的一个数字&#xff08;1~9&#xff09; 二、解答 #include <stdio.h>int main() {int n, i, term 0, sum 0;printf("请输入一个数字(1~9): ");scanf("%d", &n);if (n <…

书生谱语-基于 InternLM 和 LangChain 搭建知识库

大语言模型与外挂知识库&#xff08;RAG&#xff09;的优缺点 RAG方案构建与优化 作业 在创建web_demo时&#xff0c;需要根据教程将服务器端口映射到本地端口&#xff0c;另外需要将链接的demo从服务器中复制出来&#xff0c;不要直接从服务器打开demo页面&#xff0c;不然会…

分布式事务详解

概述 随着互联网的发展&#xff0c;软件系统由原来的单体应用转变为分布式应用。分布式系统把一个单体应用拆分为可独立部署的多个服务&#xff0c;因此需要服务与服务之间远程协作才能完成事务操作。这种分布式系统下不同服务之间通过远程协作完成的事务称之为分布式事务&…

c# 拼音 帮助类

public class PinYinHelper { #region 获取汉字对应的拼音 /// <summary> /// 获取汉字对应的拼音 /// </summary> /// <param name"str"></param> /// <returns></returns> …

JavaScript中有哪些不同的数据类型

在 JavaScript 中&#xff0c;数据类型是一种用来表示数据的分类&#xff0c;它决定了我们可以对这个数据类型执行哪些操作。在 JavaScript 中有以下几种不同的数据类型&#xff1a; 基本数据类型 字符串 (String)&#xff1a;表示一组字符&#xff0c;可以使用引号&#xff08…

ElasticSearch级查询Query DSL上

目录 ES高级查询Query DSL match_all 返回源数据_source 返回指定条数size 分页查询from&size 指定字段排序sort 术语级别查询 Term query术语查询 Terms Query多术语查询 exists query ids query range query范围查询 prefix query前缀查询 wildcard query通…

CVE-2022-25487 漏洞复现

漏洞描述&#xff1a;Atom CMS 2.0版本存在远程代码执行漏洞&#xff0c;该漏洞源于/admin/uploads.php 未能正确过滤构造代码段的特殊元素。攻击者可利用该漏洞导致任意代码执行。 其实这就是一个文件上传漏洞罢了。。。。 打开之后&#xff0c;/home路由是个空白 信息搜集&…

第十六章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性

文章目录 第十六章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性GetOneRow()GetParameter()GetUV()Password propertySetConnectOption()SetParameter()SQL 代码属性UnloadDLL()用户属性 第十六章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性…

controller-manager学习三部曲之三:deployment的controller启动分析

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 《controller-manager学习三部曲》完整链接 通过脚本文件寻找程序入口源码学习deployment的controller启动分析 本篇概览 本文是《controller-manager学习三…

STM32定时器按键扫描检测(短按)并执行其他内容的源码

文章目录 main.c #include "stm32f10x.h" // Device header #include "Key.h" #include "LED.h" #include "Timer.h"int main(void) {Timer_Init();//1msLED_Init();Key_Init();while(1){switch(KeyFlag)//按键短按&…

深入了解JavaScript混淆工具:jsjiami.v6

JavaScript混淆工具在前端开发中发挥着重要的作用&#xff0c;帮助开发者保护源代码&#xff0c;减少代码被轻易破解的风险。其中&#xff0c;jsjiami.v6 是一款备受开发者关注的混淆工具之一。本文将深入介绍jsjiami.v6的基本原理和使用方法&#xff0c;并通过案例代码演示其效…

0时区格林威治时间转换手机当地时间-Android(2023-11-01T12:59:10.420987)

假设传入的是2023-11-01T12:59:10.420987这样的格式 要将格式为2023-11-01T12:59:10.420987的UTC时间字符串转换为Android设备本地时间&#xff0c;您可以使用java.time包中的类&#xff08;在API 26及以上版本中可用&#xff09;。如果您的应用需要支持较低版本的Android&…

普通男孩的新年创作纪念日

前言 首先在新春佳节&#xff0c;小编在这里祝各位大佬。萌新友友们新年好&#xff0c;希望每一个烟火般的你在新的一年里 offer 多多&#xff0c;薪资多多 &#xff0c;龙行龘龘 &#x1f409; &#x1f409; &#x1f409; &#x1f409;&#xff0c;前程朤朤 ❤️ ❤️ ❤…

【C++】内存详解(堆,栈,静态区)

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Python电影下载利器:全网资源一键触达

本文分享给大家的是一款全网电影下载利器&#xff0c;采用requests库开发&#xff0c;支持下载各大网站的m3u8格式视频。 与一般下载工具不同&#xff0c;该工具具备强大的功能&#xff0c;能够完整地下载整个电影&#xff0c;并保存为mp4格式文件。 传统下载工具如IDM通常只…

【python】网络爬虫与信息提取--Beautiful Soup库

Beautiful Soup网站&#xff1a;https://www.crummy.com/software/BeautifulSoup/ 作用&#xff1a;它能够对HTML.xml格式进行解析&#xff0c;并且提取其中的相关信息。它可以对我们提供的任何格式进行相关的爬取&#xff0c;并且可以进行树形解析。 使用原理&#xff1a;它能…