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

标准百度网站建设网站实名认证资料

标准百度网站建设,网站实名认证资料,山西响应式网页建设报价,wordpress防止爆破作者#xff1a;小萌新 专栏#xff1a;网络 作者简介#xff1a;大二学生 希望能和大家一起进步 本篇博客简介#xff1a;简单介绍网络的基础概念 网络编程套接字#xff08;一#xff09; 预备知识源ip和目的ip端口号TCP和UDP协议网络中的字节序 socket编程接口socket常… 作者小萌新 专栏网络 作者简介大二学生 希望能和大家一起进步 本篇博客简介简单介绍网络的基础概念 网络编程套接字一 预备知识源ip和目的ip端口号TCP和UDP协议网络中的字节序 socket编程接口socket常见APIsockaddr结构 简单的UDP程序服务端创建套接字服务端绑定字符串IP和整数IP运行服务器客户端创建套接字客户端绑定问题启动客户端本地测试INADDR_ANY简易的回声服务器网络测试 预备知识 源ip和目的ip 在互联网中每一台主机都有唯一标识的公网IP地址 在互联网中数据传输的时候报文中需要加上源ip和目的ip 源ip和目的ip本质上解决的是从哪里来到哪里去的问题 就像是唐僧每次都会说从东土大唐而来去西天取经一样 有了这两个ip数据在传输的时候就知道自己要去哪里 从而知道自己下一条需要跳去哪个mac设备 端口号 我们还是使用西游记的例子来帮助我们理解端口号 唐僧经历九九八十一难走到了西天它的任务就完成了吗 显然不是 他需要从如来佛祖那里拿到经书并且返回大唐给唐太宗交差 我们在计算机的视角来解释上面的行为 数据从A主机到B主机不是目的 目的是要求B主机上的一个进程提供数据处理服务之后返回到A主机 那么我们把时间再往前面倒一点 数据是怎么产生的呢 计算机本身不产生数据 数据是人通过客户端产生的 站在计算机小白的角度 计算机之前的通信就是人与人之间的通信 而站在程序员的角度 计算机之前的通信其实是进程与进程之间的通信 拿我们的抖音客户端进程和服务器进程举例 我们在刷抖音的时候不停的往下刷实际上就是一个数据 这个数据通过网络传输给抖音的服务器 之后服务器给我们返回视频数据到客户端 实际上ip协议只是保证了两台主机之间能够进行通信 而我们要保证通信双方用户能看到数据则需要通过进程来执行 所以我们后面要写的套嵌式编程就是使用编程语言写一个程序来让进程实现各种服务 回到抖音的例子上来 我们在刷抖音的时候有没有可能在使用其他app呢 app在计算机的角度来看是不是就是一个进程啊 那么数据处理完之后从主机B返回到主机A它怎么知道应该返回数据到哪个进程呢 这里端口号的作用就体现出来了 端口号(port)的作用是唯一标识一台机器上的唯一一个进程 而我们之前说过IP地址能够在互联网中唯一标识一台主机 所以说IP PORT是不是就能唯一标识互联网中的唯一一个进程了啊 我们将网络看作是一个大的系统 在整个OS中 所有的上网行为基本都是在这个OS内进行进程间通信 我们将IP地址端口号叫做socket 为什么我们不直接用系统中进程的pid来唯一标识一个进程呢 这个就跟我们有身份证为什么大学还要给我们创建一个学号一样 有两点原因 如果使用身份证 我们就不能一眼看出这个同学是几几年入学 是哪个学院的如果使用身份证 假设国家修改一下身份证格式那么所有大学的学号就得重新编写了 而如果自己使用自己大学的学号则不需要 这是一种解藕行为 ip 和 port之间是什么关系 它们之间是一种相互促进的关系 我们通过ip来找到公网中唯一标识的主机我们通过端口号来唯一确定该主机中的进程 一个端口号可以关联多个进程吗 不可以 因为端口号的作用是可以唯一标识一个进程 关联多个进程的话端口号就失去它的意义了 一个进程可以关联多个端口号吗 可以 只要这些端口号都唯一标识这个进程就行 TCP和UDP协议 网络协议栈是贯穿整个体系结构的 在应用层 操作系统层和驱动层各有一部分 当我们使用系统调用接口实现网络数据通信时 不得不面对的协议层就是传输层 而传输层最典型的两种协议就是TCP协议和UDP协议 TCP协议 TCP协议叫做传输控制协议Transmission Control ProtocolTCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。 TCP协议是面向连接的如果两台主机之间想要进行数据传输那么必须要先建立连接当连接建立成功后才能进行数据传输。其次TCP协议是保证可靠的协议数据在传输过程中如果出现了丢包、乱序等情况TCP协议都有对应的解决方法。 UDP协议 UDP协议叫做用户数据报协议User Datagram ProtocolUDP协议是一种无需建立连接的、不可靠的、面向数据报的传输层通信协议。 使用UDP协议进行通信时无需建立连接如果两台主机之间想要进行数据传输那么直接将数据发送给对端主机就行了但这也就意味着UDP协议是不可靠的数据在传输过程中如果出现了丢包、乱序等情况UDP协议本身是不知道的。 TCP和UDP协议 TCP协议是一种可靠的传输协议 使用TCP协议能够在一定程度上保证数据传输时的可靠性 而UDP协议是一种不可靠的传输协议 拿现实中的例子来讲 你是一个学校的辅导员你经常叫两个学生来帮你办事 一个叫做张三 一个叫做李四 张三办事比较认真 你交代的任务他每隔一段时间就会给你回信说办的怎么样了 而李四办事比较马虎 你交代的任务他答应了之后文件也不拿就在旁边看着 你也不知道他什么时候会去送 这时候有的同学可能会想 既然张三办事比较认真那么我们为什么不一直用张三呢 这个时候我们要知道 认真是有代价的 由于张三办事比较认真所有说他使用的资源也会比较多 所以说如果没有特殊要求这个文件百分之一百要送到 我们一般情况下都是用李四 反应到TCP/UDP协议上 如果像是银行转账这种必须要保证数据可靠性的传输 我们选择使用TCP协议 否则为了减少成本考虑 我们也可以使用UDP协议 网络中的字节序 在网络中是有大小端之分的 大端法数据的高字节保存在内存的低地址中小端法数据的低字节保存在内存的低地址中 具体的内容可以参考我的这篇博客 大端法和小端法 如果我们编写的程序代码都在本地运行我们是不用考虑大小端的问题的 因为同一台机器上采用的储存方式肯定是一致的 但是如果涉及到网络通信 我们就必须要考虑大小端的问题了 因为在互联网中一些机器使用的是大端法而一些机器使用的是小端法 如果我们不采取一些措施就会造成数据错乱的问题 比如说我们发出去的数据可能是0X11223344 但是由于对面主机的端口法和我们不一样数据就变成了 0X44332211 因此我们变制定了一个规则 不管你的机器是大端还是小端 在传入数据的时候统一转化为大端的数据 有了这个统一的规则之后发送方接收方就都明确了发送接受是大端的数据也就不会出现数据错乱的情况了 为什么网络字节序采用的是大端而不是小端 网上关于这个的说法有很多 其中比较可信的有两点 TCP在Unix时代就有了以前Unix机器都是大端机因此网络字节序也就采用的是大端但之后人们发现用小端能简化硬件设计所以现在主流的都是小端机但协议已经不好改了。大端序更符合现代人的读写习惯。 socket编程接口 socket常见API 创建套接字TCP/UDP客户端服务器 int socket(int domain, int type, int protocol);绑定端口号TCP/UDP服务器 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);监听套接字TCP服务器 int listen(int sockfd, int backlog);接收请求TCP服务器 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);建立连接TCP客户端 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockaddr结构 sockaddr结构的出现 套接字不仅支持进程间通信而且支持网络间通信 在跨网络通信的时候我们需要传入IP和端口号 而本地通信则不需要 所以说因此套接字提供了sockaddr_in结构体和sockaddr_un结构体 为了让套接字的网络通信和本地通信能够使用同一套函数接口 于是就出现了sockeaddr结构体 该结构体与sockaddr_in和sockaddr_un的结构都不相同 但这三个结构体头部的16个比特位都是一样的 这个字段叫做协议家族 此时当我们在传递在传参时 就不用传入sockeaddr_in或sockeaddr_un这样的结构体 而统一传入sockeaddr这样的结构体 在设置参数时就可以通过设置协议家族这个字段 来表明我们是要进行网络通信还是本地通信 在这些API内部就可以提sockeaddr结构头部的16位进行识别 进而得出我们是要进行网络通信还是本地通信 然后执行对应的操作 此时我们就通过通用sockaddr结构 将套接字网络通信和本地通信的参数类型进行了统一 注意 实际我们在进行网络通信时 定义的还是sockaddr_in这样的结构体 只不过在传参时需要将该结构体的地址类型进行强转为sockaddr* 为什么不用void* 代替struct sockaddr*类型 如果在C中我们可以使用多态来解决这个问题 但是C语言中没有没有办法能够解决这个问题呢 答案是有的 就是使用void* 只要参数统一传入void* 之后在接口内部来判断是哪个类型就可以了 但是由于在设计这一套接口的时候c语言还不支持void* 所以说出现了这一套的接口 而由于设计的这一套接口是系统接口 牵一发而动全身 所以说在设计出了c语言的void*之后依旧没有改回来 简单的UDP程序 服务端创建套接字 我们把服务器封装成一个类当我们定义出一个服务器对象后需要马上初始化服务器而初始化服务器需要做的第一件事就是创建套接字。 socket函数 创建套接字的函数叫做socket该函数的函数原型如下 int socket(int domain, int type, int protocol);返回值说明 套接字创建成功返回一个文件描述符创建失败返回-1同时错误码会被设置。 参数说明 domain创建套接字的域或者叫做协议家族也就是创建套接字的类型。该参数就相当于struct sockaddr结构的前16个位。如果是本地通信就设置为AF_UNIX如果是网络通信就设置为AF_INETIPv4或AF_INET6IPv6。type创建套接字时所需的服务类型。其中最常见的服务类型是SOCK_STREAM和SOCK_DGRAM如果是基于UDP的网络通信我们采用的就是SOCK_DGRAM叫做用户数据报服务如果是基于TCP的网络通信我们采用的就是SOCK_STREAM叫做流式套接字提供的是流式服务。protocol创建套接字的协议类别。你可以指明为TCP或UDP但该字段一般直接设置为0就可以了设置为0表示的就是默认此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。 socket函数属于什么类型的接口 网络协议栈是分层的按照TCP/IP四层模型来说自顶向下依次是应用层、传输层、网络层和数据链路层。而我们现在所写的代码都叫做用户级代码也就是说我们是在应用层编写代码因此我们调用的实际是下三层的接口而传输层和网络层都是在操作系统内完成的也就意味着我们在应用层调用的接口都叫做系统调用接口。 socket函数是被谁调用的 socket这个函数是被程序调用的但并不是被程序在编码上直接调用的而是程序编码形成的可执行程序运行起来变成进程当这个进程被CPU调度执行到socket函数时然后才会执行创建套接字的代码也就是说socket函数是被进程所调用的。 socket函数底层做了什么 socket函数是被进程所调用的而每一个进程在系统层面上都有一个进程地址空间PCBtask_struct、文件描述符表files_struct以及对应打开的各种文件。而文件描述符表里面包含了一个数组fd_array其中数组中的0、1、2下标依次对应的就是标准输入、标准输出以及标准错误。 当我们调用socket函数创建套接字时实际相当于我们打开了一个“网络文件”打开后在内核层面上就形成了一个对应的struct file结构体同时该结构体被连入到了该进程对应的文件双链表并将该结构体的首地址填入到了fd_array数组当中下标为3的位置此时fd_array数组中下标为3的指针就指向了这个打开的“网络文件”最后3号文件描述符作为socket函数的返回值返回给了用户。 其中每一个struct file结构体中包含的就是对应打开文件各种信息比如文件的属性信息、操作方法以及文件缓冲区等。其中文件对应的属性在内核当中是由struct inode结构体来维护的而文件对应的操作方法实际就是一堆的函数指针比如read和write在内核当中就是由struct file_operations结构体来维护的。而文件缓冲区对于打开的普通文件来说对应的一般是磁盘但对于现在打开的“网络文件”来说这里的文件缓冲区对应的就是网卡。 对于一般的普通文件来说当用户通过文件描述符将数据写到文件缓冲区然后再把数据刷到磁盘上就完成了数据的写入操作。而对于现在socket函数打开的“网络文件”来说当用户将数据写到文件缓冲区后操作系统会定期将数据刷到网卡里面而网卡则是负责数据发送的因此数据最终就发送到了网络当中。 服务端创建套接字 当我们在进行初始化服务器创建套接字时就是调用socket函数创建套接字创建套接字时我们需要填入的协议家族就是AF_INET因为我们要进行的是网络通信而我们需要的服务类型就是SOCK_DGRAM因为我们现在编写的UDP服务器是面向数据报的而第三个参数之间设置为0即可。 代码标识如下 class UdpServer { public:bool InitServer(){//创建套接字_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){ //创建套接字失败std::cerr socket error std::endl;return false;}std::cout socket create success, sockfd: _sockfd std::endl;return true;}~UdpServer(){if (_sockfd 0){close(_sockfd);}}; private:int _sockfd; //文件描述符 }; 注意 当析构服务器时我们可以将sockfd对应的文件进行关闭但实际上不进行该操作也行因为一般服务器运行后是就不会停下来的。 我们下面可以做一个简单的测试 来看看是否正确运行 int main(){auto* ser new UdpServer();ser-InitServer();sleep(10);return 0;}运行结果如下 我们发现这里文件描述符是3 因为012标准输入 标准输出 标准错误在我们的程序创建一瞬间就占用了 服务端绑定 现在套接字已经创建成功了但作为一款服务器来讲如果只是把套接字创建好了那我们也只是在系统层面上打开了一个文件操作系统将来并不知道是要将数据写入到磁盘还是刷到网卡此时该文件还没有与网络关联起来。 由于现在编写的是不面向连接的UDP服务器所以初始化服务器要做的第二件事就是绑定。 bind函数 绑定的函数叫做bind该函数的函数原型如下 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);返回值说明 绑定成功返回0绑定失败返回-1同时错误码会被设置。 参数说明 sockfd绑定的文件的文件描述符。也就是我们创建套接字时获取到的文件描述符。addr网络相关的属性信息包括协议家族、IP地址、端口号等。addrlen传入的addr结构体的长度。 struct sockaddr_in结构体 在绑定时需要将网络相关的属性信息填充到一个结构体当中然后将该结构体作为bind函数的第二个参数进行传入这实际就是struct sockaddr_in结构体。 struct sockaddr_in当中的成员如下 sin_family表示协议家族。sin_port表示端口号是一个16位的整数。sin_addr表示IP地址是一个32位的整数。 剩下的字段一般不做处理当然你也可以进行初始化。 其中sin_addr的类型是struct in_addr实际该结构体当中就只有一个成员该成员就是一个32位的整数IP地址实际就是存储在这个整数当中的。 如何理解绑定 在进行绑定的时候需要将IP地址和端口号告诉对应的网络文件此时就可以改变网络文件当中文件操作函数的指向将对应的操作函数改为对应网卡的操作方法此时读数据和写数据对应的操作对象就是网卡了所以绑定实际上就是将文件和网络关联起来。 增加IP地址和端口号 由于绑定时需要用到IP地址和端口号因此我们需要在服务器类当中引入IP地址和端口号在创建服务器对象时需要传入对应的IP地址和端口号此时我们就可以根据传入的IP地址和端口号对对应的成员进行初始化。 class UdpServer { public:UdpServer(std::string ip, int port):_sockfd(-1),_port(port),_ip(ip){};~UdpServer(){if (_sockfd 0){close(_sockfd);}}; private:int _sockfd; //文件描述符int _port; //端口号std::string _ip; //IP地址 };虽然这里端口号定义为整型但由于端口号是16位的因此我们实际只会用到它的低16位。 服务端绑定 套接字创建完毕后我们就需要进行绑定了但在绑定之前我们需要先定义一个struct sockaddr_in结构将对应的网络属性信息填充到该结构当中。由于该结构体当中还有部分选填字段因此我们最好在填充之前对该结构体变量里面的内容进行清空然后再将协议家族、端口号、IP地址等信息填充到该结构体变量当中。 需要注意的是在发送到网络之前需要将端口号设置为网络序列由于端口号是16位的因此我们需要使用前面说到的htons函数将端口号转为网络序列。此外由于网络当中传输的是整数IP我们需要调用inet_addr函数将字符串IP转换成整数IP然后再将转换后的整数IP进行设置。 当网络属性信息填充完毕后由于bind函数提供的是通用参数类型因此在传入结构体地址时还需要将struct sockaddr_in*强转为struct sockaddr*类型后再进行传入。 class UdpServer { public:UdpServer(std::string ip, int port):_sockfd(-1),_port(port),_ip(ip){};bool InitServer(){//创建套接字_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){ //创建套接字失败std::cerr socket error std::endl;return false;}std::cout socket create success, sockfd: _sockfd std::endl;//填充网络通信相关信息struct sockaddr_in local;memset(local, \0, sizeof(local));local.sin_family AF_INET;local.sin_port htons(_port);local.sin_addr.s_addr inet_addr(_ip.c_str()); //绑定if (bind(_sockfd, (struct sockaddr*)local, sizeof(sockaddr)) 0){ //绑定失败std::cerr bind error std::endl;return false;}std::cout bind success std::endl;return true;}~UdpServer(){if (_sockfd 0){close(_sockfd);}}; private:int _sockfd; //文件描述符int _port; //端口号std::string _ip; //IP地址 };字符串IP和整数IP IP地址的表现形式有两种 字符串IP类似于192.168.233.123这种字符串形式的IP地址叫做基于字符串的点分十进制IP地址。整数IPIP地址在进行网络传输时所用的形式用一个32位的整数来表示IP地址。 整数IP存在的意义 网络传输数据时是寸土寸金的如果我们在网络传输时直接以基于字符串的点分十进制IP的形式进行IP地址的传送那么此时一个IP地址至少就需要15个字节但实际并不需要耗费这么多字节。 IP地址实际可以划分为四个区域其中每一个区域的取值都是0~255而这个范围的数字只需要用8个比特位就能表示因此我们实际只需要32个比特位就能够表示一个IP地址。其中这个32位的整数的每一个字节对应的就是IP地址中的某个区域我们将IP地址的这种表示方法称之为整数IP此时表示一个IP地址只需要4个字节。 因为采用整数IP的方案表示一个IP地址只需要4个字节并且在网络通信也能表示同样的含义因此在网络通信时就没有用字符串IP而用的是整数IP因为这样能够减少网络通信时数据的传送。 inet_addr函数 实际在进行字符串IP和整数IP的转换时我们不需要自己编写转换逻辑系统已经为我们提供了相应的转换函数我们直接调用即可。 将字符串IP转换成整数IP的函数叫做inet_addr该函数的函数原型如下 in_addr_t inet_addr(const char *cp);该函数使用起来非常简单我们只需传入待转换的字符串IP该函数返回的就是转换后的整数IP。除此之外inet_aton函数也可以将字符串IP转换成整数IP不过该函数使用起来没有inet_addr简单。 inet_ntoa函数 将整数IP转换成字符串IP的函数叫做inet_ntoa该函数的函数原型如下 char *inet_ntoa(struct in_addr in);需要注意的是传入inet_ntoa函数的参数类型是in_addr因此我们在传参时不需要选中in_addr结构当中的32位的成员传入直接传入in_addr结构体即可。 运行服务器 UDP服务器的初始化就只需要创建套接字和绑定就行了当服务器初始化完毕后我们就可以启动服务器了。 服务器实际上就是在周而复始的为我们提供某种服务服务器之所以称为服务器是因为服务器运行起来后就永远不会退出因此服务器实际执行的是一个死循环代码。由于UDP服务器是不面向连接的因此只要UDP服务器启动后就可以直接读取客户端发来的数据。 recvfrom函数 UDP服务器读取数据的函数叫做recvfrom该函数的函数原型如下 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);返回值说明 读取成功返回实际读取到的字节数读取失败返回-1同时错误码会被设置。 参数说明 sockfd对应操作的文件描述符。表示从该文件描述符索引的文件当中读取数据。buf读取数据的存放位置。len期望读取数据的字节数。flags读取的方式。一般设置为0表示阻塞读取。src_addr对端网络相关的属性信息包括协议家族、IP地址、端口号等。addrlen调用时传入期望读取的src_addr结构体的长度返回时代表实际读取到的src_addr结构体的长度这是一个输入输出型参数。 注意 由于UDP是不面向连接的因此我们除了获取到数据以外还需要获取到对端网络相关的属性信息包括IP地址和端口号等。在调用recvfrom读取数据时必须将addrlen设置为你要读取的结构体对应的大小。由于recvfrom函数提供的参数也是struct sockaddr类型的因此我们在传入结构体地址时需要将struct sockaddr_in类型进行强转。 启动服务器函数 现在服务端通过recvfrom函数读取客户端数据我们可以先将读取到的数据当作字符串看待将读取到的数据的最后一个位置设置为’\0’此时我们就可以将读取到的数据进行输出同时我们也可以将获取到的客户端的IP地址和端口号也一并进行输出。 需要注意的是我们获取到的客户端的端口号此时是网络序列我们需要调用ntohs函数将其转为主机序列再进行打印输出。同时我们获取到的客户端的IP地址是整数IP我们需要通过调用inet_ntoa函数将其转为字符串IP再进行打印输出。 class UdpServer { public:void Start(){ #define SIZE 128char buffer[SIZE];for (;;){struct sockaddr_in peer;socklen_t len sizeof(peer);ssize_t size recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)peer, len);if (size 0){buffer[size] \0;int port ntohs(peer.sin_port);std::string ip inet_ntoa(peer.sin_addr);std::cout ip : port # buffer std::endl;}else{std::cerr recvfrom error std::endl;}}} private:int _sockfd; //文件描述符int _port; //端口号std::string _ip; //IP地址 };注意 如果调用recvfrom函数读取数据失败我们可以打印一条提示信息但是不要让服务器退出服务器不能因为读取某一个客户端的数据失败就退出。 引入命令行参数 鉴于构造服务器时需要传入IP地址和端口号我们这里可以引入命令行参数。此时当我们运行服务器时在后面跟上对应的IP地址和端口号即可。 由于云服务器的原因后面实际不需要传入IP地址因此在运行服务器的时候我们只需要传入端口号即可目前我们就手动将IP地址设置为127.0.0.1。IP地址为127.0.0.1实际上等价于localhost表示本地主机我们将它称之为本地环回相当于我们一会先在本地测试一下能否正常通信然后再进行网络通信的测试。 int main(int argc, char* argv[]) {if (argc ! 2){std::cerr Usage: argv[0] port std::endl;return 1;}std::string ip 127.0.0.1; //本地环回int port atoi(argv[1]);UdpServer* svr new UdpServer(ip, port);svr-InitServer();svr-Start();return 0; } 需要注意的是agrv数组里面存储的是字符串而端口号是一个整数因此需要使用atoi函数将字符串转换成整数。然后我们就可以用这个IP地址和端口号来构造服务器了服务器构造完成并初始化后就可以调用Start函数启动服务器了。 此时带上端口号运行程序就可以看到套接字创建成功、绑定成功现在服务器就在等待客户端向它发送数据。 我们可以通过netstat命令来查看当前网络的状态这里我们可以选择携带nlup选项 netstat常用选项说明 -n直接使用IP地址而不通过域名服务器。-l显示监控中的服务器的Socket。-t显示TCP传输协议的连线状况。-u显示UDP传输协议的连线状况。-p显示正在使用Socket的程序识别码和程序名称。 如果我们去掉n选项 此时就由ip地址变为了域名 其中netstat命令显示的信息中Proto表示协议的类型Recv-Q表示网络接收队列Send-Q表示网络发送队列Local Address表示本地地址Foreign Address表示外部地址State表示当前的状态PID表示该进程的进程IDProgram name表示该进程的程序名称。 其中Foreign Address写成0.0.0.0:*表示任意IP地址、任意的端口号的程序都可以访问当前进程。 客户端创建套接字 同样的我们把客户端也封装成一个类当我们定义出一个客户端对象后也是需要对其进行初始化而客户端在初始化时也需要创建套接字之后客户端发送数据或接收数据也就是对这个套接字进行操作。 客户端创建套接字时选择的协议家族也是AF_INET需要的服务类型也是SOCK_DGRAM当客户端被析构时也可以选择关闭对应的套接字。与服务端不同的是客户端在初始化时只需要创建套接字就行了而不需要进行绑定操作。 class UdpClient { public:bool InitClient(){//创建套接字_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){std::cerr socket create error std::endl;return false;}return true;}~UdpClient(){if (_sockfd 0){close(_sockfd);}} private:int _sockfd; //文件描述符 };客户端绑定问题 首先由于是网络通信通信双方都需要找到对方因此服务端和客户端都需要有各自的IP地址和端口号只不过服务端需要进行端口号的绑定而客户端不需要。 因为服务器就是为了给别人提供服务的因此服务器必须要让别人知道自己的IP地址和端口号IP地址一般对应的就是域名而端口号一般没有显示指明过因此服务端的端口号一定要是一个众所周知的端口号并且选定后不能轻易改变否则客户端是无法知道服务端的端口号的这就是服务端要进行绑定的原因只有绑定之后这个端口号才真正属于自己因为一个端口只能被一个进程所绑定服务器绑定一个端口就是为了独占这个端口。 而客户端在通信时虽然也需要端口号但客户端一般是不进行绑定的客户端访问服务端的时候端口号只要是唯一的就行了不需要和特定客户端进程强相关。 如果客户端绑定了某个端口号那么以后这个端口号就只能给这一个客户端使用就是这个客户端没有启动这个端口号也无法分配给别人并且如果这个端口号被别人使用了那么这个客户端就无法启动了。所以客户端的端口只要保证唯一性就行了因此客户端端口可以动态的进行设置并且客户端的端口号不需要我们来设置当我们调用类似于sendto这样的接口时操作系统会自动给当前客户端获取一个唯一的端口号。 也就是说客户端每次启动时使用的端口号可能是变化的此时只要我们的端口号没有被耗尽客户端就永远可以启动。 启动客户端 增加服务端IP地址和端口号 作为一个客户端它必须知道它要访问的服务端的IP地址和端口号因此在客户端类当中需要引入服务端的IP地址和端口号此时我们就可以根据传入的服务端的IP地址和端口号对对应的成员进行初始化。 class UdpClient { public:UdpClient(std::string server_ip, int server_port):_sockfd(-1),_server_port(server_port),_server_ip(server_ip){}~UdpClient(){if (_sockfd 0){close(_sockfd);}} private:int _sockfd; //文件描述符int _server_port; //服务端端口号std::string _server_ip; //服务端IP地址 };当客户端初始化完毕后我们就可以将客户端运行起来由于客户端和服务端在功能上是相互补充的既然服务器是在读取客户端发来的数据那么客户端就应该想服务端发送数据。 sendto函数 UDP客户端发送数据的函数叫做sendto该函数的函数原型如下 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);返回值说明 写入成功返回实际写入的字节数写入失败返回-1同时错误码会被设置。 参数说明 sockfd对应操作的文件描述符。表示将数据写入该文件描述符索引的文件当中。buf待写入数据的存放位置。len期望写入数据的字节数。flags写入的方式。一般设置为0表示阻塞写入。dest_addr对端网络相关的属性信息包括协议家族、IP地址、端口号等。 addrlen传入dest_addr结构体的长度。 注意 由于UDP不是面向连接的因此除了传入待发送的数据以外还需要指明对端网络相关的信息包括IP地址和端口号等。由于sendto函数提供的参数也是struct sockaddr类型的因此我们在传入结构体地址时需要将struct sockaddr_in类型进行强转。 启动客户端函数 现在客户端要发送数据给服务端我们可以让客户端获取用户输入不断将用户输入的数据发送给服务端。 需要注意的是客户端中存储的服务端的端口号此时是主机序列我们需要调用htons函数将其转为网络序列后再设置进struct sockaddr_in结构体。同时客户端中存储的服务端的IP地址是字符串IP我们需要通过调用inet_addr函数将其转为整数IP后再设置进struct sockaddr_in结构体。 class UdpClient { public:void Start(){std::string msg;struct sockaddr_in peer;memset(peer, \0, sizeof(peer));peer.sin_family AF_INET;peer.sin_port htons(_server_port);peer.sin_addr.s_addr inet_addr(_server_ip.c_str());for (;;){std::cout Please Enter# ;getline(std::cin, msg);sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)peer, sizeof(peer));}} private:int _sockfd; //文件描述符int _server_port; //服务端端口号std::string _server_ip; //服务端IP地址 };引入命令行参数 鉴于构造客户端时需要传入对应服务端的IP地址和端口号我们这里也可以引入命令行参数。当我们运行客户端时直接在后面跟上对应服务端的IP地址和端口号即可。 int main(int argc, char* argv[]) {if (argc ! 3){std::cerr Usage: argv[0] server_ip server_port std::endl;return 1;}std::string server_ip argv[1];int server_port atoi(argv[2]);UdpClient* clt new UdpClient(server_ip, server_port);clt-InitClient();clt-Start();return 0; }需要注意的是argv数组里面存储的是字符串而端口号是一个整数因此需要使用atoi函数将字符串转换成整数。然后我们就可以用这个IP地址和端口号来构造客户端了客户端构造完成并初始化后就可以调用Start函数启动客户端了。 本地测试 现在服务端和客户端的代码都已经编写完毕我们可以先进行本地测试此时服务器没有绑定外网绑定的是本地环回。现在我们运行服务器时指明端口号为8081再运行客户端此时客户端要访问的服务器的IP地址就是本地环回127.0.0.1服务端的端口号就是8081。 客户端运行之后提示我们进行输入当我们在客户端输入数据后客户端将数据发送给服务端此时服务端再将收到的数据打印输出这时我们在服务端的窗口也看到我们输入的内容。 INADDR_ANY 现在我们已经通过了本地测试接下来就需要进行网络测试了那是不是直接让服务端绑定我的公网IP此时这个服务端就能够被外网访问了呢 理论上确实是这样的就比如我的服务器的公网IP是43.143.132.22这里用ping命令也是能够ping通的。 但是如果我们将服务端设置的本地环回改为我的公网IP此时当我们重新编译程序再次运行服务端的时候会发现服务端绑定失败。 由于云服务器的IP地址是由对应的云厂商提供的这个IP地址并不一定是真正的公网IP这个IP地址是不能直接被绑定的如果需要让外网访问此时我们需要bind 0。系统当当中提供的一个INADDR_ANY这是一个宏值它对应的值就是0。 因此如果我们需要让外网访问那么在云服务器上进行绑定时就应该绑定INADDR_ANY此时我们的服务器才能够被外网访问。 绑定INADDR_ANY的好处 当一个服务器的带宽足够大时一台机器接收数据的能力就约束了这台机器的IO效率因此一台服务器底层可能装有多张网卡此时这台服务器就可能会有多个IP地址但一台服务器上端口号为8081的服务只有一个。这台服务器在接收数据时这里的多张网卡在底层实际都收到了数据如果这些数据也都想访问端口号为8081的服务。此时如果服务端在绑定的时候是指明绑定的某一个IP地址那么此时服务端在接收数据的时候就只能从绑定IP对应的网卡接收数据。而如果服务端绑定的是INADDR_ANY那么只要是发送给端口号为8081的服务的数据系统都会可以将数据自底向上交给该服务端。 因此服务端绑定INADDR_ANY这种方案也是强烈推荐的方案所有的服务器具体在操作的时候用的也就是这种方案。 当然如果你既想让外网访问你的服务器但你又指向绑定某一个IP地址那么就不能用云服务器此时可以选择使用虚拟机或者你自定义安装的Linux操作系统那个IP地址就是支持你绑定的而云服务器是不支持的。 更改代码 因此如果想要让外网访问我们的服务我们这里就需要将服务器类当中IP地址相关的代码去掉而在填充网络相关信息设置struct sockaddr_in结构体时将设置的IP地址改为INADDR_ANY就行了。由于INADDR_ANY的值本质就是0不存在大小端的问题因此在设置时可以不进行网络字节序的转换。 class UdpServer { public:bool InitServer(){//创建套接字_sockfd socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd 0){ //创建套接字失败std::cerr socket error std::endl;return false;}std::cout socket create success, sockfd: _sockfd std::endl;//填充网络通信相关信息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; //绑定INADDR_ANY//绑定if (bind(_sockfd, (struct sockaddr*)local, sizeof(sockaddr)) 0){ //绑定失败std::cerr bind error std::endl;return false;}std::cout bind success std::endl;return true;} private:int _sockfd; //文件描述符int _port; //端口号std::string _ip; //IP地址 };此时当我们再重新编译运行服务器时就不会绑定失败了并且此时当我们再用netstat命令查看时会发现该服务器的本地IP地址变成了0.0.0.0这就意味着该UDP服务器可以在本地读取任何一张网卡里面的数据。 简易的回声服务器 由于在进行网络测试的时候当客户端发送数据给服务端时服务端会将从客户端收到的数据进行打印因此服务端是能够看到现象的。但客户端一直在向服务端发送数据在客户端这边看不出服务端是否收到了自己发送的数据。 服务端代码编写 鉴于此我们可以将该服务器改成一个简单的回声服务器。当服务端收到客户端发来的数据后除了在服务端进行打印以外服务端可以调用sento函数将收到的数据重新发送给对应的客户端。 需要注意的是服务端在调用sendto函数时需要传入客户端的网络属性信息但服务端现在是知道客户端的网络属性信息的因为服务端在此之前就已经通过recvfrom函数获取到了客户端的网络属性信息。 void Start() { #define SIZE 128char buffer[SIZE];for (;;){struct sockaddr_in peer;socklen_t len sizeof(peer);ssize_t size recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)peer, len);if (size 0){buffer[size] \0;int port ntohs(peer.sin_port);std::string ip inet_ntoa(peer.sin_addr);std::cout ip : port # buffer std::endl;}else{std::cerr recvfrom error std::endl;}std::string echo_msg server get!-;echo_msg buffer;sendto(_sockfd, echo_msg.c_str(), echo_msg.size(), 0, (struct sockaddr*)peer, len);} }客户端代码编写 服务端的代码改了之后对应客户端的代码也得改改。当客户端发完数据给服务端后由于服务端还会将该数据重新发给客户端因此客户端发完数据后还需要调recvfrom来读取服务端发来的响应数据。 在客户端调用recvfrom函数接收服务端发来的响应数据时客户端同时也需要读取服务端与网络相关的各种信息。虽然客户端早已知道服务端的网络信息了此时服务端的网络信息已经不重要了但还是建议不要把参数设置为空这样可能会出问题所以我们还是用一个临时变量将服务端的网络信息读取一下。 而客户端接收到服务端的响应数据后将数据原封不动的打印出来就行了。此时客户端发送给服务端的数据除了在服务端会打印显示以外服务端还会将数据再重新发回给客户端此时客户端也会接收到响应数据然后将该数据进行打印。 void Start() {std::string msg;struct sockaddr_in peer;memset(peer, \0, sizeof(peer));peer.sin_family AF_INET;peer.sin_port htons(_server_port);peer.sin_addr.s_addr inet_addr(_server_ip.c_str());for (;;){std::cout Please Enter# ;getline(std::cin, msg);sendto(_sockfd, msg.c_str(), msg.size(), 0, (struct sockaddr*)peer, sizeof(peer));#define SIZE 128char buffer[SIZE];struct sockaddr_in tmp;socklen_t len sizeof(tmp);ssize_t size recvfrom(_sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)tmp, len);if (size 0){buffer[size] \0;std::cout buffer std::endl;}} }网络测试 静态编译客户端 我们可以将生成的客户端的可执行程序发送给你的其他朋友进行网络级别的测试。为了保证程序在你们的机器是严格一致的可以选择在编译客户端时携带-static选项进行静态编译。 这样子客户端运行的时候就不需要依赖库文件了 分发客户端 此时我们可以先使用sz命令将该客户端可执行程序下载到本地机器然后将该程序发送给你的朋友。而我们分发客户端的过程实际上就是我们在网上下载各种PC端软件的过程我们下软件下的实际上就是客户端的可执行程序而与之对应的服务端就在Linux服务器上部署着。 当你的朋友收到这个客户端的可执行程序后可以通过rz命令或拖拽的方式将这个可执行程序上传到他的云服务器上然后通过chmod命令给该文件加上可执行权限。 进行网络测试 此时你先把你的服务器启动起来然后你的朋友将你的IP地址和端口号作为命令行参数运行客户端就可以访问你的服务器了。
文章转载自:
http://www.morning.pbbzn.cn.gov.cn.pbbzn.cn
http://www.morning.thmlt.cn.gov.cn.thmlt.cn
http://www.morning.kncrc.cn.gov.cn.kncrc.cn
http://www.morning.sdhmn.cn.gov.cn.sdhmn.cn
http://www.morning.wrbnh.cn.gov.cn.wrbnh.cn
http://www.morning.qgqck.cn.gov.cn.qgqck.cn
http://www.morning.yuminfo.com.gov.cn.yuminfo.com
http://www.morning.hrpbq.cn.gov.cn.hrpbq.cn
http://www.morning.nwllb.cn.gov.cn.nwllb.cn
http://www.morning.rxpp.cn.gov.cn.rxpp.cn
http://www.morning.aowuu.com.gov.cn.aowuu.com
http://www.morning.nlqgb.cn.gov.cn.nlqgb.cn
http://www.morning.srbfp.cn.gov.cn.srbfp.cn
http://www.morning.kclkb.cn.gov.cn.kclkb.cn
http://www.morning.dblfl.cn.gov.cn.dblfl.cn
http://www.morning.qxxj.cn.gov.cn.qxxj.cn
http://www.morning.fbzyc.cn.gov.cn.fbzyc.cn
http://www.morning.sfyqs.cn.gov.cn.sfyqs.cn
http://www.morning.bpmdh.cn.gov.cn.bpmdh.cn
http://www.morning.pnmtk.cn.gov.cn.pnmtk.cn
http://www.morning.jtjmz.cn.gov.cn.jtjmz.cn
http://www.morning.rynq.cn.gov.cn.rynq.cn
http://www.morning.htbbp.cn.gov.cn.htbbp.cn
http://www.morning.tlfzp.cn.gov.cn.tlfzp.cn
http://www.morning.lqypx.cn.gov.cn.lqypx.cn
http://www.morning.nbsfb.cn.gov.cn.nbsfb.cn
http://www.morning.bfsqz.cn.gov.cn.bfsqz.cn
http://www.morning.sypzg.cn.gov.cn.sypzg.cn
http://www.morning.yrdn.cn.gov.cn.yrdn.cn
http://www.morning.trzmb.cn.gov.cn.trzmb.cn
http://www.morning.dkmzr.cn.gov.cn.dkmzr.cn
http://www.morning.kwqwp.cn.gov.cn.kwqwp.cn
http://www.morning.kmjbs.cn.gov.cn.kmjbs.cn
http://www.morning.hmfxl.cn.gov.cn.hmfxl.cn
http://www.morning.nxfuke.com.gov.cn.nxfuke.com
http://www.morning.lltdf.cn.gov.cn.lltdf.cn
http://www.morning.jqswf.cn.gov.cn.jqswf.cn
http://www.morning.rfgkf.cn.gov.cn.rfgkf.cn
http://www.morning.ktcfl.cn.gov.cn.ktcfl.cn
http://www.morning.pmdnx.cn.gov.cn.pmdnx.cn
http://www.morning.mkczm.cn.gov.cn.mkczm.cn
http://www.morning.tdscl.cn.gov.cn.tdscl.cn
http://www.morning.qhrlb.cn.gov.cn.qhrlb.cn
http://www.morning.8yitong.com.gov.cn.8yitong.com
http://www.morning.nbqwt.cn.gov.cn.nbqwt.cn
http://www.morning.zrnph.cn.gov.cn.zrnph.cn
http://www.morning.lrmts.cn.gov.cn.lrmts.cn
http://www.morning.hhzdj.cn.gov.cn.hhzdj.cn
http://www.morning.stcds.cn.gov.cn.stcds.cn
http://www.morning.brlcj.cn.gov.cn.brlcj.cn
http://www.morning.nlqmp.cn.gov.cn.nlqmp.cn
http://www.morning.kntsd.cn.gov.cn.kntsd.cn
http://www.morning.qczpf.cn.gov.cn.qczpf.cn
http://www.morning.ityi666.cn.gov.cn.ityi666.cn
http://www.morning.hhpbj.cn.gov.cn.hhpbj.cn
http://www.morning.gbybx.cn.gov.cn.gbybx.cn
http://www.morning.kdgcx.cn.gov.cn.kdgcx.cn
http://www.morning.cwgpl.cn.gov.cn.cwgpl.cn
http://www.morning.ftlgy.cn.gov.cn.ftlgy.cn
http://www.morning.rryny.cn.gov.cn.rryny.cn
http://www.morning.lmmh.cn.gov.cn.lmmh.cn
http://www.morning.fkflc.cn.gov.cn.fkflc.cn
http://www.morning.qyfqx.cn.gov.cn.qyfqx.cn
http://www.morning.pjwrl.cn.gov.cn.pjwrl.cn
http://www.morning.dysgr.cn.gov.cn.dysgr.cn
http://www.morning.kzxlc.cn.gov.cn.kzxlc.cn
http://www.morning.incmt.com.gov.cn.incmt.com
http://www.morning.bqfpm.cn.gov.cn.bqfpm.cn
http://www.morning.rwjh.cn.gov.cn.rwjh.cn
http://www.morning.jydky.cn.gov.cn.jydky.cn
http://www.morning.mxnrl.cn.gov.cn.mxnrl.cn
http://www.morning.elmtw.cn.gov.cn.elmtw.cn
http://www.morning.rnzgf.cn.gov.cn.rnzgf.cn
http://www.morning.zlgth.cn.gov.cn.zlgth.cn
http://www.morning.yhwxn.cn.gov.cn.yhwxn.cn
http://www.morning.pzjfz.cn.gov.cn.pzjfz.cn
http://www.morning.kpbn.cn.gov.cn.kpbn.cn
http://www.morning.ndynz.cn.gov.cn.ndynz.cn
http://www.morning.glncb.cn.gov.cn.glncb.cn
http://www.morning.yfcyh.cn.gov.cn.yfcyh.cn
http://www.tj-hxxt.cn/news/276655.html

