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

德州网站建设的公司沈阳做网站哪个好

德州网站建设的公司,沈阳做网站哪个好,wordpress搜狗收录,网站建设需要的资料网络编程套接字#xff08;一#xff09;理解源IP地址和目的IP地址理解源MAC地址和目的MAC地址理解源端口号和目的端口号PORT VS PID认识TCP协议和UDP协议网络字节序socket编程接口socket常见APIsockaddr结构简单的UDP网络程序服务端创建套接字服务端绑定字符串IP VS 整数IP客… 网络编程套接字一理解源IP地址和目的IP地址理解源MAC地址和目的MAC地址理解源端口号和目的端口号PORT VS PID认识TCP协议和UDP协议网络字节序socket编程接口socket常见APIsockaddr结构简单的UDP网络程序服务端创建套接字服务端绑定字符串IP VS 整数IP客户端创建套接字关于客户端的绑定问题启动客户端理解源IP地址和目的IP地址 因特网上的每台计算机都有一个唯一的IP地址如果一台主机上的数据要传输到另一台主机那么对端主机的IP地址就应该作为该数据传输时的目的IP地址。但仅仅知道目的IP地址是不够的当对端主机收到该数据后对端主机还需要对该主机做出响应因此对端主机也需要发送数据给该主机此时对端主机就必须知道该主机的IP地址。因此一个传输的数据当中应该涵盖其源IP地址和目的IP地址目的IP地址表明该数据传输的目的地源IP地址作为对端主机响应时的目的IP地址。 在数据进行传输之前会先自顶向下贯穿网络协议栈完成数据的封装其中在网络层封装的IP报头当中就涵盖了源IP地址和目的IP地址。而除了源IP地址和目的IP地址之外还有源MAC地址和目的MAC地址的概念。 理解源MAC地址和目的MAC地址 大部分数据的传输都是跨局域网的数据在传输过程中会经过若干个路由器最终才能到达对端主机。 源MAC地址和目的MAC地址是包含在链路层的报头当中的而MAC地址实际只在当前局域网内有效因此当数据跨网络到达另一个局域网时其源MAC地址和目的MAC地址就需要发生变化因此当数据达到路由器时路由器会将该数据当中链路层的报头去掉然后再重新封装一个报头此时该数据的源MAC地址和目的MAC地址就发生了变化。 因此数据在传输的过程中是有两套地址 一套是源IP地址和目的IP地址这两个地址在数据传输过程中基本是不会发生变化的存在一些特殊情况比如在数据传输过程中使用NET技术其源IP地址会发生变化但至少目的IP地址是不会变化的。另一套就是源MAC地址和目的MAC地址这两个地址是一直在发生变化的因为在数据传输的过程中路由器不断在进行解包和重新封装。 理解源端口号和目的端口号 首先我们需要明确的是两台主机之间通信的目的不仅仅是为了将数据发送给对端主机而是为了访问对端主机上的某个服务。比如我们在用百度搜索引擎进行搜索时不仅仅是想将我们的请求发送给对端服务器而是想访问对端服务器上部署的百度相关的搜索服务。 socket通信的本质 现在通过IP地址和MAC地址已经能够将数据发送到对端主机了但实际我们是想将数据发送给对端主机上的某个服务进程此外数据的发送者也不是主机而是主机上的某个进程比如当我们用浏览器访问数据时实际就是浏览器进程向对端服务进程发起的请求。 也就是说socket通信本质上就是两个进程之间在进行通信只不过这里是跨网络的进程间通信。比如逛淘宝和刷抖音的动作实际就是手机上的淘宝进程和抖音进程在和对端服务器主机上的淘宝服务进程和抖音服务进程之间在进行通信。 因此进程间通信的方式除了管道、消息队列、信号量、共享内存等方式外还有套接字只不过前者是不跨网络的而后者是跨网络的. 端口号 实际在两台主机上可能会同时存在多个正在进行跨网络通信的进程因此当数据到达对端主机后必须要通过某种方法找到该主机上对应的服务进程然后将数据交给该进程处理。而当该进程处理完数据后还要对发送端进行响应因此对端主机也需要知道是发送端上的哪一个进程向它发送的数据请求。 端口号port的作用实际就是标识一台主机上的一个进程。 端口号是传输层协议的内容。端口号是一个2字节16位的整数。端口号用来标识一个进程告诉操作系统当前的这个数据要交给哪一个进程来处理。一个端口号只能被一个进程占用。 由于IP地址能够唯一标识公网内的一台主机而端口号能够唯一标识一台主机上的一个进程因此用IP地址端口号就能够唯一标识网络上的某一台主机的某一个进程。 当数据在传输层进行封装时就会添加上对应源端口号和目的端口号的信息。这时通过源IP地址源端口号就能够在网络上唯一标识发送数据的进程通过目的IP地址目的端口号就能够在网络上唯一标识接收数据的进程此时就实现了跨网络的进程间通信。 注意 因为端口号是隶属于某台主机的所以端口号可以在两台不同的主机当中重复但是在同一台主机上进行网络通信的进程的端口号不能重复。此外一个进程可以绑定多个端口号但是一个端口号不能被多个进程同时绑定。 PORT VS PID 端口号port的作用唯一标识一台主机上的某个进程进程IDPID的作用也是唯一标识一台主机上的某个进程那在进行网络通信时为什么不直接用PID来代替port呢 进程IDPID是用来标识系统内所有进程的唯一性的它是属于系统级的概念而端口号port是用来标识需要对外进行网络数据请求的进程的唯一性的它是属于网络的概念。 一台机器上可能会有大量的进程但并不是所有的进程都要进行网络通信可能有很大一部分的进程是不需要进行网络通信的本地进程此时PID虽然也可以标识这些网络进程的唯一性但在该场景下就不太合适了。 比如每个人都有自己的身份证号身份证号已经可以标识我们的唯一性了但是当我们到了学校还是会有学号到了公司还是会有工号。这是为什么呢为什么不直接用身份证号来代替学号和工号呢 因为身份证号是国家用于行政管理时用的编号而学号是学校用于管理学生时用的编号工号是公司用于管理员工时用的编号。但并不是全中国人都在某所学校或某家公司因此在学校或公司当中没必要用身份证号来标识每个人的唯一性。此时就出现了学号和工号在学号和工号当中还可以包含一些便于管理的信息比如入学入职年份、性别等信息。 也就是说在不同的场景下可能需要不同的编号来标识某种事物的唯一性因为这些编号更适合用于该场景。 底层如何通过port找到对应进程的 实际底层采用哈希的方式建立了端口号和进程PID或PCB之间的映射关系当底层拿到端口号时就可以直接执行对应的哈希算法然后就能够找到该端口号对应的进程。 认识TCP协议和UDP协议 网络协议栈是贯穿整个体系结构的在应用层、操作系统层和驱动层各有一部分。当我们使用系统调用接口实现网络数据通信时不得不面对的协议层就是传输层而传输层最典型的两种协议就是TCP协议和UDP协议。 TCP协议 TCP协议叫做传输控制协议Transmission Control ProtocolTCP协议是一种面向连接的、可靠的、基于字节流的传输层通信协议。 TCP协议是面向连接的如果两台主机之间想要进行数据传输那么必须要先建立连接当连接建立成功后才能进行数据传输。其次TCP协议是保证可靠的协议数据在传输过程中如果出现了丢包、乱序等情况TCP协议都有对应的解决方法。 UDP协议 UDP协议叫做用户数据报协议User Datagram ProtocolUDP协议是一种无需建立连接的、不可靠的、面向数据报的传输层通信协议。 使用UDP协议进行通信时无需建立连接如果两台主机之间想要进行数据传输那么直接将数据发送给对端主机就行了但这也就意味着UDP协议是不可靠的数据在传输过程中如果出现了丢包、乱序等情况UDP协议本身是不知道的。 既然UDP协议是不可靠的那为什么还要有UDP协议的存在 TCP协议是一种可靠的传输协议使用TCP协议能够在一定程度上保证数据传输时的可靠性而UDP协议是一种不可靠的传输协议UDP协议的存在有什么意义 首先可靠是需要我们做更多的工作的TCP协议虽然是一种可靠的传输协议但这一定意味着TCP协议在底层需要做更多的工作因此TCP协议底层的实现是比较复杂的我们不能只看到TCP协议面向连接可靠这一个特点我们也要能看到TCP协议对应的缺点。 同样的UDP协议虽然是一种不可靠的传输协议但这一定意味着UDP协议在底层不需要做过多的工作因此UDP协议底层的实现一定比TCP协议要简单UDP协议虽然不可靠但是它能够快速的将数据发送给对方虽然在数据在传输的过程中可能会出错。 编写网络通信代码时具体采用TCP协议还是UDP协议完全取决于上层的应用场景。如果应用场景严格要求数据在传输过程中的可靠性此时我们就必须采用TCP协议如果应用场景允许数据在传输出现少量丢包那么我们肯定优先选择UDP协议因为UDP协议足够简单。 注意 一些优秀的网站在设计网络通信算法时会同时采用TCP协议和UDP协议当网络流畅时就使用UDP协议进行数据传输而当网速不好时就使用TCP协议进行数据传输此时就可以动态的调整后台数据通信的算法。 网络字节序 网络中的大小端问题 计算机在存储数据时是有大小端的概念的 大端模式 数据的高字节内容保存在内存的低地址处数据的低字节内容保存在内存的高地址处。小端模式 数据的高字节内容保存在内存的高地址处数据的低字节内容保存在内存的低地址处。 如果编写的程序只在本地机器上运行那么是不需要考虑大小端问题的因为同一台机器上的数据采用的存储方式都是一样的要么采用的都是大端存储模式要么采用的都是小端存储模式。但如果涉及网络通信那就必须考虑大小端的问题否则对端主机识别出来的数据可能与发送端想要发送的数据是不一致的。 由于我们不能保证通信双方存储数据的方式是一样的因此网络当中传在这里插入代码片输的数据必须考虑大小端问题。因此TCP/IP协议规定网络数据流采用大端字节序即低地址高字节。无论是大端机还是小端机都必须按照TCP/IP协议规定的网络字节序来发送和接收数据。 如果发送端是小端需要先将数据转成大端然后再发送到网络当中。如果发送端是大端则可以直接进行发送。如果接收端是小端需要先将接收到数据转成小端后再进行数据识别。如果接收端是大端则可以直接进行数据识别。 需要注意的是所有的大小端的转化工作是由操作系统来完成的因为该操作属于通信细节不过也有部分的信息需要我们自行进行处理比如端口号和IP地址。 网络字节序与主机字节序之间的转换 为使网络程序具有可移植性使同样的C代码在大端和小端计算机上编译后都能正常运行系统提供了四个函数可以通过调用以下库函数实现网络字节序和主机字节序之间的转换。 #include arpa/inet.huint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);函数名当中的h表示hostn表示networkl表示32位长整数s表示16位短整数。例如htonl表示将32位长整数从主机字节序转换为网络字节序。如果主机是小端字节序则这些函数将参数做相应的大小端转换然后返回。如果主机是大端字节序则这些函数不做任何转换将参数原封不动地返回。 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结构体其中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类型 我们可以将这些函数的struct sockaddr参数类型改为void此时在函数内部也可以直接指定提取头部的16个比特位进行识别最终也能够判断是需要进行网络通信还是本地通信那为什么还要设计出sockaddr这样的结构呢 实际在设计这一套网络接口的时候C语言还不支持void*于是就设计出了sockaddr这样的解决方案。并且在C语言支持了void*之后也没有将它改回来因为这些接口是系统接口系统接口是所有上层软件接口的基石系统接口是不能轻易更改的否则引发的后果是不可想的这也就是为什么现在依旧保留sockaddr结构的原因。 简单的UDP网络程序 服务端创建套接字 我们把服务器封装成一个类当我们定义出一个服务器对象后需要马上初始化服务器而初始化服务器需要做的第一件事就是创建套接字。 socket函数 创建套接字的函数叫做socket该函数的函数原型如下 int socket(int domain, int type, int protocol);参数说明 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表示的就是默认此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。 返回值说明 套接字创建成功返回一个文件描述符创建失败返回-1同时错误码会被设置。 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对应的文件进行关闭但实际上不进行该操作也行因为一般服务器运行后是就不会停下来的。 服务端绑定 现在套接字已经创建成功了但作为一款服务器来讲如果只是把套接字创建好了那我们也只是在系统层面上打开了一个文件操作系统将来并不知道是要将数据写入到磁盘还是刷到网卡此时该文件还没有与网络关联起来。 bind函数 绑定的函数叫做bind该函数的函数原型如下 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数说明 sockfd绑定的文件的文件描述符。也就是我们创建套接字时获取到的文件描述符。addr网络相关的属性信息包括协议家族、IP地址、端口号等。addrlen传入的addr结构体的长度。 返回值说明 绑定成功返回0绑定失败返回-1同时错误码会被设置。 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地址实际就是存储在这个整数当中的。 服务端绑定 套接字创建完毕后我们就需要进行绑定了但在绑定之前我们需要先定义一个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 VS 整数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);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);参数说明 sockfd对应操作的文件描述符。表示从该文件描述符索引的文件当中读取数据。buf读取数据的存放位置。len期望读取数据的字节数。flags读取的方式。一般设置为0表示阻塞读取。src_addr对端网络相关的属性信息包括协议家族、IP地址、端口号等。addrlen调用时传入期望读取的src_addr结构体的长度返回时代表实际读取到的src_addr结构体的长度这是一个输入输出型参数。 返回值说明 读取成功返回实际读取到的字节数读取失败返回-1同时错误码会被设置。 注意 由于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地址 };引入命令行参数 鉴于构造服务器时需要传入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; }虽然现在客户端代码还没有编写但是我们可以通过netstat命令来查看当前网络的状态这里我们可以选择携带nlup选项。 netstat常用选项说明 -n直接使用IP地址而不通过域名服务器。-l显示监控中的服务器的Socket。-t显示TCP传输协议的连线状况。-u显示UDP传输协议的连线状况。-p显示正在使用Socket的程序识别码和程序名称。 此时你就能查看到对应网络相关的信息在这些信息中程序名称为./udp_server的那一行显示的就是我们运行的UDP服务器的网络信息。 其中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);参数说明 sockfd对应操作的文件描述符。表示将数据写入该文件描述符索引的文件当中。buf待写入数据的存放位置。len期望写入数据的字节数。flags写入的方式。一般设置为0表示阻塞写入。 -dest_addr对端网络相关的属性信息包括协议家族、IP地址、端口号等。 addrlen传入- dest_addr结构体的长度。 返回值说明 写入成功返回实际写入的字节数写入失败返回-1同时错误码会被设置。 注意 由于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函数启动客户端了。
http://www.hkea.cn/news/14527755/

