郑州网站开发培训班,可遇公寓网站哪个公司做的,wordpress 常用的钩子,协会网站信息平台建设1. 概念
局域网和广域网
局域网#xff1a;局域网将一定区域内的各种计算机、外部设备和数据库连接起来形成计算机通信的私有网络。广域网#xff1a;又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程公共网络。
IP#xff08;Internet Protocol局域网将一定区域内的各种计算机、外部设备和数据库连接起来形成计算机通信的私有网络。广域网又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程公共网络。
IPInternet Protocol本质是一个整形数用于表示计算机在网络中的地址。IP协议版本有两个IPv4和IPv6
IPv4Internet Protocol version4使用一个32位的整形数描述一个IP地址4个字节int型也可以使用一个点分十进制字符串描述这个IP地址 192.168.247.135。分成了4份每份1字节8bitchar最大值为 2550.0.0.0 是最小的IP地址255.255.255.255是最大的IP地址按照IPv4协议计算可以使用的IP地址共有 232 个IPv6Internet Protocol version6使用一个128位的整形数描述一个IP地址16个字节也可以使用一个字符串描述这个IP地址2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b分成了8份每份2字节每一部分以16进制的方式表示按照IPv6协议计算可以使用的IP地址共有 2128 个
查看IP地址
# linux
$ ifconfig# windows
$ ipconfig# 测试网络是否畅通
# 主机a: 192.168.1.11
# 当前主机: 192.168.1.12
$ ping 192.168.1.11 # 测试是否可用连接局域网
$ ping www.baidu.com # 测试是否可用连接外网# 特殊的IP地址: 127.0.0.1 和本地的IP地址是等价的
# 假设当前电脑没有联网, 就没有IP地址, 又要做网络测试, 可用使用 127.0.0.1 进行本地测试 端口端口的作用是定位到主机上的某一个进程通过这个端口进程就可以接受到对应的网络数据了。 比如: 在电脑上运行了微信和QQ, 小明通过客户端给我的的微信发消息, 电脑上的微信就收到了消息, 为什么?
运行在电脑上的微信和QQ都绑定了不同的端口通过IP地址可以定位到某一台主机通过端口就可以定位到主机上的某一个进程通过指定的IP和端口发送数据的时候对端就能接受到数据了 端口也是一个整形数 unsigned short 一个16位整形数有效端口的取值范围是0 ~ 65535(0 ~ 216-1)
提问计算机中所有的进程都需要关联一个端口吗一个端口可以被重复使用吗?
不需要如果这个进程不需要网络通信那么这个进程就不需要绑定端口的一个端口只能给某一个进程使用多个进程不能同时使用同一个端口 OSI/ISO 网络分层模型OSIOpen System Interconnect即开放式系统互联。 一般都叫OSI参考模型是ISO国际标准化组织组织在1985年研究的网络互联模型。 1. 物理层Physical layer负责传输比特流将数据转化为适合在物理媒介上传输的电信号如网线、光纤等。2. 数据链路层Data Link layer提供可靠的数据传输将物理层传输的比特流组织为帧Frame并通过检验和等方式进行错误检测和纠正。3. 网络层Network layer实现网络互连和数据路由负责确定数据的传输路径并将数据包Packet从源节点传输到目标节点。4. 传输层Transport layer负责为应用程序提供端到端的通信服务包括可靠的数据传输和数据分段重组常见的协议如TCP和UDP。5. 会话层Session layer负责建立、管理和终止应用程序之间的会话连接包括控制会话的建立、同步和终止等操作。6. 表示层Presentation layer负责数据的格式转换、加密解密和压缩解压缩等操作以确保应用程序能够正确解释和处理接收到的数据。7. 应用层Application layer提供用户应用程序与网络之间的接口包括文件传输、电子邮件、网页浏览等各种应用服务。 这七层协议组成了一个通用的网络模型每一层都具有不同的功能和责任通过协同工作实现了数据在计算机网络中的可靠传输和应用程序之间的交互。请注意实际的网络协议栈如TCP/IP并不完全符合OSI模型但是OSI模型仍然被广泛用于理解和教学网络概念。
2. 网络协议 网络协议是计算机网络中用于在不同计算机之间进行数据传输和交换的规则和约定。它定义了数据如何在网络中传输、如何组织和解释数据以及如何处理错误和冲突等情况。常见的网络协议包括
1. TCP/IP协议是互联网的核心协议包括传输控制协议TCP和Internet协议IP。TCP提供可靠的数据传输确保数据的完整性和有序性IP负责数据包的路由和寻址。2. HTTP协议是用于在Web上进行超文本传输的协议定义了客户端和服务器之间的通信规则。常用于浏览器和服务器之间传输网页、图片和其他资源。3. FTP协议是文件传输协议用于在客户端和服务器之间进行文件传输。它支持上传、下载、删除和重命名等操作。4. SMTP协议是简单邮件传输协议用于在邮件服务器之间传输电子邮件。它定义了邮件的格式和传输规则。5. POP协议和IMAP协议是接收邮件的协议用于从邮件服务器中接收电子邮件。POP协议通常会将邮件下载到本地设备而IMAP协议则保留邮件在服务器上以便在不同设备上同步访问邮件。6. DNS协议是域名系统协议用于将域名如www.example.com转换为对应的IP地址。它提供了域名解析和IP地址管理的功能。7. DHCP协议是动态主机配置协议用于在局域网中自动分配IP地址给计算机和其他设备。它还提供了其他网络配置信息如子网掩码、网关和DNS服务器等。 这些协议在计算机网络中发挥着重要的作用实现了可靠的数据传输、资源共享、电子邮件通信和互联网的基本功能。不同的协议在不同层次上工作并相互配合以实现全面的网络通信。
3. socket编程 Socket编程是一种用于在计算机网络之间进行通信的编程技术。它基于TCP/IP协议栈允许不同计算机之间的进程通过网络进行数据传输和交互。 通过Socket编程可以实现客户端和服务器之间的通信。客户端可以向服务器发送请求服务器则可以对这些请求进行响应。Socket编程使用网络套接字socket作为通信的端点允许进程在网络上发送和接收数据。 在Socket编程中存在两种常见类型的套接字客户端套接字和服务器套接字。客户端套接字用于连接服务器并发送请求而服务器套接字用于侦听客户端连接并处理请求。 Socket编程可以用于构建各种网络应用程序例如网络聊天程序、文件传输程序、Web服务器等。编程语言如Python、Java和C都提供了Socket编程的库和API使开发者能够方便地使用Socket进行网络通信。 需要注意的是Socket编程涉及到网络通信和数据传输因此在实际使用中需要考虑网络连接的稳定性、数据的完整性和安全性等方面的问题。
3.1 字节序 在各种计算机体系结构中对于字节、字等的存储机制有所不同因而引发了计算机通信领域中一个很重要的问题即通信双方交流的信息单元比特、字节、字、双字等等应该以什么样的顺序进行传送。如果不达成一致的规则通信双方将无法进行正确的编/译码从而导致通信失败。 字节序顾名思义字节的顺序就是大于一个字节类型的数据在内存中的存放顺序也就是说对于单字符来说是没有字节序问题的字符串是单字符的集合因此字符串也没有字节序问题。 目前在各种体系的计算机中通常采用的字节存储机制主要有两种Big-Endian 和 Little-Endian下面先从字节序说起。
Little-Endian - 主机字节序 (小端)数据的低位字节存储到内存的低地址位, 数据的高位字节存储到内存的高地址位我们使用的PC机数据的存储默认使用的是小端Big-Endian - 网络字节序 (大端)据的低位字节存储到内存的高地址位, 数据的高位字节存储到内存的低地址位套接字通信过程中操作的数据都是大端存储的包括接收/发送的数据、IP地址、端口。
字节序举例
// 有一个16进制的数, 有32位 (int): 0xab5c01ff
// 字节序, 最小的单位: char 字节, int 有4个字节, 需要将其拆分为4份
// 一个字节 unsigned char, 最大值是 255(十进制) ff(16进制) 内存低地址位 内存的高地址位
---------------------------------------------------------------------------
小端: 0xff 0x01 0x5c 0xab
大端: 0xab 0x5c 0x01 0xff 函数 BSD Socket提供了封装好的转换接口方便程序员使用。包括从主机字节序到网络字节序的转换函数htons、htonl从网络字节序到主机字节序的转换函数ntohs、ntohl。
#include arpa/inet.h
// u:unsigned
// 16: 16位, 32:32位
// h: host, 主机字节序
// n: net, 网络字节序
// s: short
// l: int// 这套api主要用于 网络通信过程中 IP 和 端口 的 转换
// 将一个短整形从主机字节序 - 网络字节序
uint16_t htons(uint16_t hostshort);
// 将一个整形从主机字节序 - 网络字节序
uint32_t htonl(uint32_t hostlong); // 将一个短整形从网络字节序 - 主机字节序
uint16_t ntohs(uint16_t netshort)
// 将一个整形从网络字节序 - 主机字节序
uint32_t ntohl(uint32_t netlong);
3.2 IP地址转换 虽然IP地址本质是一个整形数但是在使用的过程中都是通过一个字符串来描述下面的函数描述了如何将一个字符串类型的IP地址进行大小端转换
// 主机字节序的IP地址转换为网络字节序
// 主机字节序的IP地址是字符串, 网络字节序IP地址是整形
int inet_pton(int af, const char *src, void *dst);
参数:
af: 地址族(IP地址的家族包括ipv4和ipv6)协议 AF_INET: ipv4格式的ip地址AF_INET6: ipv6格式的ip地址src: 传入参数, 对应要转换的点分十进制的ip地址: 192.168.1.100dst: 传出参数, 函数调用完成, 转换得到的大端整形IP被写入到这块内存中
返回值成功返回1失败返回0或者-1
#include arpa/inet.h
// 将大端的整形数, 转换为小端的点分十进制的IP地址
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
参数:
af: 地址族协议 AF_INET: ipv4格式的ip地址AF_INET6: ipv6格式的ip地址src: 传入参数, 这个指针指向的内存中存储了大端的整形IP地址dst: 传出参数, 存储转换得到的小端的点分十进制的IP地址size: 修饰dst参数的, 标记dst指向的内存中最多可以存储多少个字节
返回值:
成功: 指针指向第三个参数对应的内存地址, 通过返回值也可以直接取出转换得到的IP字符串失败: NULL
还有一组函数也能进程IP地址大小端的转换但是只能处理ipv4的ip地址
// 点分十进制IP - 大端整形
in_addr_t inet_addr (const char *cp);// 大端整形 - 点分十进制IP
char* inet_ntoa(struct in_addr in);
3.3 sockaddr 数据结构
// 在写数据的时候不好用
struct sockaddr {sa_family_t sa_family; // 地址族协议, ipv4char sa_data[14]; // 端口(2字节) IP地址(4字节) 填充(8字节)
}typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;
typedef unsigned short int sa_family_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))struct in_addr
{in_addr_t s_addr;
}; // sizeof(struct sockaddr) sizeof(struct sockaddr_in)
struct sockaddr_in
{sa_family_t sin_family; /* 地址族协议: AF_INET */in_port_t sin_port; /* 端口, 2字节- 大端 */struct in_addr sin_addr; /* IP地址, 4字节 - 大端 *//* 填充 8字节 */unsigned char sin_zero[sizeof (struct sockaddr) - sizeof(sin_family) -sizeof (in_port_t) - sizeof (struct in_addr)];
};
3.4 套接字函数 使用套接字通信函数需要包含头文件arpa/inet.h包含了这个头文件sys/socket.h就不用在包含了。
// 创建一个套接字
int socket(int domain, int type, int protocol);
参数: domain: 使用的地址族协议
AF_INET: 使用IPv4格式的ip地址AF_INET6: 使用IPv4格式的ip地址
type:
SOCK_STREAM: 使用流式的传输协议SOCK_DGRAM: 使用报式(报文)的传输协议
protocol: 一般写0即可, 使用默认的协议
SOCK_STREAM: 流式传输默认使用的是tcpSOCK_DGRAM: 报式传输默认使用的udp
返回值:
成功: 可用于套接字通信的文件描述符失败: -1 函数的返回值是一个文件描述符通过这个文件描述符可以操作内核中的某一块内存网络通信是基于这个文件描述符来完成的。
// 将文件描述符和本地的IP与端口进行绑定
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd: 监听的文件描述符, 通过socket()调用得到的返回值addr: 传入参数, 要绑定的IP和端口信息需要初始化到这个结构体中IP和端口要转换为网络字节序addrlen: 参数addr指向的内存大小, sizeof(struct sockaddr)
返回值成功返回0失败返回-1
// 给监听的套接字设置监听
int listen(int sockfd, int backlog);
参数:
sockfd: 文件描述符, 可以通过调用socket()得到在监听之前必须要绑定 bind()backlog: 同时能处理的最大连接要求最大值为128
返回值函数调用成功返回0调用失败返回 -1
// 等待并接受客户端的连接请求, 建立新的连接, 会得到一个新的文件描述符(通信的)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd: 监听的文件描述符addr: 传出参数, 里边存储了建立连接的客户端的地址信息addrlen: 传入传出参数用于存储addr指向的内存大小
返回值函数调用成功得到一个文件描述符, 用于和建立连接的这个客户端通信调用失败返回 -1 这个函数是一个阻塞函数当没有新的客户端连接请求的时候该函数阻塞当检测到有新的客户端连接请求时阻塞解除新连接就建立了得到的返回值也是一个文件描述符基于这个文件描述符就可以和客户端通信了。
// 接收数据
ssize_t read(int sockfd, void *buf, size_t size);
ssize_t recv(int sockfd, void *buf, size_t size, int flags);
参数:
sockfd: 用于通信的文件描述符, accept() 函数的返回值buf: 指向一块有效内存, 用于存储接收是数据size: 参数buf指向的内存的容量flags: 特殊的属性, 一般不使用, 指定为 0
返回值:
大于0实际接收的字节数等于0对方断开了连接-1接收数据失败了 如果连接没有断开接收端接收不到数据接收数据的函数会阻塞等待数据到达数据到达后函数解除阻塞开始接收数据当发送端断开连接接收端无法接收到任何数据但是这时候就不会阻塞了函数直接返回0。
// 发送数据的函数
ssize_t write(int fd, const void *buf, size_t len);
ssize_t send(int fd, const void *buf, size_t len, int flags);
参数:
fd: 通信的文件描述符, accept() 函数的返回值buf: 传入参数, 要发送的字符串len: 要发送的字符串的长度flags: 特殊的属性, 一般不使用, 指定为 0
返回值
大于0实际发送的字节数和参数len是相等的-1发送数据失败了
// 成功连接服务器之后, 客户端会自动随机绑定一个端口
// 服务器端调用accept()的函数, 第二个参数存储的就是客户端的IP和端口信息
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd: 通信的文件描述符, 通过调用socket()函数就得到了addr: 存储了要连接的服务器端的地址信息: iP 和 端口这个IP和端口也需要转换为大端然后再赋值addrlen: addr指针指向的内存的大小 sizeof(struct sockaddr)
返回值连接成功返回0连接失败返回-1
4. TCP通信流程
TCP是一个面向连接的安全的流式传输协议这个协议是一个传输层协议。
面向连接是一个双向连接通过三次握手完成断开连接需要通过四次挥手完成。安全tcp通信过程中会对发送的每一数据包都会进行校验, 如果发现数据丢失, 会自动重传流式传输发送端和接收端处理数据的速度数据的量都可以不一致 4.1 服务器端通信流程
1. 创建用于监听的套接字, 这个套接字是一个文件描述符
int lfd socket();
2. 将得到的监听的文件描述符和本地的IP 端口进行绑定
bind();
3. 设置监听(成功之后开始监听, 监听的是客户端的连接)
listen();
4. 等待并接受客户端的连接请求, 建立新的连接, 会得到一个新的文件描述符(通信的)没有新连接请求就阻塞
int cfd accept();
5. 通信读写操作默认都是阻塞的
// 接收数据
read(); / recv();
// 发送数据
write(); / send();
6. 断开连接, 关闭套接字
close();
在tcp的服务器端, 有两类文件描述符
监听的文件描述符
只需要有一个不负责和客户端通信, 负责检测客户端的连接请求, 检测到之后调用accept就可以建立新的连接
通信的文件描述符
负责和建立连接的客户端通信如果有N个客户端和服务器建立了新的连接, 通信的文件描述符就有N个每个客户端和服务器都对应一个通信的文件描述符 文件描述符对应的内存结构
一个文件文件描述符对应两块内存, 一块内存是读缓冲区, 一块内存是写缓冲区读数据: 通过文件描述符将内存中的数据读出, 这块内存称之为读缓冲区写数据: 通过文件描述符将数据写入到某块内存中, 这块内存称之为写缓冲区
监听的文件描述符:
客户端的连接请求会发送到服务器端监听的文件描述符的读缓冲区中读缓冲区中有数据, 说明有新的客户端连接调用accept()函数, 这个函数会检测监听文件描述符的读缓冲区 检测不到数据, 该函数阻塞如果检测到数据, 解除阻塞, 新的连接建立
通信的文件描述符:
客户端和服务器端都有通信的文件描述符发送数据调用函数 write() / send()数据进入到内核中 数据并没有被发送出去, 而是将数据写入到了通信的文件描述符对应的写缓冲区中内核检测到通信的文件描述符写缓冲区中有数据, 内核会将数据发送到网络中接收数据: 调用的函数 read() / recv(), 从内核读数据 数据如何进入到内核程序猿不需要处理, 数据进入到通信的文件描述符的读缓冲区中数据进入到内核, 必须使用通信的文件描述符, 将数据从读缓冲区中读出即可 基于tcp的服务器端通信代码:
// server.c
#include stdio.h
#include stdlib.h
#include unistd.h
#include string.h
#include arpa/inet.hint main()
{// 1. 创建监听的套接字int lfd socket(AF_INET, SOCK_STREAM, 0);if(lfd -1){perror(socket);exit(0);}// 2. 将socket()返回值和本地的IP端口绑定到一起struct sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(10000); // 大端端口// INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址// 这个宏可以代表任意一个IP地址// 这个宏一般用于本地的绑定操作addr.sin_addr.s_addr INADDR_ANY; // 这个宏的值为0 0.0.0.0
// inet_pton(AF_INET, 192.168.237.131, addr.sin_addr.s_addr);int ret bind(lfd, (struct sockaddr*)addr, sizeof(addr));if(ret -1){perror(bind);exit(0);}// 3. 设置监听ret listen(lfd, 128);if(ret -1){perror(listen);exit(0);}// 4. 阻塞等待并接受客户端连接struct sockaddr_in cliaddr;int clilen sizeof(cliaddr);int cfd accept(lfd, (struct sockaddr*)cliaddr, clilen);if(cfd -1){perror(accept);exit(0);}// 打印客户端的地址信息char ip[24] {0};printf(客户端的IP地址: %s, 端口: %d\n,inet_ntop(AF_INET, cliaddr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(cliaddr.sin_port));// 5. 和客户端通信while(1){// 接收数据char buf[1024];memset(buf, 0, sizeof(buf));int len read(cfd, buf, sizeof(buf));if(len 0){printf(客户端say: %s\n, buf);write(cfd, buf, len);}else if(len 0){printf(客户端断开了连接...\n);break;}else{perror(read);break;}}close(cfd);close(lfd);return 0;
}
4.2 客户端通信流程
在单线程的情况下客户端通信的文件描述符有一个, 没有监听的文件描述符
1. 创建一个通信的套接字
int cfd socket();
2. 连接服务器, 需要知道服务器绑定的IP和端口
connect();
3. 通信
// 接收数据
read(); / recv();
// 发送数据
write(); / send();
4. 断开连接, 关闭文件描述符(套接字)
close(); 基于tcp通信的客户端通信代码:
// client.c
#include stdio.h
#include stdlib.h
#include unistd.h
#include string.h
#include arpa/inet.hint main()
{// 1. 创建通信的套接字int fd socket(AF_INET, SOCK_STREAM, 0);if(fd -1){perror(socket);exit(0);}// 2. 连接服务器struct sockaddr_in addr;addr.sin_family AF_INET;addr.sin_port htons(10000); // 大端端口inet_pton(AF_INET, 192.168.237.131, addr.sin_addr.s_addr);int ret connect(fd, (struct sockaddr*)addr, sizeof(addr));if(ret -1){perror(connect);exit(0);}// 3. 和服务器端通信int number 0;while(1){// 发送数据char buf[1024];sprintf(buf, 你好, 服务器...%d\n, number);write(fd, buf, strlen(buf)1);// 接收数据memset(buf, 0, sizeof(buf));int len read(fd, buf, sizeof(buf));if(len 0){printf(服务器say: %s\n, buf);}else if(len 0){printf(服务器断开了连接...\n);break;}else{perror(read);break;}sleep(1); // 每隔1s发送一条数据}close(fd);return 0;
}