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

o2o平台有哪些网站wordpress音乐分享

o2o平台有哪些网站,wordpress音乐分享,江西省建设厅教育网站,手机网站制作要求标准目录 一. 端口号的概念 二. 对于UDP和TCP协议的认识 三. 网络字节序 3.1 字节序的概念 3.2 网络通信中的字节序 3.3 本地地址格式和网络地址格式 四. socket编程的常用函数 4.1 sockaddr结构体 4.2 socket编程常见函数的功能和使用方法 五. UDP协议实现网络通信 5.…目录 一. 端口号的概念 二. 对于UDP和TCP协议的认识 三. 网络字节序 3.1 字节序的概念 3.2 网络通信中的字节序 3.3 本地地址格式和网络地址格式  四. socket编程的常用函数  4.1 sockaddr结构体 4.2 socket编程常见函数的功能和使用方法 五. UDP协议实现网络通信 5.1 UDP协议服务端的封装 5.2 UDP协议客户端的封装 六. TCP协议实现网络通信 6.1 TCP协议服务端的封装 6.2 TCP协议客户端的封装 七. 总结 一. 端口号的概念 进行网络通信其根本目的并不是让两个主机之间进行通信而是让运行在主机上的两个进程之间相互通信。如我们在日常生活中经常要通过微信发送消息我发送的消息必须要经过网络传输才能够被对方接受。我发送的消息并没有被对方主机的其他应用接受而只是被对方主机上运行的微信这一进程接受。由此可见网络通信本质上是网络中的两主机通过网络实现进程间通信为此OS必须通过特定的方式来标识接受数据的进程。 端口号是在某一主机上用来标识进程唯一性的编号与之对应的IP地址用于表示网络中唯一的一台主机因此IP地址  端口号可用于表示全网中唯一的一个进程。 关于端口号有如下的基本结论 端口号用于表示主机中的唯一一个进程。端口号是一个16位、2字节的整数其数据类型为uint16_t。IP  端口号可用于表示全网唯一一个进程。一个端口号只能对应一个进程而一个进程能够对应多个端口号。 端口号port和进程pid之间的关系每个进程都有对应的pid用于在系统中标识特定进程但不进行网络通信的进程不需要有端口号理论上讲id  port也可以识别网络中唯一一个进程但是使用端口号和id能够实现系统进程和网络通信功能之间的解耦。 二. 对于UDP和TCP协议的认识 UDP协议即用户数据报协议User Datagram Protocol其特征有 属于传输层面的协议。不需要连接 -- 一方给另一方发送数据时不需要另一方处于等待状态。不可靠传输 -- 可能出现失帧、丢包等问题。面向数据报的通信方式。 TCP协议即传输控制协议Transmission Control Protocol其特征有 属于控制层面的协议。需要连接 -- 一方给另一方发送数据时另一方必须处于等待状态发送数据前要预先建立连接才能够发送成功。可靠传输 -- 不会出现失帧、丢包等问题。面向字节流的通信。 对于需要连接和不需要连接的理解需要链接类似于生活中的接打电话我们给一方打电话的时候对方需要听到电话铃声确认接听才能通信确认接听电话就类似于网络通信中的建立链接。不需要连接类似于生活中发送电子邮件的通信方式我们要给某人发送电子邮件时可以不用事先通知对方只要发送等待对方合适的时候查看即可对方不需要事先准备接收邮件即不需要连接。 对于可靠传输和不可靠传输的理解可靠传输和不可靠传输并不是好坏的评判标准原因是(a). UDP协议虽然可能存在丢包失帧等问题但是发生问题的概率极小有些时候这并不是不可以接受的。 (b). 虽然TCP协议不会出现UDP这样的不可靠传输的问题但是可靠通信的建立是需要成本的在有些可以一定程度接受数据传输出现问题的场景采用TCP协议综合效益并不高。 三. 网络字节序 3.1 字节序的概念 内存中存储数据的字节序有两种(1). 小端字节序 -- 低位存储在低地址高位存储在高地址。(2). 大端字节序 -- 低位存储在高地址高位存储在低地址。 图3.1以十六进制表示的数据int num 0XAABBCCDD为例展示了大端机和小端机存储数据的规则这个数据第低位为DD高位为AA。 图3.1 小端机和大端机使用内存的方式 3.2 网络通信中的字节序 假设这样一种场景一台小端机要通过网络给一台大端机发送数据假设他们以他们各自的字节序发送向网络中发数据和从网络中读数据那么就会出现“乱序”问题因此需要一定的协议用于规范网络数据的字节序以避免“乱序问题”。 规定网络中的数据全部采用大端字节序。 我们有时候无法确定发送的数据或者从网络中读取来的数据是大端还是小端为了保证发送和读取数据的可靠性C标准库提供了下面4个函数可以实现网络和主机数据之间的相互转换 uint32_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位网络数据转为主机格式。  3.3 本地地址格式和网络地址格式  一般我们在主机中标识ip地址都采用const char*数据类型、点分十进制方法来表示 如1.23.122.234但是在网络中为了节省资源ip应当采用四字节的方法来表示下面几个函数的功能是实现本地const char*点分十进制ip格式和网络四字节ip格式之间的转化 in_addr_t inet_addr(const char *cp) -- 本地ip格式转网络ip格式。const char* inet_ntoa(struct in_addr in) -- 网络ip格式转本地ip格式。const char *inet_ntop(int af, const void *src, char *dfs, socklen_t len) -- 网络ip格式转本地ip格式将转换后的结果存储在dfs所指向的空间中。 注意一般建议使用inet_ntop函数而不是采用inet_ntoa函数因为inet_ntoa为了返回本地格式的ip会在函数内部开辟一块static空间来记录转换来的结果这样就带来两个问题1. 线程不安全  2. 如果多次调用inet_ntoa函数那么最后一次调用的返回结果会覆盖掉前面的结果。而采用inet_ntop函数返回结果会被存储在用户指定的buffer空间中杜绝了inet_ntoa函数的这两个问题。 inet_ntop函数的af参数表示通信方式AF_INET表示ipv4格式网络地址AF_INET6表示ipv6格式网络地址src为指向struct sin_addr类型数据的指针dfs为接收结果的输出型参数。 四. socket编程的常用函数  4.1 sockaddr结构体 图4.1给出了sockaddr、sockaddr_in和sockaddr_un的结构其中sockaddr为socket API抽象出来的一种结构体使用与ipv4、ipv6、udp、tcp、本地通信等各种形式的socket。 sockaddr_in为网络通信使用的结构体sockaddr_un为本地通信使用的结构体。 图4.1 sockaddr结构体 网络通信常用的结构为sockaddr_in其内容包括16位地址类型AF_INIT用于确定通信方式为网络通信、32位IP地址用于在网络中定位特定的主机、8字节填充没有实际意义一般为0。 struct sockaddr_in的定义 struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port; /* Port number. */struct in_addr sin_addr; /* Internet address. *//* Pad to size of struct sockaddr. */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];}; struct in_addr的定义 typedef uint32_t in_addr_t; struct in_addr{in_addr_t s_addr;}; 4.2 socket编程常见函数的功能和使用方法 对于socket程序一般包含四个头文件即可支持所有的socket相关函数 #include sys/types.h #include sys/socket.h #include netinet/in.h #include apra/inet.h 创建socket文件描述符函数 -- socket UDP协议和TCP协议、客户端以及服务端都需要创建socket文件描述符。socket文件描述符与系统级文件描述符的本质相同都是数组arrayfd对应的一个下标表示进程打开了某个特定的文件。 socket函数 -- 创建socket文件描述符 函数原型int socket(int domain, int type, int protocol) 函数参数 domain -- 选取通信的范围网络还是本地AF_INET为网络通信AF_LOCAL为本地通信。type -- 通信类型UDP协议传SOCK_DGRAMTCP协议传SOCK_STREAM。protocal -- 指定协议类型一般传0即可。 返回值如果成功返回创建的socket文件描述符如果失败返回-1。 绑定端口号-- bind 将用户给出和端口号在内核中与当前的进程相关联。在TCP和UDP协议的服务端都需要使用bind绑定。在客户端不采用bind绑定用户设定的端口号因为如果客户端由用户设置端口号如果这个端口号被其他进程占用那么会出现绑定失败的问题。客户端在第一次给服务端发送消息时由OS自动分配端口号。 bind函数 -- 绑定端口号 函数原型int bind(int socket, const struct sockaddr *address, socklen_t *len) 函数参数 socket -- socket文件描述符address -- 输入型参数用于指定套接字sockaddrlen -- 输入型参数用于给出sockaddr结构体的长度占用多少字节 返回值成功返回0失败返回-1。 设置监听状态 -- listen 一般用于TCP协议中的服务器端。在TCP协议中只有设置服务器处于监听状态客户端才可以与服务器建立链接并实现通信。 listen函数 -- 设置监听状态 函数原型int listen(int socket, int backlog) 函数参数 socket -- socket文件描述符backlog -- 应传一个不太大也不太小的数字。 返回值成功返回0失败返回-1。 建立通信连接 -- connect 一般用于TCP协议的客户端。在TCP协议中如果客户端希望与服务端通信那么就必须与服务端建立连接。客户端使用connect函数与服务器建立链接必须保证服务器处于监听状态。 connect函数 -- 建立通信连接 函数原型int connect(int socket, struct sockaddr *address, socklen_t len) 函数参数 socket -- socket文件描述符address -- 被链接的一端的套接字len -- 套接字长度 返回值成功返回0失败返回-1。 接受通信另一方的连接请求 -- accept 如果另一方尝试与本地进程建立网络通信那么accept可以用于接收对方的连接。accept函数一般用于TCP协议的服务端只有accept函数成功接收了另一端的连接请求才算是真正建立起来通信连接。 accept函数 -- 接受通信另一端的连接请求 函数原型int accept(int socket, struct sockaddr_in *address, socklen_t *len) 函数参数 socket -- socket文件描述符。address -- 输出型参数用于获取请求连接的一方的套接字信息。len -- 套接字长度。 返回值成功返回用于通信的“网络文件”的文件描述符失败返回-1。 从网络中读取数据函数 -- recvfrom 从网络中读取指定长度的数据到指定的缓冲区中。用于UDP协议的客户端和服务端读取对端消息。 recvfrom函数 -- UDP协议从网络中读取数据 函数原型ssize_t recvfrom(int socket, void *buffer, size_t length, int flag, struct sockaddr *addr, socklen_t *addr_length) 函数参数 socket -- socket文件描述符。buffer -- 接受数据的缓冲区。length -- 至多读取的字节数。flag -- 读取数据的方式一般设置为0表示阻塞式等待网络中被写入数据。addr -- 输出型参数用于接受发送数据的主机ip及进程端口号。addr_length套接字长度。 返回值读取成功返回读到的字节数对端关闭返回0失败返回-1。 从网络中读取数据 -- recv 从网络中读取数据到指定缓冲区。用于TCP协议中客户端和服务端读取对端消息。 recvfrom函数 -- TCP协议从网络中读取数据 函数原型ssize_t recv(int socket, void *buffer, size_t length, int flag) 函数参数 socket -- socket文件描述符。buffer -- 接受数据的缓冲区。length -- 至多读取的字节数。flag -- 读取数据的方式一般设置为0表示阻塞式等待网络中被写入数据。 返回值读取成功返回读到的字节数对端关闭返回0失败返回-1。 向网络中发送数据函数 -- sendto 可以通过指定ip和端口号将数据发送给指定主机的某个进程。给定端口号和ip时要注意将ip和端口号转为网络格式。 sendto函数 -- 向网络中发送数据 函数原型ssize_t sendto(int socket, void *buffer, size_t length, int flag, const struct sockaddr *addr, socklen_t addr_length) 函数参数 socket -- socket文件描述符。buffer -- 存储待发送数据的缓冲区。length -- 要发送的数据的字节数。flag -- 发生数据的方式一般直接设置为0即可。addr -- 指定接受数据的主机ip和端口号。addr_length套接字长度。 返回值成功返回发送出去的字节数失败返回-1。 五. UDP协议实现网络通信 5.1 UDP协议服务端的封装 本文实现一个服务端的demo程序其功能为服务端从客户端读取数据记录数据源主机的ip和端口号如果源主机第一次向服务器发送数据就将该主机的ip和端口号插入到哈希表中每次服务器接受到数据就将数据发回哈希表中记录的主机这样就模拟实现了简单的群聊功能。 服务端初始化步骤创建socket文件描述符 - 绑定端口号。 服务端启动后的工作流程通过recvfrom函数从客户端读取数据 - 判断发送数据的客户端是否已经向服务器发送过数据如果没有那么将客户端的ip和端口号插入哈希表 - 将读取到的数据发回哈希表中记录的客户端。 启动服务端程序时要指定端口号以便客户端能够与服务端建立通信一般来说服务端不用显示设定ip而是通过INADDR_ANY来设置这表示无论客户端ip是多少都可以实现与服务端的通信。 Log.hpp文件日志打印相关内容 #pragma once #include iostream #include cstdio #include cstdarg #include ctime#define DEBUG 0 #define NORMAL 1 #define WARING 2 #define ERROR 3 #define FATAL 4const char* g_levelMap[5] {DEBUG,NORMAL,WARING,ERROR,FATAL };void logMessage(int level, const char *format, ...) {// 1. 输出常规部分time_t timeStamp time(nullptr);struct tm *localTime localtime(timeStamp);printf([%s] %d-%d-%d, %02d:%02d:%02d\n, g_levelMap[level], localTime-tm_year, localTime-tm_mon, \localTime-tm_mday, localTime-tm_hour, localTime-tm_min, localTime-tm_sec);// 2. 输出用户自定义部分va_list args;va_start(args, format);vprintf(format, args);va_end(args); } udp_serve.hpp文件对服务端封装 #pragma once#include Log.hpp #include iostream #include string #include unordered_map #include cerrno #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.hclass Server { public:// 服务端构造函数Server(uint16_t port, const std::string ip ): _port(port), _ip(ip), _sock(-1){ }// 初始化服务器void init(){// 1. 创建网络套接字_sock socket(AF_INET, SOCK_DGRAM, 0); if(_sock 0) // 检查套接字创建成功与否{logMessage(FATAL, %d:%s\n, errno, strerror(errno));exit(2);}logMessage(DEBUG, 套接字创建成功, _sock:%d\n, _sock);// 2. bind:将用户设置的ip和port在内核中与进程相关联struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET;// INADDR_ANY: 表示服务器在工作过程中可以从任意ip获取数据// inet_addr函数: 将主机ip转为4字节网络iplocal.sin_addr.s_addr _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); local.sin_port htons(_port); // 将主机端口转换为网络端口格式if(bind(_sock, (struct sockaddr *)local, sizeof(local)) 0){logMessage(FATAL, %d:%s\n, errno, strerror(errno));exit(3);}logMessage(DEBUG, 用户设置的ip和port在内核中与进程相关联成功\n);}// 启动服务器程序void start(){// 服务器进程永不退出// 从客户端读取数据char buffer[1024]; // 输出缓冲区char key[128]; // 存储客户端ip和端口号struct sockaddr_in sock_cli; // 客户端套接字memset(sock_cli, 0, sizeof(sock_cli)); // 初始化0socklen_t len sizeof(sock_cli); // 输入型参数 -- 套接字长度std::string addr_cli; // 数据源客户端的ipuint16_t port_cli; // 数据源客户端的端口号while(true){// 输出读取到的数据memset(buffer, 0, sizeof(buffer));memset(key, 0, sizeof(key));ssize_t n recvfrom(_sock, (void *)buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)sock_cli, len);if(n 0){buffer[n] 0; // 添加尾部/0addr_cli inet_ntoa(sock_cli.sin_addr); // inet_ntoa函数负责将网络ip转换为主机ipport_cli ntohs(sock_cli.sin_port); // 网络套接字转换为主机套接字snprintf(key, 128, [%s-%d], addr_cli.c_str(), port_cli);printf([%s:%d]# %s\n, addr_cli.c_str(), port_cli, buffer); // 输出发送端的ip和port以及发送的内容}else if(n 0){logMessage(DEBUG, 未读取到数据!\n);continue;}else // 数据读取失败{logMessage(ERROR, 读取数据失败!\n);continue;}// 将客户端的ip和port插入到哈希表if(_mp.find(key) _mp.end()){_mp.insert({key, sock_cli});logMessage(NORMAL, 成功插入客户端, %s\n, key);}// 将读取到的数据全部发送给客户端主机for(const auto iter : _mp){std::string msg_cli;msg_cli key;msg_cli # ;msg_cli buffer;if(sendto(_sock, msg_cli.c_str(), msg_cli.size(), 0, (struct sockaddr *)(iter.second), sizeof(iter.second)) 0){logMessage(ERROR, 服务器发回消息失败!);continue;} logMessage(NORMAL, 向客户端写回数据 -- %s\n, iter.first.c_str());}}}// 析构函数~Server(){if(_sock 0){close(_sock);}}private:uint16_t _port; // 端口号std::string _ip; // 服务器ip地址int _sock; // 套接字对应文件描述符std::unordered_mapstd::string, struct sockaddr_in _mp; // 哈希表记录接收到信息的客户端的ip和port }; udpserve.cc文件服务端源文件 #include udp_serve.hpp #include memoryvoid usage(const char *command) {std::cout \nUsage# command port\n std::endl; }int main(int argc, const char **argv) {if(argc ! 2){usage(argv[0]);exit(1);}uint16_t port static_castuint16_t(atoi(argv[1]));std::unique_ptrServer psvr(new Server(port));psvr-init();psvr-start();return 0; } 5.2 UDP协议客户端的封装 本文采用多线程的方式实现UDP客户端demo程序一个线程负责从服务器读取数据另一个线程负责向服务器发送数据。 客户端初始化init创建socket即可不需要bind端口号当客户端第一次向服务端发送数据的时候OS会自动为客户端进程分配端口号。 客户端启动函数执行的工作创建两个线程一个调用recvfrom函数从服务端读数据另一个调用sendto函数向服务器写数据。 启动客户端程序时需要告知服务器对应的ip和端口号才能够成功与服务器建立通信。 udp_client.hpp文件封装客户端 #pragma once#include Log.hpp #include Thread.hpp #include iostream #include string #include cerrno #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.hstruct SendMessageData {int _sock;struct sockaddr_in _sock_srv;socklen_t _len;SendMessageData(int sock, struct sockaddr_in sock_srv): _sock(sock), _sock_srv(sock_srv), _len(sizeof(sock_srv)){} };class Client { public:// 构造函数Client(const std::string ip, uint16_t port): _ip(ip), _port(port), _sock(-1){}// 初始化函数void init(){// 1. 创建网络套接字_sock socket(AF_INET, SOCK_DGRAM, 0);if (_sock 0){logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}// 2. 绑定 -- 客户端需要绑定但一般不会由程序员来进行绑定// 而是在第一次发送消息时由OS自动分配ip和port}// 客户端启动程序void start(){// 创建服务器的struct sockaddrstruct sockaddr_in srv_sock;memset(srv_sock, 0, sizeof(srv_sock));srv_sock.sin_family AF_INET;srv_sock.sin_addr.s_addr inet_addr(_ip.c_str()); // 主机ip转为网络ipsrv_sock.sin_port htons(_port); // 主机port转为网络portSendMessageData sendData(_sock, srv_sock);// 发送消息thread th_send(send_message, (void *)sendData);th_send.start();// 接受反馈回来的消息thread th_recieve(recieve_message, (void *)_sock);th_recieve.start();th_send.join();th_recieve.join();}// 析构函数~Client(){if (_sock 0){close(_sock);}}private:static void *send_message(void *args){SendMessageData *ptr (SendMessageData *)args;while (true){std::string msg;std::cerr 请输入你要发送的消息: std::flush;std::getline(std::cin, msg); // 按行读取sendto(ptr-_sock, msg.c_str(), msg.size(), 0, (const sockaddr *)ptr-_sock_srv, ptr-_len);}return nullptr;}static void *recieve_message(void *args){// memset(buffer, 0, sizeof(buffer));char buffer[1024];while (true){struct sockaddr tmp;memset(tmp, 0, sizeof(tmp));socklen_t len sizeof(tmp);ssize_t n recvfrom(*(int *)args, (void *)buffer, sizeof(buffer) - 1, 0, (sockaddr *)tmp, len);if (n 0){// std::cerr aaaa std::endl;buffer[n] \0;printf(%s\n, buffer);}}return nullptr;}std::string _ip; // 发生数据的主机ipuint16_t _port; // 端口号int _sock; // 套接字 }; udp_client.cc文件客户端源文件 #include udp_client.hpp #include memory #include signal.hvoid Usage(const char *command) {std::cout /nUsage: command ip port\n std::endl; }int main(int argc, const char **argv) {if(argc ! 3){Usage(argv[0]);exit(1);}// signal(SIGCHLD, SIG_IGN);std::unique_ptrClient pcli(new Client(argv[1], atoi(argv[2])));pcli-init();pcli-start();return 0; } 六. TCP协议实现网络通信 6.1 TCP协议服务端的封装 本文采用多进程的方式来编写服务端demo代码每次接收到客户端的连接请求就为这个客户端创建一个进程用于与该客户端通信。 服务端初始化init创建socket套接字 - 将本地ip和端口号在内核中与当前进程绑定 - 设置服务端进程处于listen状态以便随时接受客户端的连接请求。 服务端启动start接受客户端的连接请求并记录请求连接的客户端的套接字 - 创建子进程 - 在子进程中调用读取客户端发送的信息 - 读取成功后发回给客户端。 启动服务器时需要显示给出端口号以便客户端能够顺利连接到服务器。 tcp_server.hpp文件服务端封装 #pragma once#include Log.hpp #include iostream #include unistd.h #include string #include cerrno #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include sys/wait.h #include unistd.hconst int g_size 1024;static void server(int serverSock, struct sockaddr_in sock_cli) {char buffer[g_size]; // 存储读取数据的缓冲区while(true){ssize_t n recv(serverSock, buffer, g_size - 1, 0); // 读取数据if(n 0){buffer[n] \0;uint16_t cli_port ntohs(sock_cli.sin_port);char cli_addr[20]; // 地址memset(cli_addr, 0, sizeof(cli_addr));// socklen_t len sizeof(sock_cli.sin_addr);inet_ntop(AF_INET, sock_cli.sin_addr, cli_addr, sizeof(cli_addr));printf([%s-%d] %s\n, cli_addr, cli_port, buffer);// 发回客户端send(serverSock, buffer, strlen(buffer), 0);}else if(n 0){logMessage(DEBUG, 对端关闭读取结束\n);break;}else // n 0{logMessage(ERROR, 读取失败\n);break;}}close(serverSock); }class TcpServer { public:TcpServer(uint16_t port, const std::string ip ): _port(port), _ip(ip), _listenSock(-1){ }// 服务器初始化void init(){// 1. 创建网络套接字_listenSock socket(AF_INET, SOCK_STREAM, 0);if (_listenSock 0) // 创建socket失败{logMessage(FATAL, socket error, %d:%s\n, errno, strerror(errno));exit(2);}logMessage(NORMAL, socket success, _listenSock:%d\n, _listenSock);// 2. 绑定端口号struct sockaddr_in local;memset(local, 0, sizeof(local));local.sin_family AF_INET; // 设置网络协议族local.sin_addr.s_addr _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); // 本地格式地址转为网络格式local.sin_port htons(_port); // 本地格式端口号转为网络格式if (bind(_listenSock, (const sockaddr *)local, sizeof(local)) 0){logMessage(FATAL, bind error, %d:%s\n, errno, strerror(errno));exit(3);}logMessage(NORMAL, bind success, %s:%d\n, _ip.c_str(), _port);// 3. 设置监听状态if (listen(_listenSock, _backlog) 0){logMessage(FATAL, listen error, %d:%s\n, errno, strerror(errno));exit(4);}logMessage(NORMAL, listen success\n);logMessage(NORMAL, Server Init Success!\n);}// 运行服务器void start(){while (true){// 接受客户端的链接请求struct sockaddr_in sock_cli;memset(sock_cli, 0, sizeof(sock_cli));socklen_t len sizeof(sock_cli);int serverSock accept(_listenSock, (struct sockaddr *)sock_cli, len);if (serverSock 0) // 接受客户端请求失败{logMessage(ERROR, accept error, %d:%s\n, errno, strerror(errno));continue;}uint16_t cli_port ntohs(sock_cli.sin_port);char cli_addr[20]; // 地址memset(cli_addr, 0, sizeof(cli_addr));inet_ntop(AF_INET, sock_cli.sin_addr, cli_addr, len);cli_addr[strlen(cli_addr)] \0;logMessage(NORMAL, accept success [%s-%d]\n, cli_addr, cli_port);// 多进程接受客户端信息pid_t id fork();if (id 0){if (fork() 0)exit(0); // 子进程退出// 子进程的子进程孙子进程此时变为孤儿进程// 由1号进程领养OS自动回收进程server(serverSock, sock_cli);exit(0);}waitpid(id, nullptr, 0);close(serverSock);}}// 析构函数~TcpServer(){if (_listenSock 0){close(_listenSock);}}private:uint16_t _port; // 端口号std::string _ip; // 本地ipint _listenSock; // socket文件描述符static const int _backlog 20; }; tcp_server.cc文件服务端源文件 #include tcp_server.hpp #include memoryvoid Usage(const char *proc) {std::cout \nServer Usage# proc ServerPort\n std::endl; }int main(int argc, const char **argv) {if(argc ! 2){Usage(argv[0]);exit(1);}std::unique_ptrTcpServer ptr_srv(new TcpServer(atoi(argv[1])));ptr_srv-init();ptr_srv-start();return 0; } 6.2 TCP协议客户端的封装 TCP协议客户端需要先调用connect函数尝试与服务器建立链接才能与服务器正常通信。 运行TCP协议客户端的时候需要显示给的IP地址和服务器对应的端口号。 tcp_client.hpp文件客户端封装 #pragma once#include Log.hpp #include iostream #include unistd.h #include string #include cerrno #include cstring #include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include sys/wait.h #include unistd.hclass TcpClient { public:TcpClient(const std::string ip, uint16_t port): _ip(ip), _port(port), _sock(-1){ }// 初始化客户端void init(){// 创建socket文件描述符_sock socket(AF_INET, SOCK_STREAM, 0);if(_sock 0){logMessage(FATAL, socket error, %d:%s\n, errno, strerror(errno));exit(2);}logMessage(NORMAL, socket success, _sock:%d\n, _sock);// 客户端与服务端连接struct sockaddr_in sock_srv;memset(sock_srv, 0, sizeof(sock_srv));sock_srv.sin_family AF_INET;sock_srv.sin_addr.s_addr inet_addr(_ip.c_str());sock_srv.sin_port htons(_port);socklen_t len sizeof(sock_srv);if(connect(_sock, (struct sockaddr *)sock_srv, len) 0){logMessage(FATAL, connect error, %d:%s\n, errno, strerror(errno));exit(3);}logMessage(NORMAL, connect success\n);logMessage(NORMAL, Client Init Success\n);}void start(){std::string msg; // 发送的消息char buffer[1024]; // 接受服务器发回的消息while(true){std::cout 请输入你要发送的消息: std::flush;std::getline(std::cin, msg);if(msg quit)break;ssize_t n send(_sock, msg.c_str(), msg.size(), 0);if(n 0){logMessage(NORMAL, 成功发送数据\n);ssize_t s recv(_sock, buffer, 1023, 0);if(s 0){buffer[s] \0;printf(回显# %s\n, buffer);}else if(s 0){logMessage(DEBUG, 服务器退出!\n);break;}else{logMessage(DEBUG, 获取服务器发回数据失败!\n);continue;}}}}~TcpClient(){if(_sock 0){close(_sock);}}private:std::string _ip;uint16_t _port;int _sock; }; tcp_client.cc文件客户端源文件 #include tcp_server.hpp #include memoryvoid Usage(const char *proc) {std::cout \nServer Usage# proc ServerPort\n std::endl; }int main(int argc, const char **argv) {if(argc ! 2){Usage(argv[0]);exit(1);}std::unique_ptrTcpServer ptr_srv(new TcpServer(atoi(argv[1])));ptr_srv-init();ptr_srv-start();return 0; }七. 总结 在网络中每一台主机都有一个独立的ip地址每台主机上的进程都可以对应与一个或多个端口号但是一个端口号不能够对应多个进程。网络通信本质上是网络中两台主机上的相应进程之间的进程间通信通过 IP Port可以标识全网中唯一的一个进程。主机字节序有大端和小端之分网络数据统一采用大端字节序进行网络通信时必须调用相应的接口函数来实现网络格式和主机格式之间的转换。sockaddr结构体可用于网络本地通信表示套接字struct sockaddr_in专门用于网络通信其成员包括网络协议族AF_INET、AF_INET6、AF_LOCAL等、主机ip以及端口号用于socket编程的函数接受的参数均为struct sockaddr类型。UDP协议全称用户数据报协议使用UDP协议通信不需要通信双方建立连接通信不可靠可能出现丢包、失帧等问题但是UDP协议通信的成本较低因此依旧存在广泛的应用。TCP协议全称传输控制协议通信双方需要再建立好连接之后才可以通信通信过程可靠不会出现丢包等问题但是可靠通信的成本也相对较高。
http://www.hkea.cn/news/14443800/

