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

app网站开发工具下载漳州网站建设到博大赞

app网站开发工具下载,漳州网站建设到博大赞,做自媒体好还是网站好,网站建设与维护ppt模板下载目录 端口号 源端口号和目的端口号 认识TCP协议和UDP协议 网络字节序 socket编程接口 socket常见接口 sockaddr结构 UDP socket bind recvfrom sendto 编写客户端 绑定INADDR_ANY 实现聊天功能 端口号 在这之前我们已经说过源IP地址和目的IP地址#xff0c;还有…目录 端口号 源端口号和目的端口号 认识TCP协议和UDP协议 网络字节序 socket编程接口 socket常见接口 sockaddr结构 UDP socket bind recvfrom sendto 编写客户端 绑定INADDR_ANY 实现聊天功能 端口号 在这之前我们已经说过源IP地址和目的IP地址还有源MAC地址和目的MAC地址。接下来我们再来谈一谈什么是端口号。 源端口号和目的端口号 首先我们要知道的是IP地址标定了主机的唯一性当我们在打游戏或者刷视频的时候一定是通过手机或者电脑里面一定会有对应的App这叫做客户端软件运行起来后就是客户端进程我们通过请求服务端进程来执行某种功能通过TCP/IP协议把数据送到对方的主机但是这不是真正的网络通信的过程它本质上是在进行进程间通信将数据在主机间转发只是过程所以机器收到后需要将数据交付给指定的进程。         端口号port就是标识特定主机上进程的唯一性他是一个2字节16位的整数。但是之前我们说过进程pid也是标识进程的唯一性那么为什么不用pid代替端口号呢原因是进程是进程端口号是端口号这两个概念本来就没有什么关系我们不希望让两个没有关系的概念揉在一起。         所以在网络上两台主机要进行通信就要有源IP地址和源端口号还要有目的IP和目的端口号通过IP地址和端口号就可以标识网络中唯一一个进程我们把他们就叫做套接字。 认识TCP协议和UDP协议 在应用层下就是传输层传输层使用的是由操作系统提供的系统调用接口而传输层最典型的两种协议就是TCP和UDP协议。 UDP协议         UDP协议叫做用户数据报协议User Datagram Pool它无需建立连接而且不可靠面向数据报的传输层协议可能会出现丢包、数据包乱序的问题。 TCP协议         TCP协议叫做传输控制协议Transmission Control Protocol它是一种可连接、可靠的、面向字节流的传输层通信协议。         看着UDP是不是没有TCP可靠但是在现在的主流网络丢包的问题概率并不大有些是可以容忍的。可不可靠只是他们两个的一个特点不可靠不会为了让网络通信变得可靠而做过多的工作可靠性是需要大量的编码和数据处理的TCP为了保证可靠性一定要设计更多的策略这些都要用户自己去完成虽然保证了可靠性但是它就会更复杂维护成本也更高。 网络字节序 在计算机中是有大小端的概念的大端就是高字节内容放在低地址处低字节内容放在高地址处小端就是高字节内容放在高地址处低字节内容放在低地址处。         如果编写的程序在本地上是不需要考虑这些问题的如果是网络通信就不确定对方的主机是哪种存储方式。         由于我们不能保证通信双方存储数据的方式是一样的所以就规定网络当中传输的数据必须采用大端字节序无论是大端机还是小端机都必须转换成大端。         所以小端机器发送时要将数据转换成大端接受数据时再把数据转换成小端。大端机器就不需要考虑这些。 为了解决这个问题也有了这些接口 h代表host主机的意思n代表network网络的意思l 代表long32位的意思s代表short16位的意思 所以上面这些接口无非就是主机转网络或者是网络转主机。 socket编程接口 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); sockaddr结构 套接字不仅支持网络的进程间通信还支持本地的进程间通信域间套接字。在进行网络通信时我们需要传递IP地址和端口号而本地通信则不需要因此套接字提供了sockaddr_in结构体和sockaddr_un结构体其中sockaddr_in结构体是用于跨网络通信的而sockaddr_un结构体是用于本地通信的。         因为设计师不想设计不同的结构对应不同的通信方式于是就出现了sockaddr结构体他们三个虽然结构不同但是三个结构体前16位比特位都是一样的这叫做套接字的域或者叫协议家族之后我们也是要填充这个字段的。 在传参时统一传的都是sockaddr结构体。通过前16个比特位确定通信方式 UDP 作为一个服务器那就一定要有服务端和用户端我们就先来写一下服务端。 socket 要进程网络通信第一步就要创建socket套接字。 参数 domain创建套接字的域或者叫协议家族网络通信就设置为AF_INETIPV6折纸为AF_INET6本地通信就设置为AF_UNIX这其实是一个宏。type创建套接字的服务类型字节流SOCK_STREAM或数据报SOCK_DGRAM。protocol创建套接字的协议类别可以指明值TCP或UDP。一般只设置为0。 返回值成功返回一个文件描述符失败返回-1错误码被设置。 _sock socket(AF_INET, SOCK_DGRAM, 0);if (_sock 0){logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}bind 作用将ip和port在内核中和当前进程强关联。 参数 sockfd套接字文件描述符addr我们要填充的服务端网络属性信息包括协议家族、端口号、IP地址因为我们使用的是sockaddr_in所以最后要取地址强转为struct sockaddr*addrlenaddr结构体的长度 返回值绑定成功返回0失败返回-1错误码被设置。         套接字文件描述符我们是有的addrlen可以使用sizeof那就剩addr了除了memset可以初始化bzero也是可以初始化的这些接口也多用一下。 struct sockaddr_in local就是我们要填充的字段。 三个参数看着也很熟悉 sin_family这是域或者协议家族。sin_port这是端口号16位整数。sin_addr这是IP地址32位整数。 typedef unsigned short int sa_family_t; #define __SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##familytypedef uint16_t in_port_t;typedef uint32_t in_addr_t; struct in_addr {in_addr_t s_addr; };struct sockaddr_in {__SOCKADDR_COMMON (sin_); // 协议家族in_port_t sin_port; // 16位端口号struct in_addr sin_addr; // 32位IP地址// ... };未来要把数据发送给服务端也要把自己的IP和端口号发送到网络所以一定要改变网络字节序。不过我们要注意的是IP地址通常是以字符串形式出现的所以还要再做一下处理使用inet_addr函数就可以把IP地址从char*类型转换成网络序列的32位整数。 至此我们初始化的工作就做好了我们把它封装成一个类中的成员函数。 class UdpServer { public:UdpServer(uint16_t port, std::string ip 0.0.0.0):_port(port),_ip(ip),_sock(-1){} bool initServer(){// 1. 创建套接字_sock socket(AF_INET, SOCK_DGRAM, 0);if (_sock 0){// 这就是使用到我们进程池写的日志文件了logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}// 2. bind绑定将ip和port在内核中和当前进程强关联struct sockaddr_in local;bzero(local, sizeof(local));local.sin_family AF_INET;// 主机到网络的转换port是16位的使用host to net shortlocal.sin_port htons(_port);// 把IP地址从点分十进制字符串变成4字节32位整数再变成网络序列local.sin_addr.s_addr inet_addr(_ip.c_str());if (bind(_sock, (struct sockaddr*)local, sizeof local) 0){logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}logMessage(NORMAL, %s, init udp server done ... , strerror(errno));}private:// 服务器必须要有ip地址和端口号uint16_t _port; // 192.168.16.1std::string _ip;int _sock; }; recvfrom         这个时候我们的服务器就已经可以启动了UDP的初始化只需要创建套接字加绑定就可以了服务器就是一直为用户提供某种服务所以服务器运行起来后就永远不会退出实际上执行的就是一个死循环代码。         由于UDP服务器是不面向连接也不需要其他操作而UDP服务器读取客户端数据的函数就是这个。 参数 sockfd套接字文件描述符buf读取的数据要放到的缓冲区len要读取的字节数flags读取的方式一般设置为0表示阻塞式读取src_addr输出型参数对端网络的属性addrlen调用时传入的src_addr结构体的长度返回实际读取到的实际长度这是一个输入输出型参数 返回值读取成功返回实际读取的字节数读取失败返回-1错误码被设置。         因为UDP不面向连接所以一定要获取对方的网络信息如IP和端口号recvfrom函数提供的参数是struct sockaddr*类型的所以一定要取地址强转。 使用inet_ntoa函数就可以把struct in_addr中的IP从32位整数的网络序列转化为本机的char*类型。 // 作为一款服务器来说一定是永远不退出的 // 所以该进程一定是一个常驻进程永远在内存中存在。除非进程终止了#define SIZE 1024char buffer[SIZE]; for (;;) {struct sockaddr_in peer; // 对端网络的属性信息bzero(peer, sizeof(peer)); // 初始化socklen_t len sizeof(peer);// 读取数据ssize_t s recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)peer, len);if (s 0){buffer[s] 0; // 传过来的数据// 1.输出信息// 2.是谁发的uint16_t cli_port ntohs(peer.sin_port); // 从网络中来的数据要转换成本机字节序std::string cli_ip inet_ntoa(peer.sin_addr); // 4字节的32位整数IP网络序列-本主机的字符串点分十进制IPprintf([%s:%d]# %s\n, cli_ip, cli_port, buffer);}// ... } sendto UDP服务器返回给客户端数据的函数 参数 sockfd套接字文件描述符buffer要把那个缓冲区中的数据返回对端len缓冲区字符的个数flags一般为0表示阻塞写入dest_addr对端网络相关的属性信息addrlendest_addr结构体的长度 返回值成功返回实际写入的字节数写入失败返回-1错误码被设置。 // udp_server.hpp#include iostream #include string#include unistd.h #include cerrno #include cstring #include cstdio #include cstdio#include sys/types.h #include sys/socket.h #include netinet/in.h #include arpa/inet.h#include log.hpp#define SIZE 1024class UdpServer { public:UdpServer(uint16_t port, std::string ip 0.0.0.0):_port(port),_ip(ip),_sock(-1){} bool initServer(){// 1. 创建套接字_sock socket(AF_INET, SOCK_DGRAM, 0);if (_sock 0){logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}// 2. bind绑定将ip和port在内核中和当前进程强关联struct sockaddr_in local;bzero(local, sizeof(local));local.sin_family AF_INET;// 主机到网络的转换port是16位的使用host to net shortlocal.sin_port htons(_port);// 把IP地址从点分十进制字符串变成4字节32位整数再变成网络序列local.sin_addr.s_addr inet_addr(_ip.c_str());if (bind(_sock, (struct sockaddr*)local, sizeof local) 0){logMessage(FATAL, %d:%s, errno, strerror(errno));exit(2);}logMessage(NORMAL, init udp server done ... %s, strerror(errno));return true;}void Start(){// 作为一款服务器来说一定是永远不退出的// 所以该进程一定是一个常驻进程永远在内存中存在。除非进程终止了char buffer[SIZE];for (;;){struct sockaddr_in peer; // 对端网络的属性信息bzero(peer, sizeof(peer)); // 初始化socklen_t len sizeof(peer);// 读取数据ssize_t s recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)peer, len);if (s 0){buffer[s] 0; // 传过来的数据// 1.输出信息// 2.是谁发的uint16_t cli_port ntohs(peer.sin_port); // 从网络中来的数据要转换成本机字节序std::string cli_ip inet_ntoa(peer.sin_addr); // 4字节的32位整数IP网络序列-本主机的字符串点分十进制IPprintf([%s:%d]# %s\n, cli_ip.c_str(), cli_port, buffer);}// 分析数据// 写回数据sendto(_sock, buffer, strlen(buffer), 0, (struct sockaddr*)peer, len);}}~UdpServer(){if (_sock 0) close(_sock);}private:// 服务器必须要有ip地址和端口号uint16_t _port; // 192.168.16.1std::string _ip;int _sock; }; 至此我们的UDP服务端就已经写好了封装了一下服务端。 编写客户端 // udp_client.cc#include iostream #include string#include cstring #include cstdio#include sys/types.h #include sys/socket.h #include arpa/inet.h #include netinet/in.h// .udp_client server_ip server_port static void usage(std::string proc) {std::cout \nUsage: proc ip port\n std::endl; }int main(int argc, char* argv[]) {if (argc ! 3){usage(argv[0]);exit(1);}// 1.创建套接字int sock socket(AF_INET, SOCK_DGRAM, 0);if (sock 0){std::cerr socket error std::endl;exit(2);}// client不需要主动bind这是一个客户端普通用户不知道如果程序员写了bind那么一定bind了一个固定的端口号// 如果这个端口号被其他进程占用了呢所以就让OS自动随机选择端口号std::string message;struct sockaddr_in server;memset(server, 0, sizeof(server));server.sin_family AF_INET;server.sin_port htons(atoi(argv[2]));server.sin_addr.s_addr inet_addr(argv[1]);char buffer[1024];while (true){std::cout 请输入:;std::getline(std::cin, message);// 当client首次发送消息给服务器的时候OS会自动给client bind他的IP和PORTsendto(sock, message.c_str(), message.size(), 0, (struct sockaddr*)server, sizeof(server));// 读取struct sockaddr_in temp;socklen_t len sizeof(temp);ssize_t s recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr*)temp, len);if (s 0){std::cout Server echo# buffer std::endl;}}return 0; }         首先就来说一下客户端绑定的问题网络通信中IP地址和端口号一定要的服务器是给别人提供服务的所以就必须要告诉别人服务器的IP和端口号。因为服务器是一个死循环的进程选定的端口号不能轻易改变所以服务端必须要bind。客户端不需要绑定的原因就是他在访问服务器的时候只需要确定此时的端口号唯一如果他绑定了那么客户端就只能通过这个端口号访问服务器所以写客户端的时候就不要bind当调用sendto的时候操作系统会自动给当前客户端获取一个唯一端口号。         写好了之后就可以启动服务端和客户端了。 // udp_server.cc#include udp_server.hpp #include memory #include cstdlib// .udp_server ip port void usage(std::string proc) {std::cout \nUsage: proc ip port\n std::endl; }int main(int argc, char* argv[]) {if (argc ! 3){usage(argv[0]);exit(1);}std::string ip argv[1];uint16_t port atoi(argv[2]);std::unique_ptrUdpServer svr(new UdpServer(port, ip));svr-initServer();svr-Start();return 0; }         我们通过netstat命令查看网络状态netstat的选项 -n直接使用IP地址而不通过域名服务器。-l 显示监控中的服务器的Socket。-t 显示TCP传输协议的连线状况。-u显示UDP传输协议的连线状况。-p显示正在使用Socket的程序识别码和程序名称。         我们的服务端流程通过设置本机IP和固定端口号初始化UdpServer创建套接字再bind将IP和port进行强关联编辑本机的sockaddr运行服务器recvfrom读取数据接受客户端的sockaddr信息处理过后再调用sendto给客户端发送回去。         客户端流程设置要访问的服务端IP和端口号编辑服务端sockaddr再调用sendto向服务端发送信息最后读取服务端的信息。         使用netstat -nlup查看当前网络信息。 绑定INADDR_ANY 客户端和服务端在本机中已经可以实现通信了如果想要在网络中通过客户端访问服务端绑定自己的公网IP但是我现在用的是一个云服务器它的IP地址并不是真正的公网IP所以不能被绑定让外网访问就要绑定0系统中提供了一个宏就是INADDR_ANY它的值就是0。         一个服务器中可能放了多张网卡那么就会有多个IP地址但是端口号只有一个服务器在接收数据的时候多张网卡都收到了数据如果bind的时候是指定IP地址的那就只能从这个IP地址中接收数据如果bind的是INADDR_ANY只要是发送给指定端口号的就可以从不同的IP地址接收交给服务端。 // 2. bind绑定将ip和port在内核中和当前进程强关联 struct sockaddr_in local; bzero(local, sizeof(local)); local.sin_family AF_INET; // 主机到网络的转换port是16位的使用host to net short local.sin_port htons(_port); // 把IP地址从点分十进制字符串变成4字节32位整数再变成网络序列 local.sin_addr.s_addr _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); 实现聊天功能 我们现在想要实现一个聊天功能一个客户端向服务端发送信息服务端收到信息并把这个用户对应的信息保存起来再把消息从服务端发送回客户端此时我们再来一个用户一样的操作这时候两个客户端应该可以看到两个人发送的信息。 // 添加一个成员变量 std::unordered_mapstd::string, struct sockaddr_in _users; // IP-PORT : sockaddr_in// 修改一下Start成员函数char buffer[SIZE]; char key[64]; // 将ip-port写到key中 for (;;) {// ...ssize_t s recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)peer, len);if (s 0){// ...snprintf(key, sizeof(key), %s-%u, cli_ip.c_str(), cli_port);logMessage(NORMAL, key: %s, key);auto it _users.find(key); // 把信息写到map中if (it _users.end()){logMessage(NORMAL, add new user: %s, key);_users.insert({key, peer});}}// 写回数据for (auto iter : _users){std::string sendMessage key;sendMessage # ;sendMessage buffer; logMessage(NORMAL, push message to %s, iter.first.c_str());sendto(_sock, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr*)iter.second, sizeof(iter.second));} }         想法很美好但是现实往往和想象中的不一样一开始还行后面打印的都是什么东西原因就是IO被阻塞了就是当我们getline拿用户输入的数据的时候后面的sendto和recvfrom都不会执行所以现在就可以使用多线程一个线程发数据另一个线程负责收数据。         这就有一个要注意的点不管是读数据还是写数据都要用sock如果使用多线程就要把sock设置成全局的或者再把客户端封装成一个类成员变量对于整个类也是全局的。那会不会有线程安全的问题呢这也是没有的因为在多线程之前就要创建出socket而线程只是用这个socket并不会修改它所以可以并发访问。         我们再把之前已经封装好的线程拿过来这样sock会直接传入ThreadData中所以也就不需要把sock定义成全局的。 #include thread.hpp// port、ip uint16_t serverport 0; std::string serverip;// .udp_client ip port static void usage(std::string proc) {std::cout \nUsage: proc ip port\n std::endl; }static void *udpSend(void *args) {// 拿到sockint sock *(int *)((ThreadData *)args)-args_;std::string name ((ThreadData *)args)-name_;// 填充服务端信息std::string message;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::cerr 请输入:;std::getline(std::cin, message);if (message quit)break;sendto(sock, message.c_str(), message.size(), 0, (struct sockaddr *)server, sizeof(server));}return nullptr; }static void *udpRecv(void *args) {int sock *(int *)((ThreadData *)args)-args_;std::string name ((ThreadData *)args)-name_;char buffer[1024];while (true){struct sockaddr_in temp;socklen_t len sizeof(temp);ssize_t s recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)temp, len);if (s 0){buffer[s] 0;std::cout buffer std::endl;}}return nullptr; }int main(int argc, char *argv[]) {if (argc ! 3){usage(argv[0]);exit(1);}// 1.创建套接字int sock socket(AF_INET, SOCK_DGRAM, 0);if (sock 0){std::cerr socket error std::endl;exit(2);}serverport atoi(argv[2]);serverip argv[1];std::unique_ptrThread sender(new Thread(1, udpSend, (void *)sock)); // 发送线程std::unique_ptrThread recver(new Thread(2, udpRecv, (void *)sock)); // 接收线程sender-start();recver-start();sender-join();recver-join();close(sock);return 0; }         至此多线程就写好了虽然用的socket都是同一个但是没有读写冲突的情况因为UDP是全双工的可以同时进行收和发不会受到干扰。         我们在目录下使用mkfifo创建两个管道文件client1和client2将客户端输出重定向到管道文件再使用cat输出重定向这就好像类似于一个输入框和一个显示框。
http://www.dnsts.com.cn/news/180121.html