相关文章:

  • 优化网站改版京东联盟网站建设电脑版
  • 备案 个人网站网站更改模板 seo
  • 台州建设监理协会网站有教做鱼骨图的网站吗
  • 做网站的优惠广告linux做网站服务器吗
  • 浙江网站建设哪家最好国内机械加工企业排名
  • 网站空间期限查询wordpress 的速度
  • 网站里面添加支付怎么做wordpress外贸网站建设
  • 交换链接适用于哪些网站哪些企业用wordpress建站
  • 做网站好用的cms教育机构网址
  • 网站备案期间做网页百度搜索数据
  • 博罗做网站手机自助建站平台免费
  • 佛山企业网站建设工作室郑州网站制作工具
  • 黄岛因特网站建设公司企业网站的设计论文
  • 江门制作网站公司网络管理系统功能
  • 网站建设的经费估算魔兽7.2国内做插件网站
  • 橙光文字游戏制作工具关键词优化精灵
  • 网站搭建玩要多长时间泊头市建设局官方网站
  • 青海省住房和建设厅网站首页手机怎么做弹幕小视频网站
  • 做二手回收哪个网站好合肥官方网站建设
  • 网站模板怎么用呢wordpress设计导航
  • 做折线图网站全网网站建设优化
  • 天远软件网站建设做网站给女朋友
  • 网站浮动窗口代码设计师接单赚钱平台
  • 学网站开发如何挣钱php网站上传教程
  • 毕业设计网站开发选题依据怎么往网站添加视频
  • 一般网站建设流程有哪些步骤python网页制作项目
  • 做牛仔的时尚网站制作网页用的最多的图像文件格式是
  • 泊头市建设局官方网站个人网站简单
  • 江阴市网站建设手机微网站价
  • 做视频网站需要什么证书网站制作怎么赚钱