相关文章:

  • 阿里巴巴外贸订单网站常州网站建设教程
  • 对招聘网站页面设计做建议网站推广品牌建设
  • 韩国男女直接做视频网站专业建站公司联系方式
  • 网站建设定制开发推广短视频万能素材库
  • 永州网站推广wordpress内容页标题
  • 做兼职什么网站比较好装修设计视频
  • 织梦手机端网站怎么做建筑网站新闻写那些好
  • 深圳网站优化平台ps网站头部
  • 网站推广站点建设与策划手机网站建设的公司排名
  • 云盘网站建设图片上传网站源码
  • 常州做网站哪家快黑河网站建设公司
  • 南昌网站建设方案优化做一个软件需要哪些技术
  • dnf网站上怎么做商人本周新闻热点10条
  • 建网站需要服务器吗做网站的软件工程师
  • 网站在政务新媒体建设方案crm系统官网
  • 弹窗广告最多的网站网站的头尾和导航的公用文件
  • 如何在网站中加入百度地图深圳市建设工程交易服务中心宝安分中心
  • 金花站长工具平台商业模式有哪些
  • wordpress站点名没有更改做折扣的网站有哪些
  • 宁波网站推广网站优化郑州网站建设制作费用
  • 国家网站备案常德市网站建设
  • wap网站开发价格wordpress 文章rss
  • 分类信息网站营销杭州精高端网站建设
  • 南通网站排名方法交互做的比较好的网站
  • 网站开发和美工的区别个人网站制作申请
  • 卯兔科技网站建设购买一个网站需要多少钱
  • 青浦网站开发做饲料的专业网站
  • 域名不转出可以做网站吗霸州市网站建设
  • 昆明企业网站建设一条龙cnnic可信网站必须做吗
  • 黄山网站建设推荐电子网站建设推广方案