相关文章:

  • 网站虚拟空间过期一个用vue做的网站
  • 不关站备案wordpress学影视后期大概多少钱
  • 做网站百度百科网站单页面怎么做
  • 珠海网站建设优化网站模版 源码
  • 网站被墙怎么办擦彩网站开发
  • 海南北京网站建设上海松江做网站多少钱
  • 做网站英文怎么写网页设计师的发展路径
  • 大型公司网站制作云县网站建设 云县网
  • 天津自助建站软件百度网站改版工具
  • wordpress影视主题下载失败贺州seo
  • 不用下载就能看的网站的浏览器wordpress 近期文章
  • 高端品牌网站建设公司哪家好在线制作h5网页
  • 给我一个网站贴吧电商网络营销是干什么的
  • 竞价网站做推广方案漳州市建设网站
  • 什么网站都可以进入的浏览器企业信息系统查询系统
  • 中英企业网站模板成全视频在线观看高清版
  • 怎么样检查网站有没有做全站301商标设计网图大全
  • 企业组织网站建设方案大数据网站建设费用
  • html5 网站平台好看云在线网站模板下载 迅雷下载 迅雷下载地址
  • 为什么用html5做网站近期国内新闻
  • 跳转网站怎么做青岛官网seo价格
  • 官方网站是什么公司官网在哪找
  • 设计logo网站免费下载网建服务
  • 电子商务网站建设前期潍坊专业网站建设怎么收费
  • 做招聘网站做服务器多少钱施工企业自营率怎么算
  • 怎样给一个网站做专题策划电子元器件商城网站建设
  • 霸州市建设局网站网站开发的风险
  • 八字排盘网站建设加盟平台
  • 荆州建设局网站打开一个网站
  • 四川省建设工程造价信息网站做网站的电脑需要什么配置