2026/5/21 13:20:27
网站建设
项目流程
网站建设与管理试题答案,银联支付网站建设,秦皇岛市教育考试院,自己怎么开发网站源IP地址和⽬的IP地址我们知道在⽹络中#xff0c;IP ⽤来标识主机的唯⼀性。源 IP 地址就是发送数据的设备的IP地址#xff0c;相当于快递上的寄件人地址。⽬的IP地址接收数据的设备的 IP 地址#xff0c;相当于快递上的收件人地址。端⼝号端口号解决的是 “主机上哪个应用…源IP地址和⽬的IP地址我们知道在⽹络中IP ⽤来标识主机的唯⼀性。源 IP 地址就是发送数据的设备的IP地址相当于快递上的寄件人地址。⽬的IP地址接收数据的设备的 IP 地址相当于快递上的收件人地址。端⼝号端口号解决的是 “主机上哪个应用接收数据” 的问题。IP就相当于你们小区的地址端口号port就代表了你在这一小区的哪一栋哪一户。所以网络中通信的本质实际上就是进程间通信。只不过该通信是跨越网络的而我们将跨越网络的进程间通信称之为套接字通信。认识端口号端⼝号是⼀个 2 字节 16 位的整数;端⼝号⽤来标识⼀个进程, 告诉操作系统, 当前的这个数据要交给哪⼀个进程来处理;IP地址 端⼝号能够标识⽹络上的某⼀台主机的某⼀个进程;⼀个端⼝号只能被⼀个进程占⽤.但是我们需要知道端口号并不是自动分配给所有进程的而是进行绑定和申请的。只有需要进行网络通信的进程才需要分配端口号。端口号的范围划分0 - 1023 : 属于系统保留端口号像HTTP, FTP, SSH 等这些⼴为使⽤的应⽤层协议, 他们的端⼝号都是固定的.尽管有一些端口号还没有被正式分配但也不推荐用来绑定普通用户进程如若想绑定需要root权限。1024~49151 : 普通用户进程可直接绑定无需管理员权限。49152~65535不建议服务器绑定这个区间是操作系统的临时端口池当客户端进程发起网络连接时操作系统会从这个区间随机分配一个临时端口作为客户端的源端口通信结束后端口会被释放。理解 端⼝号 和 进程ID既然端口号port和 进程IDPID的作用都是标识一台主机上唯一的一个进程那在进行socket通信时为什么不直接用进程ID 呢我们知道PID是随进程的创建和销毁动态变化的每重启一次服务器PID都会变化这就会导致一个致命的问题客户端每次访问都需要知道服务器进程的最新PID。源端⼝号和⽬的端⼝号传输层协议( TCP 和 UDP )的数据段中有两个端⼝号, 分别叫做源端⼝号和⽬的端⼝号. 就是在描述 数据是谁发的, 要发给谁;理解socketIP代表互联网主机的唯一标识Port 主机内网络进程的唯一标识。IPPort 套接字地址Socket Address能够代表互联网上的唯一进程了。通信的时候本质是两个互联⽹进程代表⼈来进⾏通信{srcIpsrcPortdstIpdstPort}这样的4元组就能标识互联⽹中唯⼆的两个进程。TCP协议与UDP协议如果我们了解了系统也了解了⽹络协议栈我们就会清楚传输层是属于内核的那么我们要通过⽹络协议栈进⾏通信必定调⽤的是传输层提供的系统调⽤来进⾏的⽹络通信。认识UDP协议特点有连接 TCP 通信前必须经过三次握手建立连接通信后通过四次挥手关闭连接连接的生命周期有明确的状态可靠传输TCP 通过序列号、确认应答ACK、重传机制保证数据的完整性。面向字节流TCP 把数据当作无边界的字节流处理发送方可以分多次写数据接收方可以分多次读数据他的这些特点 “可靠” 和 “有连接” 就注定了他有缺点建立 / 关闭连接开销大协议头大额外开销高实时性差状态维护成本高认识UDP协议特点无连接通信开销极低面向数据报消息边界清晰不可靠传输数据可能丢失 / 乱序 / 重复UDP虽然有不可靠传输的特点但并不代表它就比TCP要差。 一些优秀的网站在设计网络通信算法时会同时采用TCP协议和UDP协议当网络流畅时就使用UDP协议进行数据传输而当网速不好时就使用TCP协议进行数据传输此时就可以动态的调整后台数据通信的算法。总结编写网络通信代码时具体采用TCP协议还是UDP协议完全取决于上层的应用场景。如果应用场景严格要求数据在传输过程中的可靠性此时我们就必须采用TCP协议如果应用场景允许数据在传输出现少量丢包那么我们肯定优先选择UDP协议因为UDP协议足够简单。⽹络字节序我们知道,内存中的多字节数据相对于内存地址有⼤端和⼩端之分, 磁盘⽂件中的多字节数据相对于⽂件中的偏移地址也有⼤端⼩端之分, ⽹络数据流同样有⼤端⼩端之分.那么在网络中如何定义网络数据流的地址呢因此TCP/IP协议规定网络数据流采用大端字节序即低地址高字节。无论是大端机还是小端机都必须按照TCP/IP协议规定的网络字节序来发送和接收数据。如果发送端是小端需要先将数据转成大端然后再发送到网络当中如果发送端是大端则可以直接进行发送如果接收端是小端需要先将接收到数据转成小端后再进行数据识别如果接收端是大端则可以直接进行数据识别为使⽹络程序具有可移植性,使同样的C代码在⼤端和⼩端计算机上编译后都能正常运⾏,可以调⽤以下库函数做⽹络字节序和主机字节序的转换。#include arpa/inet.h uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);这几个函数名也很好理解当中h 表⽰ host , n 表⽰ network , l 表⽰ (long) 32 位⻓整数, s 表⽰(short) 16 位短整数.htonl 表⽰将 32 位的⻓整数从主机字节序转换为⽹络字节序后准备发送。如果主机是⼩端字节序,这些函数将参数做相应的⼤⼩端转换然后返回;如果主机是⼤端字节序,这些函数不做转换,将参数原封不动地返回。socket编程接⼝socket常见API// 创建 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);创建套接字socketint socket(int domain, int type, int protocol);作用socket()的作用是向操作系统内核申请创建一个套接字Socket对象并返回一个用于操作这个 Socket 的文件描述符fd。失败则返回-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表示的就是默认此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。绑定端口号bindint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);bind()的核心是将创建好的 Socket 文件描述符sockfd与指定的套接字地址IPPort关联起来。绑定成功返回0绑定失败返回-1参数sockfd绑定的文件的文件描述符。也就是我们创建套接字时获取到的文件描述符addr网络相关的属性信息包括协议家族、IP地址、端口号等addrlen传入的addr结构体的长度发送数据send、sendto// 用于面向连接(TCP)的套接字将数据发送到已连接的对等方 ssize_t send(int sockfd, const void *buf, size_t len, int flags); // 用于无连接(UDP)的套接字或未连接的面向连接套接字将数据发送到指定地址 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);TCP 通信因为提前建立了连接三次握手内核已经知道目标地址所以用send()直接发数据即可UDP 是无连接的每次发数据都要告诉内核 “发给谁”所以必须用sendto()指定目标IPPort。成功时返回实际发送的字节数。这个数字可能小于len表示只发送了部分数据。这通常发生在以下情况send发送缓冲区已满无法一次性发送全部数据需要多次调用send函数将剩余数据发送完。失败时返回 -1参数sockfd套接字描述符。buf指向要发送数据的缓冲区的指针。len要发送数据的长度。flags控制发送操作的标志如 MSG_DONTWAIT 不阻塞等。dest_addr仅 sendto指向接收方地址的指针。addrlen仅 sendto接收方地址结构体的长度。接收数据 recv、recvfrom// 用于面向连接的套接字从已连接的对等方接收数据 ssize_t recv(int sockfd, void *buf, size_t len, int flags); // 用于无连接的套接字或未连接的面向连接套接字接收数据并获取发送方地址 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);TCP 因为提前建立了连接内核已经知道数据来自哪个客户端所以用recv()只需要接收数据即可。UDP 是无连接的每次收到数据都不知道是谁发的所以必须用recvfrom()同时获取数据和发送方地址。成功时返回接收到的字节数如果连接已经正常关闭返回值为 0表示对方已关闭连接。失败时返回 -1参数sockfd套接字描述符。buf指向接收数据的缓冲区的指针。len缓冲区的长度。flags控制接收操作的标志如 MSG_DONTWAIT 不阻塞等。src_addr仅 recvfrom指向发送方地址的指针。addrlen仅 recvfrom指向发送方地址结构体长度的指针。监听套接字listenint listen(int sockfd, int backlog);状态转换把调用bind()后的 Socket 从主动套接字可发起连接转为被动监听套接字告诉内核“这个 Socket 要专门等待客户端的连接请求”创建连接队列内核会为这个监听 Socket 创建两个队列未完成连接队列 已完成连接队列用于存放 TCP 三次握手过程中的连接请求backlog参数就是用来限制队列的最大长度。成功返回 0表示套接字已成功进入监听状态服务器可以接收客户端的连接请求。失败返回 -1并设置相应的错误代码。可以使用perror函数来输出具体的错误信息。参数sockfd这是一个已经创建并绑定了本地地址的套接字描述符。套接字通常由socket函数创建例如int sockfd socket(AF_INET, SOCK_STREAM, 0);。对于AF_INET表示使用 IPv4 协议族SOCK_STREAM表示使用面向连接的 TCP 套接字。在调用listen之前该套接字需要使用bind函数绑定到一个本地地址以便让服务器程序在特定的地址和端口上监听客户端的连接请求。backlog它指定了连接请求队列的最大长度。当多个客户端同时尝试连接服务器时操作系统会将这些连接请求存储在一个队列中等待服务器处理。例如将backlog设置为 10意味着最多可以有 10 个未处理的连接请求在队列中等待服务器的accept操作。设置这个值时需要权衡资源利用和性能过小可能导致连接请求丢失过大可能会占用过多的系统资源接收请求acceptint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);从连接队列取连接从listen()创建的已完成连接队列中取出一个三次握手完成的客户端连接创建新 Socket为这个客户端连接创建一个新的 Socket 文件描述符client_fd后续和客户端的recv()/send()都通过这个新 fd 操作获取客户端地址可选地获取客户端的IPPort填充到addr参数中方便服务器识别 “谁连过来了”。返回值成功accept函数会返回一个新的套接字描述符这个新的套接字将用于和客户端进行通信。服务器可以使用这个新的套接字进行读写操作例如使用send和recv函数发送和接收数据。失败如果accept调用失败它将返回 -1并设置相应的错误代码。可以使用perror函数来输出具体的错误信息常见的错误可能包括系统资源不足、没有连接请求等待等。参数sockfd这是一个已经处于监听状态的套接字描述符通常是由socket函数创建并经过bind和listen函数处理后进入监听状态的套接字。例如int sockfd socket(AF_INET, SOCK_STREAM, 0);并且经过listen(sockfd, backlog);处理。它代表服务器端的监听套接字通过该套接字接收客户端的连接请求。addr这是一个指向struct sockaddr或其变体如struct sockaddr_in对于 IPv4的指针用于存储客户端的地址信息。当accept成功返回时这个指针所指向的结构体将被填充为发起连接请求的客户端的地址信息包括客户端的 IP 地址和端口号。如果不关心客户端的地址信息可以将此参数设置为NULL。addrlen这是一个指向socklen_t类型的指针它表示addr参数所指向的结构体的长度。在调用accept之前需要将其初始化为addr所指向结构体的大小例如对于struct sockaddr_in可以使用sizeof(struct sockaddr_in)。在accept返回后addrlen的值可能会被修改为实际存储客户端地址信息的长度尤其是在使用可变长度地址结构体时。建立连接connectint connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);发起连接请求向指定的服务器IPPort发送 TCP 三次握手的第一个 SYN 包完成三次握手等待服务器回复 SYNACK再发送 ACK 包最终建立 TCP 连接但是UDP 是无连接的调用connect()只是 “绑定默认目标地址”不会发起任何网络包后续可直接用send()代替sendto()。返回值成功如果connect函数调用成功它将返回 0表示已经成功连接到服务器。一旦连接成功客户端就可以使用这个套接字发送和接收数据例如使用send和recv函数。失败如果connect函数调用失败它将返回 -1并设置相应的错误代码。可以使用perror函数输出具体的错误信息。常见的错误可能包括服务器不可达、连接超时、服务器未监听等。参数sockfd这是一个已经创建的客户端套接字描述符addr这是一个指向struct sockaddr或其变体如struct sockaddr_in对于 IPv4 或struct sockaddr_in6对于 IPv6的指针包含了服务器的地址信息。addrlen这是addr所指向的结构体的长度通常使用sizeof操作符获取。sockaddr结构socket API是⼀层抽象的⽹络编程接⼝,适⽤于各种底层⽹络协议,如IPv4、IPv6,以及UNIX Domain Socket. 然⽽, 各种⽹络协议的地址格式并不相同.套接字不仅支持跨网络的进程间通信还支持本地的进程间通信域间套接字。在进行跨网络通信时我们需要传递的端口号和IP地址而本地通信则不需要因此套接字提供了sockaddr_in结构体和sockaddr_un结构体sockaddr_in 结构体是用于跨网络通信的sockaddr_un 结构体是用于本地通信的为了让套接字的网络通信和本地通信能够使用同一套函数接口于是就出现了sockeaddr结构体该结构体与sockaddr_in和sockaddr_un的结构都不相同但这三个结构体头部的16个比特位都是一样的这个字段叫做协议家族IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址⽤sockaddr_in结构体表⽰,包括16位地址类型, 16位端⼝号和32位IP地址.IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的⾸地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.socket API可以都⽤struct sockaddr *类型表⽰, 在使⽤的时候需要强制转化成sockaddr_in; 这样的好处是程序的通⽤性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数;为什么没有用 void* 代替 struct sockaddr* 类型Socket API 诞生于 1980 年代的 BSD 系统而 C 语言的void*是在 C89 标准1989 年才被标准化 —— 早期的 Socket API 设计时void*还不是通用的 “无类型指针”因此选择用struct sockaddr作为通用地址类型后续为了兼容已有代码一直保留了这个设计。