广州建网站白云区建设公司和建筑公司哪个好
- 作者: 五速梦信息网
- 时间: 2026年04月20日 11:05
当前位置: 首页 > news >正文
广州建网站白云区,建设公司和建筑公司哪个好,上海公司网站建设,控制台网站本篇将使用 Linux 中的系统调用来实现模拟 TCP 和 UDP 的通信过程#xff0c;其中只对 UDP 和 TCP 进行了简单的介绍#xff0c;本篇主要实现的是代码#xff0c;至于 UDP 和 TCP 的详细讲解将会在之后的文章中给出。 本篇给出的 tcp 和 udp 的代码中的 echo 都是测试连接是… 本篇将使用 Linux 中的系统调用来实现模拟 TCP 和 UDP 的通信过程其中只对 UDP 和 TCP 进行了简单的介绍本篇主要实现的是代码至于 UDP 和 TCP 的详细讲解将会在之后的文章中给出。 本篇给出的 tcp 和 udp 的代码中的 echo 都是测试连接是否成功的代码之后的代码都是在 echo 代码的基础上修改实现了不同功能的代码。目录如下 目录 网络字节序/Socket编程接口 1. socket 常见 API 2. sockaddr 结构 UDP Socket编程 1. echo server 2. Dict server 3. chat_server TCP Socket编程 1. echo server 2. command server 网络字节序/Socket编程接口 在内存中的多字节数据相对于内存地址有着大小端之分磁盘文件中的多字节数据相对于文件中的偏移地址也有着大小端之分。但是对于不同的主机有的是小端存储有的是大端存储然而在网络间通信是从一个主机传输到另一个主机我们应该怎样区分传送过来的数据是小端数据还是大端数据呢 所以 TCP/IP 为了区分这两种存储方式规定了在网络间传输数据必须都按照大端数据流进行传输小端机器在传输前需要将数据转换成大端数据流。因此网络间先发出的数据是低地址后发出的数据在高地址。 转换函数如下 #include arpa/inet.huint32_t htonl(uint32_t hostlong); // 32 位主机序列转入网络序列 uint16_t htons(uint16_t hostshort); // 16 位主机序列转入网络序列 uint32_t ntohl(uint32_t netlong); // 32 位网络序列转入主机序列 uint16_t ntohs(uint16_t netshort); // 16 位网络序列转入主机序列以上的转换函数 h host代表主机 n network代表网络若主机是小端字节序这些函数将参数做相应的大小端转换然后返回 若主机是大端字节序这些函数不做转换将参数原封不动的返回。 1. socket 常见 API 一下为 socket 编程常用的接口函数如下 // 创建 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 socket, int backlog);// 接收请求 (TCP, 服务器) int accept(int socket, struct sockaddr address, socklen_t* address_len);// 建立连接 (TCP, 客户端) int connect(int sockfd, const struct sockaddr addr, socklen_t addrlen); 2. sockaddr 结构 上文中的 socket API 是一层抽象的网络编程接口适用于各种底层网络协议如 IPv4、IPv6但是我们发现其中大部分接口都有一个结构体struct sockaddr 这个结构体其实有分为两个不同的结构体struct sockaddr_in、struct sockaddr_un前者用于网络间通信后者用于主机内通信如下 当我们将 struct sockaddr_in、struct sockaddr_un 这两种类型的数据传入接口中只需要使用指针强转为 struct sockaddr 即可。 UDP Socket编程 UDP 协议是在传输层中常用的一种通信协议其主要特点如下 1. 传输层协议 2. 无连接 3. 不可靠传输 4. 面向数据报 以上的不可靠传输我们并不能将其认定为 UDP 协议的缺点只将其认定为 UDP 协议的一种的特性虽然是不可靠的传输但是 UDP 的效率相对 TCP 很高。 注本篇的代码量比较多其中大部分相同的代码都放在了 echo server 中之后的代码只会给出一些不同的代码文件。 1. echo server 在这一小节将写一个 UDP 协议的一个测试代码主要实现的功能为我们向服务器发送什么信息服务器就像我们返回什么信息。 UdpClient.cc #include iostream #include string #include cstring #include unistd.h #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #include Log.hppusing namespace log_ns;// 客户端在未来一定要知道服务器的ip port int main(int argc, char args[]) {if (argc ! 3) {LOG(ERROR, please input the - ./client、ip and port\n);return 0;}// 获取 ip 和 port 以及 socketuint16_t server_port std::stoi(args[2]);std::string server_ip args[1];int local_socket socket(AF_INET, SOCK_DGRAM, 0);if (local_socket 0) {LOG(FATAL, Create socket fail\n);exit(0);}LOG(INFO, Create socket success\n);// 对于client的端口号一般不让用户自己设定而是让client OS随机选择// client需要bind自己的ip和port但是client不需要显示的bind自己的ip、port// client在首次向服务器发送数据的时候OS会自动给client bind上它自己的ip、port// 绑定服务器信息struct sockaddr_in server;memset(server, 0, sizeof(server));socklen_t len sizeof(server);server.sin_family AF_INET;server.sin_port htons(server_port);server.sin_addr.s_addr inet_addr(server_ip.c_str());// 客户端的ip和port不需要指定自己会给出// 在这里就可以发送消息了while (true) {std::string info;std::cout Please input the info: ;std::getline(std::cin, info);if (info.size() 0) {// 将消息发送出去int num sendto(local_socket, info.c_str(), info.size(), 0, (struct sockaddr)server, len);if (num 0) {// 收消息struct sockaddr_in temp;char buff[1024];int recvlen recvfrom(local_socket, buff, sizeof(buff) - 1, 0, (struct sockaddr)temp, len);if (recvlen 0) {buff[recvlen] 0;std::cout buff std::endl;} else {break;}}} else {break;}}close(local_socket);return 0; } UdpServer.cc #include iostream #include memory #include UdpServer.hpp// 在这里也可以设计为 main(argc, args)多参数指定ip和port int main() {// 将日志内容打印到屏幕上EnableToScreen();// 服务器ip一般指定为0服务器可以收到来自任意ip的信息只要求端口对应UdpServer* usvr new UdpServer(127.0.0.1, 8899);usvr-Init();usvr-Start();return 0; } UdpServer.hpp #pragma once #include iostream #include string #include cstring #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #include unistd.h #include Log.hpp #include nocopy.hpp #include InetAddr.hppusing namespace log_ns;enum {SOCKET_ERROR 1,BIND_ERROR };const int gsockfd -1; const uint16_t glocalport 8888;// 网络中的很多东西不建议直接进行拷贝所以设计我们的类的时候 // 将其设置为不可拷贝的类 // 一般服务器主要用来进行网络数据读取和写入、IO的 // 我们可以将服务器的IO逻辑和业务逻辑解耦 class UdpServer : public nocopy { private:std::string ServerEcho(struct sockaddr_in peer) {// 获取发送方的ip portInetAdrr addr(peer);std::string echo([);echo addr.Ip();echo ;echo std::to_string(addr.Port());echo ] ;return echo;}public:// 构造函数传入ip和portUdpServer(const std::string ip, uint16_t port glocalport) : _sockfd(gsockfd),_localip(ip),_localport(port),_isrunning(false){}void Init() {// 先创建 sockfd 文件使用如下接口的最后一个参数设置为0// 会自动推测是哪个协议_sockfd ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0) {LOG(FATAL, Create sockfd fail\n);exit(SOCKET_ERROR);}LOG(INFO, Create Socket success\n);// 绑定我们的信息struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;// 将ip地址设置为0可以进行任意ip绑定 INADDR_ANY值为0local.sin_addr.s_addr INADDR_ANY; // 将指定的ip转化为网络序列// local.sin_addr.s_addr inet_addr(_localip.c_str());local.sin_port htons(_localport);socklen_t len sizeof(local);int n ::bind(_sockfd, (struct sockaddr)local, len);if (n 0) {LOG(FATAL, Bind socket fail, %s\n, strerror(errno));exit(BIND_ERROR);}LOG(INFO, Bind Socket success\n);}void Start() {_isrunning true;// 需要收消息while (_isrunning) {struct sockaddr_in peer;memset(peer, 0, sizeof(peer));socklen_t len sizeof(peer);// peer.sin_family AF_INET;// peer.sin_addr.s_addrchar buff[1024];int n recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr)peer, len);if (n 0) {buff[n] 0;std::string echo ServerEcho(peer);echo buff;sendto(_sockfd, echo.c_str(), echo.size(), 0, (struct sockaddr)peer, len);}}_isrunning false;}~UdpServer() {if (_sockfd gsockfd)::close(_sockfd);} private:int _sockfd;// 对于这个ip变量也可以直接不要这个参数直接让ip设置为0可以接收来自任意ip的信息std::string _localip; uint16_t _localport;bool _isrunning; }; nocopy.hpp #pragma once #include iostreamclass nocopy { public:nocopy() {}~nocopy() {}nocopy(const nocopy) delete;const nocopy operator(const nocopy) delete; }; Log.hpp - 日志Log程序C-CSDN博客 #pragma once #include iostream #include string #include cstdarg #include cstring #include fstream #include sys/types.h #include pthread.h #include unistd.hnamespace log_ns {enum { DEBUG 1, INFO, WARNING, ERROR, FATAL };// 定义日子真正需要记录的信息struct LogMessage {std::string _level;int _id;std::string _filename;int _filenumber;std::string _curtime;std::string _log_message;};#define SCREEN_TYPE 1#define FILE_TYPE 2const std::string defaultlogfile ./log.txt;pthread_mutex_t log_lock PTHREAD_MUTEX_INITIALIZER;class Log {private:std::string LevelToString(int level) {switch(level) {case DEBUG:return DEBUG;case INFO:return INFO;case WARNING:return WARNING;case ERROR:return ERROR;case FATAL:return FATAL;default:return UNKNOWN;}}std::string CurTime() {// 获取当前的时间戳time_t curtime time(nullptr);// 将当前时间戳转换成结构体struct tm now localtime(curtime);char buff[128];snprintf(buff, sizeof(buff), %d-%02d-%02d %02d:%02d:%02d, now-tm_year 1900,now-tm_mon 1,now-tm_mday,now-tm_hour,now-tm_min,now-tm_sec);return buff;}void Flush(const LogMessage lg) {// 打印日志的时候可能存在线程安全使用锁lock住pthread_mutex_lock(log_lock);switch(_type) {case SCREEN_TYPE:FlushToScreen(lg);break;case FILE_TYPE:FlushToFile(lg);break;}pthread_mutex_unlock(log_lock);}void FlushToFile(const LogMessage lg) {std::ofstream out;out.open(_logfile, std::ios::app); // 文件的操作使用追加if (!out.is_open()) return;char buff[2024];snprintf(buff ,sizeof(buff), [%s][%d][%s][%d][%s] %s,lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curtime.c_str(),lg._log_message.c_str()); out.write(buff, strlen(buff));out.close();}void FlushToScreen(const LogMessage lg) {printf([%s][%d][%s][%d][%s] %s,lg._level.c_str(),lg._id,lg._filename.c_str(),lg._filenumber,lg._curtime.c_str(),lg._log_message.c_str());}public:Log(std::string logfile defaultlogfile): _type(SCREEN_TYPE),_logfile(logfile){}void Enable(int type) {_type type;}void LoadMessage(std::string filename, int filenumber, int level, const char* format, …) {LogMessage lg;lg._level LevelToString(level);lg._filename filename;lg._filenumber filenumber;// 获取当前时间lg._curtime CurTime();// std::cout lg._curtime std::endl;lg._id getpid();// 获取可变参数va_list ap;va_start(ap, format);char buff[2048];vsnprintf(buff, sizeof(buff), format, ap);va_end(ap);lg._log_message buff;// std::cout lg._log_message;Flush(lg);}void ClearOurFile() {std::ofstream out;out.open(_logfile);out.close();}~Log() {}private:int _type;std::string _logfile;};Log lg;// LOG 宏 #define LOG(level, format, …) \do { \lg.LoadMessage(FILE, LINE, level, format, ##VA_ARGS); } while (0)#define EnableToScreen() \do { \lg.Enable(SCREEN_TYPE); } while (0)#define EnableToFile() \do { \lg.Enable(FILE_TYPE); } while (0)// 清理文件 #define ClearFile() \do { \lg.ClearOurFile(); } while (0) } InetAddr.hpp #pragma once #include iostream #include string #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.hclass InetAdrr {void ToHost(const struct sockaddr_in addr) {// inet_ntoa 函数不是线程安全的函数推荐使用 inet_ntop 函数// _ip inet_ntoa(addr.sin_addr);char ip_buff[32];// 该函数是网络序列转主机序列 :network to processinet_ntop(AF_INET, addr.sin_addr, ip_buff, sizeof(ip_buff));// 若想要将主机序列转换成网络序列使用函数 :// inet_pton(AF_INET, _ip.c_str(), (void)addr.sin_addr.s_addr); _ip ip_buff;_port ntohs(addr.sin_port);} public:InetAdrr(const struct sockaddr_in addr) : _addr(addr){ToHost(_addr);}std::string Ip() const {return _ip;}bool operator(const InetAdrr addr) {return (_port addr._port _ip addr._ip);}struct sockaddr_in Addr() const {return _addr;}std::string AddrString() const {return _ip : std::to_string(_port);}uint16_t Port() const {return _port;}~InetAdrr() {} private:uint16_t _port;std::string _ip;struct sockaddr_in _addr; }; makefile .PHONY:all all:server clientserver:UdpServer.ccg -o \( \)^ -stdc11 client:UdpClient.ccg -o \( \)^ -stdc11.PHONY:clean clean:rm -f server client 测试结果 2. Dict server 这里实现的是一个字典翻译程序连接上服务器后只需要输入想要翻译的单词就可以翻译出来不过单词库需要我们提前填充 UdpServer.hpp #pragma once #include iostream #include string #include functional #include cstring #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #include unistd.h #include Log.hpp #include nocopy.hpp #include InetAddr.hppusing namespace log_ns;enum {SOCKET_ERROR 1,BIND_ERROR };using func_t std::functionstd::string(const std::string);const int gsockfd -1; const uint16_t glocalport 8888;// 网络中的很多东西不建议直接进行拷贝所以设计我们的类的时候 // 将其设置为不可拷贝的类 // 一般服务器主要用来进行网络数据读取和写入、IO的 // 我们可以将服务器的IO逻辑和业务逻辑解耦 class UdpServer : public nocopy { private:std::string ServerEcho(struct sockaddr_in peer) {// 获取发送方的ip portInetAdrr addr(peer);std::string echo([);echo addr.Ip();echo ;echo std::to_string(addr.Port());echo ] ;return echo;}public:// 构造函数传入ip和portUdpServer(func_t func, const std::string ip, uint16_t port glocalport) : _func(func), _sockfd(gsockfd),_localip(ip),_localport(port),_isrunning(false){}void Init() {// 先创建 sockfd 文件使用如下接口的最后一个参数设置为0// 会自动推测是哪个协议_sockfd ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0) {LOG(FATAL, Create sockfd fail\n);exit(SOCKET_ERROR);}LOG(INFO, Create Socket success\n);// 绑定我们的信息struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_addr.s_addr INADDR_ANY; // 将ip地址设置为0可以进行任意ip绑定// local.sin_addr.s_addr inet_addr(_localip.c_str());local.sin_port htons(_localport);socklen_t len sizeof(local);int n ::bind(_sockfd, (struct sockaddr)local, len);if (n 0) {LOG(FATAL, Bind socket fail, %s\n, strerror(errno));exit(BIND_ERROR);}LOG(INFO, Bind Socket success\n);}void Start() {_isrunning true;// 需要收消息while (_isrunning) {struct sockaddr_in peer;memset(peer, 0, sizeof(peer));socklen_t len sizeof(peer);// peer.sin_family AF_INET;// peer.sin_addr.s_addrchar buff[1024];int n recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr)peer, len);if (n 0) {buff[n] 0;std::string echo ServerEcho(peer);echo _func(buff);sendto(_sockfd, echo.c_str(), echo.size(), 0, (struct sockaddr)peer, len);} }_isrunning false;}~UdpServer() {if (_sockfd 0)::close(_sockfd);} private:int _sockfd;std::string _localip;uint16_t _localport;bool _isrunning;func_t _func; }; UdpServer.cc #include iostream #include memory #include UdpServer.hpp #include Dict.hppconst std::string dict_path ./dict.txt;int main() {EnableToScreen();// 服务器ip一般指定为0服务器可以收到来自任意ip的信息只要求端口对应Dict dict(dict_path);func_t translate std::bind(Dict::Translate, dict, std::placeholders::_1);UdpServer* usvr new UdpServer(translate, 127.0.0.1, 8899);usvr-Init();usvr-Start();return 0; } UdpClient.cc #include iostream #include string #include cstring #include unistd.h #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #include Log.hppusing namespace log_ns;// 客户端在未来一定要知道服务器的ip port int main(int argc, char* args[]) {if (argc ! 3) {LOG(ERROR, please input the - ./client、ip and port\n);return 0;}// 获取 ip 和 port 以及 socketuint16_t server_port std::stoi(args[2]);std::string server_ip args[1];int local_socket socket(AF_INET, SOCK_DGRAM, 0);if (local_socket 0) {LOG(FATAL, Create socket fail\n);exit(0);}LOG(INFO, Create socket success\n);// 绑定服务器信息struct sockaddr_in server;memset(server, 0, sizeof(server));socklen_t len sizeof(server);server.sin_family AF_INET;server.sin_port htons(server_port);server.sin_addr.s_addr inet_addr(server_ip.c_str());// 客户端的ip和port不需要指定自己会给出// 在这里就可以发送消息了while (true) {std::string info;std::cout Please input the info: ;std::getline(std::cin, info);if (info.size() 0) {// 将消息发送出去int num sendto(local_socket, info.c_str(), info.size(), 0, (struct sockaddr)server, len);if (num 0) {// 收消息struct sockaddr_in temp;char buff[1024];int recvlen recvfrom(local_socket, buff, sizeof(buff) - 1, 0, (struct sockaddr)temp, len);if (recvlen 0) {buff[recvlen] 0;std::cout buff std::endl;}}} else {break;}}close(local_socket);return 0; } Dict.hpp #pragma once #include iostream #include string #include unordered_map #include fstream #include Log.hppusing namespace log_ns;const std::string sep : ;class Dict {void LoadDict(const std::string path) {std::ifstream in(path);if (!in.is_open()) {LOG(FATAL, load the dictionary failed\n);exit(0);}LOG(DEBUG, open the dictionary success\n);// 将数据加载到map中std::string info;while (std::getline(in, info)) {if (info.empty()) continue;size_t pos info.find(sep);if (pos std::string::npos) continue;// 现在将获取出来的字符串分隔开std::string key info.substr(0, pos);std::string value info.substr(pos sep.size());if (key.empty() || value.empty()) continue;// 走到这里就是正常可以加载的数据LOG(DEBUG, load the info: %s\n, info.c_str());_dict.insert(std::make_pair(key, value));}LOG(DEBUG, load the dictionary success\n);in.close();} public:Dict(const std::string path) : _dict_path(path){LoadDict(_dict_path);}std::string Translate(const std::string word) {if (word.empty() || !_dict.count(word))return None;return _dict[word];}~Dict() {} private:std::string _dict_path;std::unordered_mapstd::string, std::string _dict; }; dict.txt 这个文件可以自己填充 apple: 苹果 banana: 香蕉 cat: 猫 dog: 狗 book: 书 pen: 笔 happy: 快乐的 sad: 悲伤的 run: 跑 jump: 跳 teacher: 老师 student: 学生 car: 汽车 bus: 公交车 love: 爱 hate: 恨 hello: 你好 goodbye: 再见 summer: 夏天 winter: 冬天 测试结果 3. chat_server 接下来的代码为一个聊天室的代码只需要连接到我们的服务器端就可以发送消息了只要连接该服务器的客户端都可以收到来自其他客服端发送的消息。使用多线程的单例模式实现的 UdpClient.cc #include iostream #include string #include cstring #include functional #include unistd.h #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #include Log.hpp #include Thread.hppusing namespace log_ns;int GetSockfd() {int local_socket socket(AF_INET, SOCK_DGRAM, 0);if (local_socket 0) {LOG(FATAL, Create socket fail\n);exit(0);}LOG(INFO, Create socket success\n);return local_socket; }void SendInfo(int local_socket, uint16_t server_port, const std::string server_ip, const std::string name) {struct sockaddr_in server;memset(server, 0, sizeof(server));socklen_t len sizeof(server);server.sin_family AF_INET;server.sin_port htons(server_port);server.sin_addr.s_addr inet_addr(server_ip.c_str());while (true) {std::string info;std::cout Please input the info: ;std::getline(std::cin, info);if (info.size() 0)// 将消息发送出去int num sendto(local_socket, info.c_str(), info.size(), 0, (struct sockaddr *)server, len);elsebreak;} }void ReceiveInfo(int local_socket, const std::string name) {while (true) {struct sockaddr_in temp;socklen_t len sizeof(temp);char buff[1024];int recvlen recvfrom(local_socket, buff, sizeof(buff) - 1, 0, (struct sockaddr )temp, len);// 加锁的话还需要使用条件变量if (recvlen 0){buff[recvlen] 0;std::cout buff std::endl;}} }// 客户端在未来一定要知道服务器的ip port int main(int argc, char args[]) {if (argc ! 3) {LOG(ERROR, please input the - ./client、ip and port\n);return 0;}// 获取 ip 和 port 以及 socketuint16_t server_port std::stoi(args[2]);std::string server_ip args[1];int local_socket GetSockfd();// 现在需要将发送消息和接收消息的函数绑定func_t send_func std::bind(SendInfo, local_socket, server_port, server_ip, std::placeholders::_1);func_t recev_func std::bind(ReceiveInfo, local_socket, std::placeholders::_1);Thread receive_thread(recev_func, client receive);Thread send_thread(send_func, client send);// 绑定服务器信息receive_thread.Start();send_thread.Start();receive_thread.Join();send_thread.Join();close(local_socket);return 0; } UdpServer.cc #include iostream #include memory #include UdpServer.hpp #include Route.hppint main() {EnableToScreen();// 服务器ip一般指定为0服务器可以收到来自任意ip的信息只要求端口对应Route route_message;server_task_t forword_message std::bind(Route::ForwordMessage, route_message, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);UdpServer* usvr new UdpServer(forword_message, 127.0.0.1, 8899);usvr-Init();usvr-Start();return 0; } UdpServer.hpp #pragma once #include iostream #include string #include cstring #include functional #include netinet/in.h #include arpa/inet.h #include sys/types.h #include sys/socket.h #include unistd.h #include Log.hpp #include nocopy.hpp #include InetAddr.hppusing namespace log_ns;enum {SOCKET_ERROR 1,BIND_ERROR };const int gsockfd -1; const uint16_t glocalport 8888;using server_task_t std::functionvoid(int, const std::string, InetAdrr);// 网络中的很多东西不建议直接进行拷贝所以设计我们的类的时候 // 将其设置为不可拷贝的类 // 一般服务器主要用来进行网络数据读取和写入、IO的 // 我们可以将服务器的IO逻辑和业务逻辑解耦 class UdpServer : public nocopy { private:std::string ServerEcho(struct sockaddr_in peer) {// 获取发送方的ip portInetAdrr addr(peer);std::string echo([);echo addr.Ip();echo ;echo std::to_string(addr.Port());echo ] ;return echo;}public:// 构造函数传入ip和portUdpServer(server_task_t task, const std::string ip, uint16_t port glocalport) : _task(task),_sockfd(gsockfd),_localip(ip),_localport(port),_isrunning(false){}void Init() {// 先创建 sockfd 文件使用如下接口的最后一个参数设置为0// 会自动推测是哪个协议_sockfd ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0) {LOG(FATAL, Create sockfd fail\n);exit(SOCKET_ERROR);}LOG(INFO, Create Socket success\n);// 绑定我们的信息struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;local.sin_addr.s_addr INADDR_ANY; // 将ip地址设置为0可以进行任意ip绑定// local.sin_addr.s_addr inet_addr(_localip.c_str());local.sin_port htons(_localport);socklen_t len sizeof(local);int n ::bind(_sockfd, (struct sockaddr)local, len);if (n 0) {LOG(FATAL, Bind socket fail, %s\n, strerror(errno));exit(BIND_ERROR);}LOG(INFO, Bind Socket success\n);}void Start() {_isrunning true;// 需要收消息while (_isrunning) {struct sockaddr_in peer;memset(peer, 0, sizeof(peer));socklen_t len sizeof(peer);// peer.sin_family AF_INET;// peer.sin_addr.s_addrchar buff[1024];int n recvfrom(_sockfd, buff, sizeof(buff) - 1, 0, (struct sockaddr)peer, len);if (n 0) {InetAdrr addr(peer);buff[n] 0;std::string messeage buff;// 将消息转发出去LOG(INFO, begin to forword\n);_task(_sockfd, messeage, addr);LOG(INFO, return server\n);}}_isrunning false;}~UdpServer() {if (_sockfd 0)::close(_sockfd);} private:int _sockfd;std::string _localip;uint16_t _localport;bool _isrunning;server_task_t _task; }; ThreadPool.hpp #pragma once #include iostream #include queue #include vector #include string #include pthread.h #include Thread.hppconst int default_thread_num 5;using namespace log_ns;template typename T class ThreadPool { private:void LockQueue() {pthread_mutex_lock(_mutex);}void UnLockQueue() {pthread_mutex_unlock(_mutex);}void WakeUpThread() {pthread_cond_signal(_cond);}void SleepThread() {pthread_cond_wait(_cond, _mutex);}bool IsEmptyQueue() {return _task_queue.empty();}void HandlerTask(std::string name) {while (true) {LockQueue();while (IsEmptyQueue() _isrunning) {// 只有当队列为空以及在运行的状态才会继续向下运行LOG(DEBUG, %s sleep\n, name.c_str());_sleep_thread_num;SleepThread();_sleep_thread_num–;LOG(DEBUG, %s wakeup\n, name.c_str());}// 当队列为空且不运行时自动退出if (IsEmptyQueue() !_isrunning) {// std::cout name quit… std::endl;LOG(DEBUG, %s quit…\n, name.c_str());UnLockQueue();break;}// 运行到这个位置任务队列中一定有元素且愿意运行下去T t _task_queue.front();_task_queue.pop();t();// t 执行任务// std::cout name - t.result() std::endl;// LOG(DEBUG, %s - %s\n, name.c_str(), t.result().c_str());UnLockQueue();}}ThreadPool(int threadnum default_thread_num): _thread_num(default_thread_num),_sleep_thread_num(0),_isrunning(false){pthread_mutex_init(_mutex, nullptr);pthread_cond_init(_cond, nullptr);}ThreadPool(const ThreadPoolT tp) delete;ThreadPool operator(const ThreadPoolT tp) delete;void Init() {// 将线程池内中的handler任务绑定this让其可以传入线程中运行func_t func std::bind(ThreadPool::HandlerTask, this, std::placeholders::_1);for (int i 0; i _thread_num; i) {std::string name thread- std::to_string(i 1);_threads.emplace_back(func, name);LOG(DEBUG, %s init\n, name.c_str());}}void Start() {// 将线程池的状态设置为运行状态_isrunning true;for (auto thread : _threads)thread.Start();}public:static ThreadPoolT* GetInstance() {if (_tp nullptr) {// 创建线程池可能存在线程安全的问题pthread_mutex_lock(_sig_mutex);if (_tp nullptr) {_tp new ThreadPool();_tp-Init();_tp-Start();LOG(INFO, create thread pool\n);}pthread_mutex_unlock(_sig_mutex);} else {LOG(INFO, get thread pool\n);}return _tp;}void Stop() {LockQueue();_isrunning false;// 唤醒所有线程让线程退出pthread_cond_broadcast(_cond);UnLockQueue();LOG(DEBUG, thread pool stop\n);}void Push(const T in) {LockQueue();// 只有在运行状态我们才往任务队列中放入任务if (_isrunning) {_task_queue.push(in);if (_sleep_thread_num 0)WakeUpThread();}UnLockQueue();}~ThreadPool() {for (auto t : _threads)t.Join();pthread_mutex_destroy(_mutex);pthread_cond_destroy(_cond);}private:int _thread_num;int _sleep_thread_num;std::vectorThread _threads;std::queueT _task_queue;bool _isrunning;pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPoolT* _tp;static pthread_mutex_t _sig_mutex; };// 单例懒汉模式 template typename T ThreadPoolT* ThreadPoolT::_tp nullptr;template typename T pthread_mutex_t ThreadPoolT::_sig_mutex PTHREAD_MUTEX_INITIALIZER; Thread.hpp #pragma once #include iostream #include functional #include string #include unistd.h #include pthread.h #include cstring #include cerrno #include Log.hpp// using func_t std::functionvoid(const std::string name, pthread_mutex_t* lock); using func_t std::functionvoid(const std::string name); using namespace log_ns; // typedef void*(func_t)(void);const pthread_t ctid -1;class Thread { private:void excute() {// std::cout _name begin to run std::endl;// LOG(INFO, %s begin to run\n, _name.c_str());_isrunning true;_func(_name);_isrunning false;}static void* ThreadRoutine(void* args) {Thread* self static_castThread(args);self-excute();return nullptr;} public:Thread(func_t func, const std::string name) : _func(func),_isrunning(false),_tid(ctid),_name(name){}~Thread() {}void Start() {// 创建之后就开始运行了int n pthread_create(_tid, nullptr, ThreadRoutine, (void)this);if (n ! 0) {std::cout thread create failed!!! std::endl;exit(1);}}void Stop() {// 将线程暂停使用if (_isrunning false) return;// std::cout _name stop std::endl;int n ::pthread_cancel(_tid);if (n ! 0) {std::cout thread stop failed std::endl;}_isrunning false;}void Join() {// 线程等待if (_isrunning) return;int n pthread_join(_tid, nullptr);if (n ! 0) {std::cout thread wait failed!!! strerror(errno) std::endl;}// std::cout _name join std::endl;}std::string Status() {if (_isrunning) return running;else return sleep;} private:pthread_t _tid;func_t _func;bool _isrunning;std::string _name; }; Route.hpp #pragma once #include iostream #include string #include vector #include functional #include pthread.h #include unistd.h #include sys/types.h #include sys/socket.h #include InetAddr.hpp #include Log.hpp #include LockGuard.hpp #include ThreadPool.hpp using namespace log_ns;using route_task_t std::functionvoid();class Route { private:void CheckUserinList(const InetAdrr who) {LockGuard lockguard(_mutex);for (auto user : _user_online) {if (user who) return;}LOG(DEBUG, %s is not exist, add it now\n, who.AddrString().c_str());_user_online.push_back(who);}void Lineoff(InetAdrr who) {LockGuard lockguard(_mutex);auto it _user_online.begin();while (it ! _user_online.end()) {if (it who) {_user_online.erase(it);LOG(DEBUG, %s line off\n, who.AddrString().c_str());return;}}}void ForwordHelper(int sockfd, const std::string message, InetAdrr who) {std::string send_message [ who.AddrString() ] ;send_message message;// 现在将messeage转发出去for (auto user : _user_online) {struct sockaddr_in peer user.Addr();socklen_t len sizeof(peer);LOG(INFO, %s forword to %s\n, send_message.c_str(), user.AddrString().c_str());sendto(sockfd, send_message.c_str(), send_message.size(), 0, (struct sockaddr)peer, len);}}public:Route() {pthread_mutex_init(_mutex, nullptr);}void ForwordMessage(int sockfd, const std::string message, InetAdrr who) {// 先检查当前用户列表中是否存在whoCheckUserinList(who);if (message Q || message QUIT) {Lineoff(who);}// 开始转发信息// ForwordHelper(sockfd, message, who);// 现在开始绑定我们的函数route_task_t t std::bind(Route::ForwordHelper, this, sockfd, message, who);ThreadPoolroute_task_t::GetInstance()-Push(t);}~Route() {pthread_mutex_destroy(_mutex);} private:// 用户列表std::vectorInetAdrr _user_online;pthread_mutex_t _mutex; }; LockGuard.hpp #pragma once #include iostream #include pthread.hclass LockGuard { public:LockGuard(pthread_mutex_t* mtx): _mtx(mtx){pthread_mutex_lock(_mtx);}~LockGuard() {pthread_mutex_unlock(_mtx);} private:pthread_mutex_t* _mtx; }; 测试结果 TCP Socket编程 Tcp传输层控制协议 协议是传输层协议中很常用很重要的一个协议主要特点如下 1. 有连接 2. 可靠的传输数据 3. 面向字节流 在传输层中关于 Tcp 协议的选择和 Udp 协议的选择看主要的应用场景tcp 可靠但是效率相对较低udp 不可靠的但是效率相对较高 1. echo server 这部分的代码功能和 udp 的 echo 的代码功能十分相似因为该程序主要是为了试探我们写的代码对不对测试客户端和服务端是否连接上如下 TcpServer.hpp #pragma once #include iostream #include string #include functional #include cstring #include unistd.h #include sys/wait.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include pthread.h #include Log.hpp #include InetAddr.hpp #include ThreadPool.hppusing namespace log_ns;enum {SOCKET_ERROR 1,BIND_ERROR,LISTEN_ERROR };const int glistensockfd -1; const int gblcklog 8;using tcp_task_t std::functionvoid();class TcpServer { private:void Service(int sockfd, InetAdrr who) {while (true) {// 开始读和写char buff[1024];int n read(sockfd, buff, sizeof(buff) - 1);if (n 0) {buff[n] 0;LOG(DEBUG, %s send a message: %s\n, who.AddrString().c_str(), buff);std::string echo [ who.AddrString() ] ;echo buff;write(sockfd, echo.c_str(), echo.size());} else if (n 0) {LOG(INFO, client %s quit\n, who.AddrString().c_str());break;} else {LOG(FATAL, read error\n);break;}}close(sockfd);}public:TcpServer(uint16_t port): _port(port),_listensocked(glistensockfd),_isrunning(false) {}void Init() {// 先获取listensocked_listensocked socket(AF_INET, SOCK_STREAM, 0);if (_listensocked 0) {LOG(FATAL, create listensockfd fail\n);exit(SOCKET_ERROR);}LOG(INFO, get listensockfd success, listensockfd: %d\n, _listensocked);// 现在开始绑定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;int bind_n bind(_listensocked, (struct sockaddr)local, sizeof(local));if (bind_n 0) {LOG(FATAL, bind listensockfd fail\n);exit(BIND_ERROR);}LOG(INFO, bind success\n);int n listen(_listensocked, gblcklog);if (n 0) {LOG(FATAL, listen socket fail\n);exit(LISTEN_ERROR);}LOG(INFO, listen sucess\n);}// 创建一个内部类struct ThreadData {int _sockfd;InetAdrr _addr;TcpServer _tcp_point;ThreadData(const int sockfd, const InetAdrr addr, TcpServer* tcp): _sockfd(sockfd),_addr(addr),_tcp_point(tcp){}};static void* runServer(void* args) {// 将线程分离防止线程pthread_detach(pthread_self());ThreadData* td static_castThreadData(args);td-_tcp_point-Service(td-_sockfd, td-_addr);delete td;return nullptr;}void Start() {_isrunning true;while (_isrunning) {// 现在开始收消息和发消息struct sockaddr_in peer;socklen_t len sizeof(peer);int sockfd accept(_listensocked, (struct sockaddr)peer, len);InetAdrr addr(peer);if (sockfd 0) {LOG(ERROR, %s get sockfd fail, the reason is %s\n, addr.AddrString().c_str(), strerror(errno));continue;}LOG(INFO, get sockfd success, sockfd: %d\n, sockfd);// Service(sockfd, addr);// 串行运行长服务并不能满足多个用户访问服务器需要使用并行运行才能满足// 1. 多进程 2、多线程 3、线程池// // 1. 多进程版本// pid_t id fork();// if (id 0) {// // 让子进程关闭listensocked文件描述符// close(_listensocked);// // 创建孙子进程直接让子进程退出孙子进程会被bash接管// // 这样长服务就会被孙子进程运行孙子进程退出直接退出// // 子进程也不会阻塞可以让父进程继续等下去// // 当然最好的方法是使用信号 signal(SIGCHLD, SIG_IGN) 操作// if (fork() 0) exit(0);// Service(sockfd, addr);// exit(0);// }// // 让父进程关闭sockfd文件描述符防止文件描述符太多导致文件描述符泄露// close(sockfd);// pid_t n waitpid(id, nullptr, 0);// if (n 0) {// LOG(INFO, wait child process success\n);// }// // 2. 多线程// pthread_t tid;// ThreadData* data new ThreadData(sockfd, addr, this);// pthread_create(tid, nullptr, runServer, (void)data);// 3. 线程池tcp_task_t task std::bind(TcpServer::Service, this, sockfd, addr);ThreadPooltcp_task_t::GetInstance()-Push(task);}_isrunning false;}~TcpServer() {if (_listensocked 0) close(_listensocked);} private:uint16_t _port;int _listensocked;bool _isrunning; }; TcpServer.cc #include TcpServer.hppint main() {TcpServer tcvr new TcpServer(8888);tcvr-Init();tcvr-Start();return 0; } TcpClient.cc #include iostream #include string #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include Log.hppusing namespace log_ns;int GetSockfd() {int local_socket socket(AF_INET, SOCK_STREAM, 0);if (local_socket 0) {LOG(FATAL, Create socket fail\n);exit(0);}LOG(INFO, Create socket success\n);return local_socket; }int main(int argc, char* args[]) {if (argc ! 3) {LOG(ERROR, please input the - ./client、ip and port\n);return 0;}// 获取 ip 和 port 以及 socketuint16_t server_port std::stoi(args[2]);std::string server_ip args[1];// 绑定int local_socket GetSockfd();struct sockaddr_in server;memset(server, 0, sizeof(server));socklen_t len sizeof(server);server.sin_family AF_INET;server.sin_port htons(server_port);// server.sin_addr.s_addr inet_addr(server_ip.c_str());inet_pton(AF_INET, server_ip.c_str(), server.sin_addr);int n connect(local_socket, (struct sockaddr)server, sizeof(server));if (n 0) {LOG(FATAL, get sockfd fail\n);exit(1);}while (true) {std::string info;std::cout Please enter ;std::getline(std::cin, info);// 现在将数据写入int n write(local_socket, info.c_str(), info.size());if (n 0) {char buff[1024];int readlen read(local_socket, buff, sizeof(buff) - 1);if (readlen 0) {buff[readlen] 0;std::cout buff std::endl;}} else {LOG(INFO, client quit\n);break;}}close(local_socket);return 0; } 其余的代码文件和 udp 的一样测试如下 2. command server 该代码是在 echo 代码基础上改编的代码主要实现的功能为将我们输入的命令执行相当于一个小型的 shell 程序如下 TcpServer.hpp #pragma once #include iostream #include string #include functional #include cstring #include unistd.h #include sys/wait.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include pthread.h #include Log.hpp #include InetAddr.hppusing namespace log_ns;enum {SOCKET_ERROR 1,BIND_ERROR,LISTEN_ERROR };const int glistensockfd -1; const int gblcklog 8;using tcp_task_t std::functionvoid(int, InetAdrr);class TcpServer { private:void Service(int sockfd, InetAdrr who) {while (true) {// 开始读和写char buff[1024];int n read(sockfd, buff, sizeof(buff) - 1);if (n 0) {buff[n] 0;LOG(DEBUG, %s send a message: %s\n, who.AddrString().c_str(), buff);std::string echo [ who.AddrString() ] ;echo buff;write(sockfd, echo.c_str(), echo.size());} else if (n 0) {LOG(INFO, client %s quit\n, who.AddrString().c_str());break;} else {LOG(FATAL, read error\n);break;}}close(sockfd);}public:TcpServer(tcp_task_t task, uint16_t port): _task(task),_port(port),_listensocked(glistensockfd),_isrunning(false) {}void Init() {// 先获取listensocked_listensocked socket(AF_INET, SOCK_STREAM, 0);if (_listensocked 0) {LOG(FATAL, create listensockfd fail\n);exit(SOCKET_ERROR);}LOG(INFO, get listensockfd success, listensockfd: %d\n, _listensocked);// 现在开始绑定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;int bind_n bind(_listensocked, (struct sockaddr)local, sizeof(local));if (bind_n 0) {LOG(FATAL, bind listensockfd fail\n);exit(BIND_ERROR);}LOG(INFO, bind success\n);int n listen(_listensocked, gblcklog);if (n 0) {LOG(FATAL, listen socket fail\n);exit(LISTEN_ERROR);}LOG(INFO, listen sucess\n);}// 创建一个内部类struct ThreadData {int _sockfd;InetAdrr _addr;TcpServer* _tcp_point;ThreadData(const int sockfd, const InetAdrr addr, TcpServer* tcp): _sockfd(sockfd),_addr(addr),_tcp_point(tcp){}};static void* runServer(void* args) {// 将线程分离就不用阻塞的join线程pthread_detach(pthread_self());ThreadData* td static_castThreadData(args);// LOG(INFO, the sockfd: %d\n, td-_sockfd);td-_tcp_point-_task(td-_sockfd, td-_addr);close(td-_sockfd);delete td;return nullptr;}void Start() {_isrunning true;while (_isrunning) {// 现在开始收消息和发消息struct sockaddr_in peer;socklen_t len sizeof(peer);int sockfd accept(_listensocked, (struct sockaddr)peer, len);InetAdrr addr(peer);if (sockfd 0) {LOG(ERROR, %s get sockfd fail, the reason is %s\n, addr.AddrString().c_str(), strerror(errno));continue;}LOG(INFO, get sockfd success, sockfd: %d\n, sockfd);// 2. 多线程pthread_t tid;ThreadData* data new ThreadData(sockfd, addr, this);pthread_create(tid, nullptr, runServer, (void)data);}_isrunning false;}~TcpServer() {if (_listensocked 0) close(_listensocked);} private:uint16_t _port;int _listensocked;bool _isrunning;tcp_task_t _task; }; TcpServer.cc #include TcpServer.hpp #include Command.hppint main() {Command cmd;tcp_task_t task std::bind(Command::CommandHandler, cmd, std::placeholders::_1, std::placeholders::_2);TcpServer tcvr new TcpServer(task, 8888);tcvr-Init();tcvr-Start();return 0; } TcpClient.cc #include iostream #include string #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include Log.hppusing namespace log_ns;int GetSockfd() {int local_socket socket(AF_INET, SOCK_STREAM, 0);if (local_socket 0) {LOG(FATAL, Create socket fail\n);exit(0);}LOG(INFO, Create socket success\n);return local_socket; }int main(int argc, char* args[]) {if (argc ! 3) {LOG(ERROR, please input the - ./client、ip and port\n);return 0;}// 获取 ip 和 port 以及 socketuint16_t server_port std::stoi(args[2]);std::string server_ip args[1];// 绑定int local_socket GetSockfd();struct sockaddr_in server;memset(server, 0, sizeof(server));socklen_t len sizeof(server);server.sin_family AF_INET;server.sin_port htons(server_port);// server.sin_addr.s_addr inet_addr(server_ip.c_str());inet_pton(AF_INET, server_ip.c_str(), server.sin_addr);int n connect(local_socket, (struct sockaddr)server, sizeof(server));if (n 0) {LOG(FATAL, get sockfd fail\n);exit(1);}while (true) {std::string info;std::cout Please enter ;std::getline(std::cin, info);// 现在将数据写入int n write(local_socket, info.c_str(), info.size());if (n 0) {char buff[1024];int readlen read(local_socket, buff, sizeof(buff) - 1);if (readlen 0) {buff[readlen] 0;std::cout buff std::endl;}} else {LOG(INFO, client quit\n);break;}}close(local_socket);return 0; } Command.hpp #pragma once #include iostream #include string #include cstdio #include cstring #include sys/types.h #include sys/socket.h #include InetAddr.hpp #include Log.hppusing namespace log_ns;class Command {std::string Excute(const std::string cmd) {FILE fp popen(cmd.c_str(), r);if (fp) {std::string result;char info[1024];while (fgets(info, sizeof(info), fp)) {result info;}pclose(fp);return result.empty() ? success : result;} else {LOG(WARNING, open the fp fail\n);return None;}}public:Command() {}void CommandHandler(int sockfd, InetAdrr who) {// 接收消息然后返回消息 while (true) {// 开始读和写char buff[1024];// LOG(INFO, the sockfd: %d\n, sockfd);int n recv(sockfd, buff, sizeof(buff) - 1, 0);if (n 0) {buff[n] 0;LOG(DEBUG, %s send a message: %s\n, who.AddrString().c_str(), buff);std::string result Excute(buff);send(sockfd, result.c_str(), result.size(), 0);} else if (n 0) {LOG(INFO, client %s quit\n, who.AddrString().c_str());break;} else {LOG(FATAL, read error, %s\n, strerror(errno));break;}}}~Command() {} }; 测试如下
- 上一篇: 广州建设网站制作做行程好的网站
- 下一篇: 广州建网站公司排名一个网站用多个域名
相关文章
-
广州建设网站制作做行程好的网站
广州建设网站制作做行程好的网站
- 技术栈
- 2026年04月20日
-
广州建设外贸网站怎么制作动态的网站
广州建设外贸网站怎么制作动态的网站
- 技术栈
- 2026年04月20日
-
广州建设培训网站山西省建五公司官网
广州建设培训网站山西省建五公司官网
- 技术栈
- 2026年04月20日
-
广州建网站公司排名一个网站用多个域名
广州建网站公司排名一个网站用多个域名
- 技术栈
- 2026年04月20日
-
广州建网站哪家最好程序员一个月能挣多少钱
广州建网站哪家最好程序员一个月能挣多少钱
- 技术栈
- 2026年04月20日
-
广州建站免费模板网站建设 橙
广州建站免费模板网站建设 橙
- 技术栈
- 2026年04月20日
