当前位置: 首页 > news >正文

微信网站推广python做网站性能太差

微信网站推广,python做网站性能太差,公司做的网站列入什么科目,深圳最便宜的物流公司上一章中#xff0c;我们介绍了socket#xff0c;以及TCP/UDP协议。这一章带大家实现几个UDP协议的网络服务。我们需要一个 服务端和一个客户端。 1.服务端实现 1.1socket函数 #include sys/types.h #include sys/socket.hint socket(int domain, in…上一章中我们介绍了socket以及TCP/UDP协议。这一章带大家实现几个UDP协议的网络服务。我们需要一个 服务端和一个客户端。 1.服务端实现 1.1socket函数 #include sys/types.h #include sys/socket.hint socket(int domain, int type, int protocol);参数说明 domain域标识了这个套接字的通信类型网络通信/本地通信这个就是sockaddr结构体的前16个bit位。如果是本地通信就是AF_UNIX,如果是网络通信就是AF_INET。 type套接字提供的服务类型常见的就是SOCK_STREAM和SOCK_DGRAM如果我们是基于TCP协议的通信就使用SOCK_STREAM表示的是流式服务。如果我们是基于UDP协议通信的就使用SOCK_DGRAM表示的是数据包服务。protocol创建套接字的类型TCP/UDP但是这个参数可以由前两个参数决定。所以通常设置为0 返回值 成功会返回一个文件描述符失败返回-1错误码被设置 在系统的文件操作中也会返回文件描述符与socket返回的文件描述符不同的是普通文件的文件缓冲区对应的是磁盘而socket返回的文件描述符的文件缓冲区对应的是网卡。用户将数据写到缓冲区由操作系统自动将缓冲区中的数据刷新到网卡中网卡会负责将这个数据发送到对端主机上。 class UdpServer { public:UdpServer(){// 创建套接字_socket socket(AF_INET, SOCK_DGRAM, 0);if (_socket 0){std::cerr create socket error std::endl;exit(1);}std::cout create socket success, socket: _socket std::endl;}~UdpServer(){close(_socket);}private:int _socket; };调用socket函数就能创建一个套接字了第一个参数我们填AF_INET表示的是我们是网络通信。第二个参数我们填SOCK_DGRAM表示我们是UDP服务数据报。由于一个进程启动时默认会打开标准输入标准输出标准错误这三个文件描述符而文件描述符的创建规则就是从0开始向上找到第一个没有使用的所以我们可以猜测以下_sock的值为3. 1.2bind函数 我们创建完套接字以后也只是打开了一个文件。还没有将这个文件和网络关联起来所以我们需要使用bind函数将IPport和文件绑定 #include sys/types.h #include sys/socket.hint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 参数介绍 sockfdsocket函数的返回值。addr通用结构体包括协议家族IP地址端口号portaddrlenaddr的长度 返回值介绍 成功返回0错误返回-1 sockaddr_in结构体 struct sockaddr_in{short int sin_family;in_port_t sin_port; struct in_addr sin_addr; unsigned char sin_zero[8];}; sin_family协议家族表示通信类型AF_INET网络通信PF_INET本地通信sin_port端口号网络字节序sin_addrIP地址。sin_zero填充字段让sizeof(sockaddr_in) 16 既然第二步需要添加IP和端口号所以我们要对代码修改一下给UDP的构造函数添加port和strIP的点分十进制表示形式 class UdpServer { public:UdpServer(const uint16_t port, const std::string str ){// 创建套接字_socket socket(AF_INET, SOCK_DGRAM, 0);if (_socket 0){std::cerr create socket error std::endl;exit(1);}std::cout create socket success, socket: _socket std::endl;sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr inet_addr(str.c_str());int n bind(_socket, (sockaddr *)addr, sizeof(addr));if (n 0){std::cout bind error std::endl;exit(2);}std::cout bind success std::endl;}~UdpServer(){close(_socket);}private:int _socket; };需要注意的是在填写sockaddr_in的参数的时候 sin_family需要和socket套接字创建时的domain相同。由于端口号和IP将来是要发送到网络的而网络数据流统一采用大端的形式所以为了代码的可移植性我们不管自己是大端还是小端统一调用函数hton转化成大端。对于IP来说如果直接发送的是点分十进制形式如192.168.12.80的形式则需要占用过多的字节数对网络传输无疑是一种很大的消耗所以我们采用一个32位的整数来表示IP在网络传输中要将点分十进制的IP转化成整数再进行传输。 #include sys/socket.h #include netinet/in.h #include arpa/inet.hint inet_aton(const char *cp, struct in_addr *inp);in_addr_t inet_addr(const char *cp); //点分十进制转整数in_addr_t inet_network(const char *cp);char *inet_ntoa(struct in_addr in); //整数转点分十进制struct in_addr inet_makeaddr(int net, int host);in_addr_t inet_lnaof(struct in_addr in);in_addr_t inet_netof(struct in_addr in);这里重点学习两个inet_addr和inet_ntoa。 1.3运行服务器 首先我们要知道服务器就是一个死循环在启动之后永远不会退出。 #include iostream #include memory #include UdpServer.hppvoid Usage() {std::cout Please enter: ./UdpServer [ip] port std::endl; }int main(int argc, char* argv[]) {if (argc ! 3){Usage();return 3;}std::string ip argv[1];uint16_t port atoi(argv[argc - 1]);std::unique_ptrUdpServer p(new UdpServer(port, ip));p-Start();return 0; } 我们可以使用命令行参数的形式在启动这个程序时就把IP和端口号传递过去。 1.4IP的绑定 ifconfig操作系统中用于配置和显示网络接口参数的命令行工具。它主要用于查看和设置网络接口如以太网和 Wi-Fi的详细信息包括 IP 地址、子网掩码、广播地址、MAC 地址等。 这里的127.0.0.1表示的时本地环回用于在本地进行测试如果本地测试成功将来网络测试出现问题那么大概率就网络的问题。本地换回顾名思义就是只会在本地不会发送到网络当中。 当我们运行上面的程序的时候也可以时netstat函数查看网络情况, -a或--all 显示所有连线中的Socket。-l或--listening 显示监控中的服务器的Socket。-n或--直接使用IP地址数字而不是域名。-p或--显示正在使用Socket的程序识别码和程序名称。-t或--tcp 显示TCP传输协议的连线状况。-u或--udp 显示UDP传输协议的连线状况。 我们可以看到确实能看到我们刚刚运行的程序成功bind了一个IP和端口号。 但是在实际情况下服务器时不建议绑定一个固定IP的。 安全性攻击者可能会针对这个固定的IP地址进行攻击比如DDoS攻击或者其他类型的网络攻击。相比之下使用动态IP或者负载均衡等技术可以更好地分散攻击提高系统的安全性。可用性在某些情况下固定IP可能并不总是可用的。例如在云服务环境中IP地址可能会因为服务器的迁移或重新部署而发生变化。如果服务端绑定到这样的固定IP当IP地址发生变化时服务端可能无法正常工作导致服务中断。一个服务器可能会有多个IP多张网卡当客户端发送数据时每张网卡都会收到该数据如果我们想访问指定的某一个端口8080并且如果指定了IP那么只能由那一个指定的IP接受数据但是如果我们绑定的是任意IP那么只要是发送给8080端口的任意一个网卡接受到了都会向上交付给服务器。 addr.sin_addr.s_addr str.empty() ? INADDR_ANY : inet_addr(str.c_str()); 所以我们修改一下如果创建服务端的时候传了IP那就用传的IP如果没用那就用我们INADDR_ANY其实就是(0.0.0.0)绑定之后只要是发送到这台主机上端口号为XXX我这里是8080的就将数据全部交给这个进程。 1.5读取数据recvfrom #include sys/types.h #include sys/socket.hssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);参数介绍 sockfd从哪个套接字中读取buf读到的数据存放的缓冲区len读len个字节flags读取方式0表示阻塞读取src_addr发送端的信息addrlen输出型参数表示src_addr的长度必须初始化为sizeof(src_addr)) void Start(){while (true){char temp[1024];sockaddr_in addr;socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){printf([%s:%d]# %s, ip.c_str(), port, temp);}}} src_addr用来保存发送端的信息我们在前面使用bind函数的时候也介绍过sockaddr结构里面包含了发送端的IP和端口port但是因为是网络序列我们需要将他转化成主机序列所以使用ntoh函数ip是32位整数我们想转化成点分十进制形式方便观看于是可以使用inet_ntoa函数。 1.6服务端整体代码 #pragma once#include iostream #include string #include unordered_set #include cstring #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.hclass UdpServer { public:UdpServer(const uint16_t port, const std::string str ){// 创建套接字_socket socket(AF_INET, SOCK_DGRAM, 0);if (_socket 0){std::cerr create socket error std::endl;exit(1);}std::cout create socket success, socket: _socket std::endl;sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr str.empty() ? INADDR_ANY : inet_addr(str.c_str());int n bind(_socket, (sockaddr *)addr, sizeof(addr));if (n 0){std::cout bind error std::endl;exit(2);}std::cout bind success std::endl;}void Start(){while (true){char temp[1024];sockaddr_in addr;socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){printf([%s:%d]# %s, ip.c_str(), port, temp);}}}~UdpServer(){close(_socket);}private:int _socket; };2.客户端实现 服务端的创建和客户端类似。我们也需要创建套接字 2.1创建套接字 void Usage() {std::cout Please enter: ./Client [ip] port std::endl; }int main(int argc, char *argv[]) {if (argc ! 3){Usage();return 3;}std::string ip argv[1];uint16_t port atoi(argv[argc - 1]);// 创建套接字int clientsocket socket(AF_INET, SOCK_DGRAM, 0);if (clientsocket 0){std::cerr create socket error std::endl;exit(1);}std::cout create socket success, socket: clientsocket std::endl;return 0; } 2.1绑定问题 客户端必须绑定IP和端口但是不用显示的bind。 这句话是什么意思呢就是我们需要将网卡文件和IP端口进行绑定但是不需要我们自己手动绑定操作系统会自动帮我们绑定一个端口号。 为什么不能显示绑定呢首先我们要知道一个端口只能对应一个进程现在有两家公司各推出了一款客户端如果他们想让端口不冲突那就不能让自己家的客户端绑定的端口和对方的一样如果一样就会起冲突但是互联网上有很多家公司难道都要协商一下哪个端口谁来用吗。所以我们采用了让操作系统帮我们自动分配一个没有使用的端口号。 当我们调用了类似与sendto发送信息的函数的时候操作系统会帮我们自动分配一个端口号也就是说客户端每次启动时的端口号可能都不相同。 服务端的端口号为什么是固定的 服务端的端口号是众所周知的如果每次都不一样的话客户端就找不到服务器了 2.3发送信息sendto #include sys/types.h #include sys/socket.hssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);参数介绍 sockfd从哪个套接字中读取buf将缓冲区中的数据发给对端len要发送多少个字节flags写入方式0表示阻塞写入src_addr对端主机的信息包括协议家族IPportaddrlen表示src_addr的长度 参数和recvfrom类似 // 直接给服务器send系统会自动帮我们bindsockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr inet_addr(ip.c_str());while (true){std::string buffer;std::cout Please enter# ;std::getline(std::cin, buffer);sendto(clientsocket, buffer.c_str(), buffer.size(), 0, (sockaddr *)addr, sizeof(addr));}当我们将客户端发送消息的逻辑 完成之后就可以正常开始通信了。 我们先启动服务端再启动客户端两个不同的进程 命令行中提示我们输入 当我们在右边客户端输入的消息之后就能成功发送给服务端了。当我们再次使用netstat查看网络情况 当我们再次使用netstat查看网络情况就可以看到客户端和服务端正在运行并且能看到各自的端口号。 这样我们就实现了一个简单的服务端客户端模型下面是源码 2.4源码 UdpServer.hpp //Server.hpp#pragma once#include iostream #include string #include unordered_set #include cstring #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.hclass UdpServer { public:UdpServer(const uint16_t port, const std::string str ){// 创建套接字_socket socket(AF_INET, SOCK_DGRAM, 0);if (_socket 0){std::cerr create socket error std::endl;exit(1);}std::cout create socket success, socket: _socket std::endl;sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr str.empty() ? INADDR_ANY : inet_addr(str.c_str());int n bind(_socket, (sockaddr *)addr, sizeof(addr));if (n 0){std::cout bind error std::endl;exit(2);}std::cout bind success std::endl;}void Start(){while (true){char temp[1024];sockaddr_in addr;socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){temp[n] 0;printf([%s:%d]# %s\n, ip.c_str(), port, temp);}}}~UdpServer(){close(_socket);}private:int _socket; }; Server.cc #include iostream #include memory #include UdpServer.hppvoid Usage() {std::cout Please enter: ./UdpServer [ip] port std::endl; }int main(int argc, char* argv[]) {if (argc ! 3 argc ! 2){Usage();return 3;}std::string ip argv[1];uint16_t port atoi(argv[argc - 1]);if (argc 2)ip ;std::unique_ptrUdpServer p(new UdpServer(port, ip));p-Start();return 0; } Client.cc #include iostream #include memory #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.hvoid Usage() {std::cout Please enter: ./Client [ip] port std::endl; }int main(int argc, char *argv[]) {if (argc ! 3){Usage();return 3;}std::string ip argv[1];uint16_t port atoi(argv[argc - 1]);// 创建套接字int clientsocket socket(AF_INET, SOCK_DGRAM, 0);if (clientsocket 0){std::cerr create socket error std::endl;exit(1);}std::cout create socket success, socket: clientsocket std::endl;// 直接给服务器send系统会自动帮我们bindsockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr inet_addr(ip.c_str());while (true){std::string buffer;std::cout Please enter# ;std::getline(std::cin, buffer);sendto(clientsocket, buffer.c_str(), buffer.size(), 0, (sockaddr *)addr, sizeof(addr));}return 0; } 3.更多功能 在前面我们学习了最基础的服务端客户端编写让客户端给服务端发消息服务端直接显示我们可以让服务端在拿到数据后对数据进行特殊处理。 void ExecuteCommand(){while (true){char temp[1024];sockaddr_in addr;memset(addr, 0, sizeof(addr));socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){// 接收到数据temp[n] \0;std::string buffer temp;// 对数据做处理// 将数据写回sendto(_socket, buffer.c_str(), buffer.size(), 0, (sockaddr *)addr, sizeof(addr));}}} 我们可以设计出这样一个函数就是前面的Start但是在拿到数据后先将数据进行处理再将数据发送给客户端。我们下面要完成的就是这个对数据处理的函数。 3.1回显echo 很简单我们收到用户的数据之后并不需要处理直接返回即可 void ExecuteCommand(){while (true){char temp[1024];sockaddr_in addr;memset(addr, 0, sizeof(addr));socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){// 接收到数据temp[n] \0;std::string buffer temp;// 对数据做处理EchoMessage(buffer);// 将数据写回sendto(_socket, buffer.c_str(), buffer.size(), 0, (sockaddr *)addr, sizeof(addr));}}}void EchoMessage(std::string buffer){return;} 注意这里的EchoMessage函数虽然什么都没有做但是为了突出用户发的信息是被处理过的我们还是添加了一个函数。 也是可以成功运行。 3.2大写转换 将客户端发送的小写字母转化成大写字母 void ExecuteCommand(){while (true){char temp[1024];sockaddr_in addr;memset(addr, 0, sizeof(addr));socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){// 接收到数据temp[n] \0;std::string buffer temp;// 对数据做处理//EchoMessage(buffer);Transformed(buffer);// 将数据写回sendto(_socket, buffer.c_str(), buffer.size(), 0, (sockaddr *)addr, sizeof(addr));}}}void Transformed(std::string buffer){for (auto ch : buffer){if (a ch ch z){ch - 32;}}return;} 我们只需要将调用的函数替换一下就能完成不同的业务。 3.3英译汉词典 将用户发送的英文转化成汉语 我们先创建一个哈希表保存英语单词以及对应的意思 static std::unordered_mapstd::string, std::string Dict;void TranslationInit() {Dict.insert({apple, 苹果});Dict.insert({pear, 梨子});Dict.insert({banana, 香蕉});Dict.insert({orange, 橘子});Dict.insert({left, 左});Dict.insert({right, 右});Dict.insert({sun, 太阳});Dict.insert({moon, 月亮}); } void ExecuteCommand(){while (true){char temp[1024];sockaddr_in addr;memset(addr, 0, sizeof(addr));socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);std::string ip inet_ntoa(addr.sin_addr);uint16_t port ntohs(addr.sin_port);if (n 0){// 接收到数据temp[n] \0;std::string buffer temp;// 对数据做处理//EchoMessage(buffer);//Transformed(buffer);Translation(buffer);// 将数据写回sendto(_socket, buffer.c_str(), buffer.size(), 0, (sockaddr *)addr, sizeof(addr));}}}void Translation(std::string buffer){auto it Dict.find(buffer);if (it Dict.end()){buffer not find;}else {buffer it-second;}}3.4执行命令 执行用户发送的shell命令例如pwdls等。 我们介绍一个函数 #include stdio.hFILE *popen(const char *command, const char *type);int pclose(FILE *stream);popen函数会fork创建子进程并且让子进程程序替换执行command命令最终把结果写到一个文件当中。type就是以什么方式打开这个文件w/r/a。 std::unordered_setstd::string forbid {rm,mv,kill,cp };void ExecuteCommand(std::string buffer) {// 查询有无禁止命令if (!sercharforbid(buffer)){buffer you cant do that\n;return;}// 使用popen执行用户命令并将结果写入fp中if (buffer ll){buffer ls -l --colorauto;}FILE *fp popen(buffer.c_str(), r);if (fp nullptr){buffer command is unknow;return;}// 读取信息buffer.clear();char temp[1024];while (fgets(temp, sizeof(temp), fp) ! NULL){buffer temp;}pclose(fp); }bool sercharforbid(const std::string buffer) {for (auto com : forbid){int pos buffer.find(com);if (pos ! std::string::npos){return false;}}return true; } 我们将一些命令保存在哈希桶中并且不让用户执行这些命令例如rm删除之类。 3.5网络聊天室 将来会有很多用户加入这个聊天室一个用户发送消息能让其他用户都看到这条消息。 我们先创建一个类这个类对sockaddr_in进行封装。 #include iostream #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.hclass InetAddr { public:InetAddr(const sockaddr_in addr): _addr(addr){_ip inet_ntoa(addr.sin_addr);_port ntohs(addr.sin_port);}std::string GetUser(){std::string temp;temp _ip;temp : ;temp std::to_string(_port);return temp;}std::string GetIp(){return _ip;}uint16_t GetPort(){return _port;}sockaddr_in GetAddr(){return _addr;}private:std::string _ip;uint16_t _port;sockaddr_in _addr; }; 这个类会提取出sockaddr_in中的IP和端口并保存GetUser函数会返回一个IP端口号的字符串。 我们再创建一个保存用户信息的哈希桶 std::unordered_mapstd::string, InetAddr _users;void AddUser(InetAddr user) {std::string userMessage user.GetUser();if (_users.find(userMessage) ! _users.end()){return;}_users.insert({userMessage, user}); } 每当有用户发消息时我们根据用户的sockaddr_in就能提取出这个用户的IP端口如果这个用户是第一次发消息我们就把他的信息保存起来。 void Route(size_t sock, std::string message) {//将message发送给每一个用户for (auto user : _users){sockaddr_in addr user.second.GetAddr();sendto(sock, message.c_str(), message.size(), 0, (sockaddr*)addr, sizeof(addr));} } 最后再根据哈希桶中保存的用户信息就能再将数据发送给每一个用户了。 我们就完成了一个简单的网络聊天室但是我们通过实验会发现还存在很大的问题由于我们的服务端是单线程阻塞式读取所以当别人发送数据的时候我们可能正在阻塞并不会显示数据只有在写之后数据才会重新打印出来。 上面我们的测试也能反应这一点我们的执行顺序是从上至下从左至右每次发一条消息一共两次。可以看到第二次左上那个进程才收到了右上第一次发的消息这很显然不复合实际中的网络通信。 所以我们需要将服务端改成线程池版本服务端改成两个线程一个读一个写 #pragma once#include iostream #include unordered_set #include unordered_map #include functional #include unistd.h #include cstring #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h#include LogMessage.hpp #include ExistReason.hpp #include ThreadPool.hpp #include LocalGuard.hpp #include Pthread.hpp #include InetAddr.hppusing task_t std::functionvoid();class ChatServer { public:ChatServer(const uint16_t port, const std::string str ){// 创建套接字_socket socket(AF_INET, SOCK_DGRAM, 0);if (_socket 0){Log::LogMessage(Error, create socket error);exit(CREATE_SOCKET_ERROR);}Log::LogMessage(Debug, create socket success, socket: %d, _socket);// 进行bindsockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr str.empty() ? INADDR_ANY : inet_addr(str.c_str());int n bind(_socket, (sockaddr *)addr, sizeof(addr));if (n 0){Log::LogMessage(Error, bind error);exit(BIND_ERROR);}Log::LogMessage(Debug, bind success);pthread_mutex_init(_user_mutex, nullptr);ThreadPooltask_t::GetInstance()-Start();}~ChatServer(){pthread_mutex_destroy(_user_mutex);close(_socket);}void Start(){while (true){char temp[1024];sockaddr_in addr;memset(addr, 0, sizeof(addr));socklen_t addrlen sizeof(addr);int n recvfrom(_socket, temp, sizeof(temp) - 1, 0, (sockaddr *)addr, addrlen);temp[n] 0;InetAddr user(addr);if (n 0){// 将用户信息保存起来AddUser(user);std::string message [;message user.GetUser();message ] ;message temp;// 将任务push到队列当中task_t task std::bind(ChatServer::Route, this, _socket, message);ThreadPooltask_t::GetInstance()-Push(task);}else if (n 0){// 对端关闭连接Log::LogMessage(Debug, close connection);}else{Log::LogMessage(Warning, server recvfrom warning);}}}private:void AddUser(InetAddr user){LockGuard lock(_user_mutex);std::string userMessage user.GetUser();if (_users.find(userMessage) ! _users.end()){return;}_users.insert({userMessage, user});}void Route(size_t sock, std::string message){//将message发送给每一个用户LockGuard lock(_user_mutex);for (auto user : _users){sockaddr_in addr user.second.GetAddr();sendto(sock, message.c_str(), message.size(), 0, (sockaddr*)addr, sizeof(addr));}}private:int _socket;std::unordered_mapstd::string, InetAddr _users;pthread_mutex_t _user_mutex; }; 我们使用线程池提前创建好几个线程将来只要有一个用户发消息了就指派一个线程去处理数据将这个信息发送给其他用户。 客户端 #include iostream #include memory #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h#include LogMessage.hpp #include ExistReason.hpp #include Pthread.hppvoid Usage() {std::cout Please enter: ./Client [ip] port std::endl; }struct ThreadDate {ThreadDate(int sock, sockaddr_in addr): _sock(sock), _addr(addr){}int _sock;sockaddr_in _addr; };void Sender(ThreadDate date) {while (true){std::string buffer;std::cout Please enter# ;std::getline(std::cin, buffer);sendto(date._sock, buffer.c_str(), buffer.size(), 0, (sockaddr *)date._addr, sizeof(date._addr));if (sendto 0){std::cerr send error std::endl;}} }void Recver(ThreadDate date) {char mes[1024];while (true){sockaddr_in add;socklen_t addlen sizeof(add);int n recvfrom(date._sock, mes, sizeof(mes) - 1, 0, (sockaddr *)add, addlen);if (n 0){mes[n] \0;std::cerr mes std::endl;}} }int main(int argc, char *argv[]) {if (argc ! 3){Usage();return USE_ERROR_MANUAL;}std::string ip argv[1];uint16_t port atoi(argv[argc - 1]);// 创建套接字int clientsocket socket(AF_INET, SOCK_DGRAM, 0);if (clientsocket 0){exit(1);}// 直接给服务器send系统会自动帮我们bindsockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(port);addr.sin_addr.s_addr inet_addr(ip.c_str());ThreadDate date(clientsocket, addr);Threadvoid, ThreadDate sender(Sender, date);Threadvoid, ThreadDate recver(Recver, date);sender.Create();recver.Create();sender.Jion();recver.Jion();return 0; } 主进程创建两个线程一个线程进行等待一个线程发送数据。 最终我们发现就解决了之前的问题但是这样又有了新的问题由于是多进程向屏幕打印时会有出错。我们可以使用管道注意一个细节客户端的收消息进程在收到消息时打印使用的是cerr我们只需要将标准错误2号文件描述符重定向到管道文件中就能成功将读写分离。
http://www.zqtcl.cn/news/120776/