相关文章:

  • 深圳建设银行分行网站商丘网吧什么时候恢复营业
  • 哔哩网站开发需求分析模板wordpress 开发者
  • 响应式 网站 开发wordpress 用户密码加密
  • 江苏省建设局网站大连建网站电话
  • 电影网站标题怎么做流量多济南资海网站建设公司
  • 网站备案登录o2o平台网站建设
  • 网站被收录要怎么做网页设计与网站建设全攻略pdf
  • 交换友情链接的网站标准是什么江苏设计网站电话
  • 山西cms建站系统价格如何申请域名建网站
  • 网站建设 配资信贷员在哪个网站做推广
  • 电子商务网站建设中的重要性wordpress 美拍插件
  • 多语言 网站源码小型网站开发
  • 专门做布料的网站百度竞价查询
  • 公司网站应该包括哪些内容网站会员注册系统下载
  • wordpress获取地址栏参数谷歌广告优化
  • 网站的详细设计建设银行投诉网站
  • 网站建设销售需要哪些重庆整合营销网站建设
  • 达孜网站建设在线制图生成器
  • 某网络公司网站源码 蓝色建站企业网站源码北京网站优化经理
  • 网站域名注册服务商图片下载+wordpress
  • 张家港建设银行网站电子工程网络信息技术专业
  • 国际网站开发客户企业常见问题及解决方案
  • 如何找百度做网站个人简历 网站开发
  • 松江企业网站建设网站开发软件有哪
  • 自己可以做门户网站吗做做网站下载免费
  • 桐城做淘宝店铺网站公司搜搜网站提交
  • 怎么增加网站访问量不同网站相似的页面百度不收录吗
  • seo优化网站网页教学WordPress修改前端
  • 精品资源共享课网站建设长春网络哪个好
  • 广东中山网站建设 光龙网站加强队伍建设