合肥网站制作企业怎样用服务器做网站

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

合肥网站制作企业,怎样用服务器做网站,开发公司质量管理流程,miniui做的网站前面的文章中我们叙述了网络编程套接字的一些预备知识点#xff0c;从本文开始我们就将开始UDP套接字的编写。本文中的服务端与客户端都是在阿里云的云服务器进行编写与测试的。 udp_v1 在v1的版本中我们先来使用一下前面讲过得一些接口#xff0c;简单的构建一个udp服务器…前面的文章中我们叙述了网络编程套接字的一些预备知识点从本文开始我们就将开始UDP套接字的编写。本文中的服务端与客户端都是在阿里云的云服务器进行编写与测试的。 udp_v1 在v1的版本中我们先来使用一下前面讲过得一些接口简单的构建一个udp服务器 // udp_server.cc #include udp_server.hpp #include memoryusing namespace std; using namespace ns_server; int main() {unique_ptrUdpServer usvr(new UdpServer(1.1.1.1, 8082)); // 通过智能指针控制服务器的资源管理并且向程序传入ip与端口号usvr-InitServer(); //服务器的初始化usvr-Start();return 0; }// udp_server.hpp #pragma once#include iostream #include string #include cerrno #include cstring #include cstdlib #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h namespace ns_server {// 用于展示返回对应的错误提示enum{SOCKET_ERR 1,BIND_ERR};const static uint16_t default_port 8080;class UdpServer{public:UdpServer(std::string ip, uint16_t port defaultport) : ip(ip), port(port){std::cout server addr: ip : port std::endl;}void InitServer(){// 1. 创建socket接口打开网络文件sock_ socket(AF_INET, SOCKDGRAM, 0);if (sock 0){std::cerr create socket error: strerror(errno) std::endl;exit(SOCKETERR);}std::cout create socket success: sock std::endl; // 3// 2. 给服务器指明IP地址和端口号Portstruct sockaddr_in local; // 这个 local 在哪里定义呢用户空间的特定函数的栈帧上不在内核中 bzero(local, sizeof(local)); // 清空上述字段udlocal.sin_family AF_INET; // PF_INET 初始化socketaddr_in结构local.sinport htons(port); // 本地主机序列构建的port_需要从主机序列转变成网络序列// inet_addr: 1,2// 1. 字符串风格的IP地址转换成为4字节int, 1.1.1.1 - uint32_t - 能不能强制类型转换呢不能这里要转化// 2. 需要将主机序列转化成为网络序列local.sin_addr.s_addr inetaddr(ip.c_str()); // sin_addr C中的结构体在C中可以进行转化但是在C语言中不行// 这里需要将字符串转换uint32_t的类型并且同时进行将主机序列转换成网络序列// inet_addr 函数将包含 IPv4 点十进制地址的字符串转换为IN_ADDR结构的正确地址。而在in_addr结构之中有in_addr_t saddr的一个数据结构if (bind(sock, (struct sockaddr*)local, sizeof(local)) 0) // 然后是绑定相关的套接字文件此时需要就将前面在帧栈上定义的local进行绑定{std::cerr bind socket error: strerror(errno) std::endl;exit(BINDERR);}std::cout bind socket success: sock std::endl; //3}void Start() {}~UdpServer() {}private:int sock_;uint16t port;std::string ip_; // 后面需要去掉这个ip}; }然后运行上述的程序会出现一个问题就是 server addr: 1.1.1.1 : 8082 create socket success: 3 bind socket error: Cannot assign requested address bind socket error: Cannot assign requested address 云服务器不需要bind ip地址需要让服务器自己制定ip地址 云服务器或者一款服务器一般不要指明某一个确定的IP – 服务器可能有多张网卡可能配有多个IP我们要让我们的udpserver启动的时候bind本主机上的任意IP然后我们对上述的v1版本进行修改。 // 需要修改的地方就是 local.sin_addr.s_addr INADDR_ANY; // 让我们的udpserver在启动的时候bind本主机上的任意IPserver addr: 1.1.1.1 : 8082 create socket success: 3 bind socket success: 3 此时就可以正确的进行bind操作。 udp_v2 下面我们将上述的程序进行完善添加上服务器正常工作的程序我们想要完成的是客户端发送消息服务端接收到消息并答应在终端上同时将消息返回给客户端 void Start() {// 服务器的正常工作char buffer[1024];while (true){// 收// ssize_t 实际写入的大小 recvfrom(int sockfd 绑定的套接字, void *buf 接受数据存放的缓冲区, size_t len 缓冲区长度, int flags 读取方式(0), struct sockaddr *src_addr 需要知道client的IP和PORT 输入接收缓冲区, socklen_t *addrlen 实际结构体的大小); 输入输出型参数 需要知道谁发的数据struct sockaddr_in peer;socklent len sizeof(peer); // 这里一定要写清楚未来传入的缓冲区的大小int n recvfrom(sock, buffer, sizeof(buffer)-1/因为这是以字符作为消息的类型所以缓冲区预留一部分空间/, 0, (struct sockaddr)peer, len); // 消息的类型需要程序员来定义if (n 0) buffer[n] \0;else continue;// 提取client信息std::string clientip inet_ntoa(peer.sin_addr); // 把一个四字节的IP转化为字符串uint16_t clientport ntohs(peer.sinport); // 将从网络中获取的端口号转换成主机std::cout clientip - clientport # buffer std::endl;// 发sendto(sock, buffer, strlen(buffer), 0, (struct sockaddr)peer, len); // 往文件中去写的时候不需要携带\0} }然后对udp_server.cc文件进行修改我们想要使用./udp_server port的形式来运行程序在运行程序的时候将端口号进行传入 static void usage(string proc) {std::cout Usage:\n\t proc port\n std::endl; } // ./udp_server port int main(int argc, char* argv[]) {if (argc ! 2){usage(argv[0]);exit(USAGE_ERR);}uint16_t port atoi(argv[1]); // 此处为char*类型的数据若是要传入port需要进行转换unique_ptrUdpServer usvr(new UdpServer(port));// bind socket error: Cannot assign requested address 云服务器不需要bind ip地址需要让服务器自己制定ip地址// 自己本地装的虚拟机或者是物理机器是允许的usvr-InitServer(); //服务器的初始化usvr-Start();return 0; }然后是服务端的代码 // udp_client.cc #include string #include cstring // 127.0.0.1 本地回环表示的就是当前的主机通常用来进行本地通信或者测试 static void usage(std::string proc) {std::cout Usage:\n\t proc serverip serverport\n std::endl; } // ./udp_client serverip serverport int main(int argc, char *argv[]) {if (argc ! 3){usage(argv[0]);exit(USAGE_ERR);}std::string serverip argv[1];uint16_t serverport atoi(argv[2]);int sock socket(AF_INET, SOCK_DGRAM, 0);if (sock 0){std::cerr create socket error std::endl;exit(SOCKET_ERR);}// client 这里要不要bind 要的socket通信的本质[clientip:clientport, serverclient. serverport]// 要不要自己bind不需要自己bindos自动给我们进行bind– 为什么client的port要随机让os分配防止client出现启动冲突// server为什么要自己bind – 1. server的端口不能随意改变, 众所周知且不能随意改变2. 同一家公司的port需要统一进行管理// 明确server是谁struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(serverport);server.sin_addr.s_addr inet_addr(serverip.c_str());while (true){// 用户输入std::string message;std::cout please Enter# ;std::cin message;// 什么时候bind在我们首次系统调用发送数据的时候OS会在底层随机选择clientport 自己的IP 1. bind 2. 构建发送的数据报文// 发送sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)server, sizeof(server));// 接收char buffer[2048];struct sockaddr_in temp;socklen_t len sizeof(temp);int n recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr )temp, len);if (n 0){buffer[n] 0;std::cout server echo# buffer std::endl;}}return 0; }这样我们就完成了一个简单的UDP网络通信程序。 udp_v3 下面我们还需要对上述的UDP网络通信进行修改在上述的网络通信程序中服务端在接收到客户端的信息之后立即进行了处理然后将信息进行返回但是我们想要让信息的处理与网络IO进行分离在第三版中我们就进行对应的修改。我们在工作空间中定义了using func_t std::functionstd::string (std::string);这样的参数为string返回值为string的一个包装器在udp_server类中新增了一个成员funct service;来进行业务处理。在编写构造函数的时候将这个成员初始化那么我们就可以在外部传入一个执行业务的方法在外部定义好了这个执行的方法在外部即udp_server.cc中将业务处理完毕之后再进行网络IO。 下面就是对上述版本的修改 // udpserver.hpp // 在Start()中发送消息前执行业务处理 void Start() {// …// 做业务处理std::string response service(buffer);// 发sendto(sock_, response.c_str(), response.size(), 0, (struct sockaddr)peer, len); // 往文件中去写的时候不需要携带\0 }然后我们就在cpp文件中处理上层业务 // udp_server.cc std::string transactionString(std::string request) {std::string result;char c;for(auto r : request){if (islower®) {c toupper®;result.push_back©;}else {result.push_back®;}}return result; }static bool isPass(const std::string command) {bool pass true;auto pos command.find(mv);if (pos ! std::string::npos) pass false;pos command.find(rm);if (pos ! std::string::npos) pass false;return pass; }std::string execteCommand(std::string command) {// FILE *popen(const char *command, const char *type);// 1. 创建管道// 2. 创建子进程// 3. 通过FILE将结果直接返回可以让用户以读取文件的访问获得命令执行的结果// 安全检查if (!isPass(command)) return bad;// 业务逻辑处理FILE fp popen(command.c_str(), r);if (fp nullptr) return None;// 获取结果char line[2048];std::string result;while (fgets(line, sizeof(line), fp) ! NULL){result line;}return result;fclose(fp); }int main() {// …// 通过传入第一个transactionString函数可以将我们客户端输入的小写字符在服务端转换成大写然后在返回客户端unique_ptrUdpServer usvr(new UdpServer(transactionString, port)); // 这个业务方法就是将客户端输入的指令发送到服务端在服务端执行后再将执行的结果返回给客户端// unique_ptrUdpServer usvr(new UdpServer(execteCommand, port)); }大小写转换业务 读取客户端指令的业务
udp_v4 上述第三版的程序还有一些问题就是程序运行的时候收发的处理都在start()函数中但是这样一旦阻塞在发送或者阻塞在收取的时候假如有多个客户端要连接服务端就会有影响。那么在第四版中我们就将结合前面的讲述过的基于环形队列的生产消费模型来将接受与发送分别使用两个线程来处理。在第四版中我们想要实现的是一个简易的udp多人聊天程序。 // udp_server.hpp class UdpServer { public:UdpServer(uint16_t port defaultport) :port(port){pthread_mutex_init(lock, nullptr);p new Thread(1, std::bind(UdpServer::Recv, this)); // 这里传入接受与发送的函数时如果直接传入会发生报错以为Recv与Broadcast都是类的方法而类的方法是有隐含的this指针的此时就可以使用bind函数将this指针这个成员先绑定然后就可以正常的运行。c new Thread(2, std::bind(UdpServer::Broadcast, this));}void Start(){// …c-run();p-run();}void addUser(const std::string name, const struct sockaddr_in peer) // 构建一个新用户{LockGuard lockguard(lock);// onlineuser[name] peer;auto iter onlineuser.find(name); // 遍历检测是否存在该用户if (iter ! onlineuser.end()) return;onlineuser.insert(std::pairconst std::string, const struct sockaddr_in(name, peer));}void Recv(){// 服务器的正常工作char buffer[1024];while (true){// 收// ssize_t 实际写入的大小 recvfrom(int sockfd 绑定的套接字, void *buf 接受数据存放的缓冲区, size_t len 缓冲区长度, int flags 读取方式(0), struct sockaddr *src_addr 需struct sockaddr_in peer;socklent len sizeof(peer); // 这里一定要写清楚未来传入的缓冲区的大小int n recvfrom(sock, buffer, sizeof(buffer)-1/因为这是以字符作为消息的类型所以缓冲区预留一部分空间/, 0, (struct sockaddr*)peer, len); // 消息的类型需要程序员if (n 0) buffer[n] \0;else continue;std::cout recv done … std::endl;// 提取client信息std::string clientip inet_ntoa(peer.sin_addr); // 把一个四字节的IP转化为字符串uint16_t clientport ntohs(peer.sin_port); // 将从网络中获取的端口号转换成主机std::cout clientip - clientport # buffer std::endl;// 构建一个用户并检查std::string name clientip;name -;name std::to_string(clientport);// 如果不存在就插入如果存在什么都不做addUser(name, peer);std::string message name buffer;rq.push(message); // 将接收到的信息存放入生产消费中}}void Broadcast(){while (true){std::string sendstring;rq.pop(sendstring); // 从生产消费模型中获取信息std::vectorstruct sockaddr_in v; // 获取所有的用户使用数组进行记录{LockGuard lockguard(lock); // 使用锁进行保护防止产生冲突for (auto user : onlineuser){v.pushback(user.second);}}for (auto user : v) // 依次将信息发送出去{sendto(sock, sendstring.c_str(), sendstring.size(), 0, (struct sockaddr *)user, sizeof(user));}}}~UdpServer(){pthread_mutex_destroy(lock);c-join();p-join();delete p;delete c;} private:// …std::unordered_mapstd::string, struct sockaddr_in onlineuser; // 添加使用用户RingQueuestd::string rq; // 基于环形队列的生产消费模型pthread_mutex_t lock; // 互斥锁Thread *c, p; // 创建生产者与消费者线程 };对于客户端同样可以使用多线程 udp_client.cc void recver(void* args) {pthread_detach(pthread_self());int sock static_castint(args);while (true){// 接收char buffer[2048];struct sockaddr_in temp;socklen_t len sizeof(temp);int n recvfrom(sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr *)temp, len);if (n 0){buffer[n] 0;std::cout buffer std::endl;}} } int main(int argc, char *argv[]) {// …pthread_t tid;pthread_create(tid, nullptr, recver, sock);while (true){// 用户输入std::string message;std::cerr please Enter# ; // 往2号文件发送// std::cin message;getline(std::cin, message);// 发送sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)server, sizeof(server));}return 0; }如上图所示就可以看到在右图中两个不同的客户端发送的消息都可以被看到。