相关文章:

  • 做网站必须知道的问题wordpress制作论坛
  • 怎样在建设部网站查资质证书网页设计有哪些岗位
  • 安徽中色十二冶金建设有限公司网站cad制图初学入门
  • 开发网站监控工具网上开店怎么找货源
  • 标准网站建设报价单私密浏览器直播
  • wordpress焦点图网站seo分析
  • 域名申请哪个网站好江西有色建设集团有限公司网站
  • 新乡市做网站的公司百度推广开户费用多少
  • 免费建网站哪个平台好php 未定义函数wordpress
  • 个人网站 域名选择郑州那家做网站便宜
  • 网站建设技术合伙人的技术股份全国免费发布信息网站大全
  • 兼职网站平台有哪些新手怎么学做网站
  • 有没有直接做网站的软件iis一个文件夹配置多个网站
  • 网站怎么屏蔽ip访问信息发布网站建设
  • 陕西省住房城乡建设厅网站管理中心电信服务器
  • 外国优秀网站设计程序员做任务的网站
  • 购物网站项目经验开发一个游戏软件需要多少钱
  • 专业的大连网站建设电商网站支付方案
  • 手机如何建设网站首页株洲搜索引擎优化
  • 辉县市建设局网站制作网站站用的软件下载
  • 什么网站广告做多有没有不花钱建设网站的方法
  • 网站开发技术总监面试题五大门户网站分别是
  • 福州自助建设网站网站开发工具蜡笔小新
  • 扬州市住房和城乡建设网站html5 后台网站模板
  • 网站建设与设计意义宜兴做宠物的网站
  • 苏州建设工程人才招聘网信息网站wordpress前端库加速
  • 浙江手机版建站系统信息应用商店下载app
  • 广告投放网站动画设计模板
  • 网站发外链的好处页面跳转 英文
  • 黑链 对网站的影响网页小游戏网站有哪些