TCP/IP 协议:网络世界的基石(2/10)
一、引言
在当今数字化时代,互联网已经成为人们生活中不可或缺的一部分。而在互联网的背后,TCP/IP 协议扮演着至关重要的角色,堪称互联网的基石。
TCP/IP 协议是一组用于数据通信的协议集合,它的名字来源于其中最重要的两个协议:传输控制协议(TCP)和网际协议(IP)。自 20 世纪 70 年代末期以来,TCP/IP 协议已经成为全球互联网通信的通用语言,它定义了数据如何在网络上进行传输和路由。
TCP/IP 协议遵循分层模型,将网络通信分为四个层次:链路层、网络层、传输层和应用层。每层负责不同的功能,通过协同工作,确保数据从源头安全、高效地传输到目的地。
链路层负责在物理媒介上发送和接收数据,如以太网、Wi-Fi 等。网络层负责数据包的路由和转发,确保数据包能够跨越多个网络到达目的地。IP 协议是这一层的核心,它通过给每个数据包分配一个唯一的 IP 地址,确保数据能够正确地路由到目标计算机。
传输层负责提供端到端的数据传输服务。TCP 和 UDP 是这一层的两个主要协议。TCP 是一种面向连接的、可靠的传输层协议,它确保数据包按顺序到达,并且允许接收方确认数据包的接收。UDP 则是一种无连接的、不可靠的传输层协议,它不保证数据包的顺序或完整性,但速度更快,适用于对实时性要求高的应用。
应用层为应用软件提供网络服务,如 HTTP、FTP、SMTP 等。这些协议定义了客户端和服务器之间的通信规则,使得用户能够在互联网上进行各种活动,如浏览网页、发送电子邮件、下载文件等。
总之,TCP/IP 协议是现代互联网通信的基础,它的重要地位不可替代。
二、TCP/IP 协议栈详细解释
(一)什么是计算机网络
计算机网络是根据应用的需要发展而来的,它是将地理位置分散的计算机系统和通信设备连接起来,实现资源共享和信息传递的系统。计算机网络的功能主要表现在硬件资源共享、软件资源共享和用户间信息交换三个方面。
硬件资源共享可以在全网范围内提供对处理资源、存储资源、输入输出资源等昂贵设备的共享,从而使用户节省投资,也便于集中管理和均衡分担负荷。软件资源共享使得互联网上的用户可以远程访问各类大型数据库,可以通过网络下载某些软件到本地机上使用,可以在网络环境下访问一些安装在服务器上的公用网络软件,也可以通过网络登录到远程计算机上使用该计算机上的软件,这样可以避免软件研制上的重复劳动以及数据资源的重复存储,也便于集中管理。用户间信息交换是计算机网络最基本的功能,主要完成计算机网络中各个节点之间的系统通信,用户可以在网上传送电子邮件、发布新闻消息、进行电子购物、电子贸易、远程电子教育等。
(二)TCP/IP 网络世界的规则
- OSI 参考模型具有简化相关的网络操作、提供设备间的兼容性和标准接口、促进标准化工作、结构上可以分隔易于实现和维护等优点。
- TCP/IP 协议栈可以看成是 OSI 参考模型的简化,分为四层:网络接入层、网络层、传输层、应用层。TCP/IP 协议栈与 OSI 参考模型存在一定的对应关系,应用层对应 OSI 模型的应用层、表示层和会话层;传输层对应 OSI 模型的传输层;网络层对应 OSI 模型的网络层;网络接入层对应 OSI 模型的数据链路层和物理层。
- TCP/IP 协议栈每一层的功能如下:
- 网络接入层:位于 TCP/IP 协议栈的最底层,包括各种网络技术,如以太网、Wi-Fi 等,并处理数据帧的传输。它基本上包括了 ISO/OSI 模型中的数据链路层和物理层的所有功能,定义了如何利用网络来传送 IP 数据报,必须知道物理网络的各种细节,以便准确地格式化传输的数据,使其遵守网络规定。
- 网络层:由互连网协议(IP)、互连网控制报文协议(ICMP)和互连网组管理协议(IGMP)等协议组成。IP 是 TCP/IP 的核心,也是网络互连层中最重要的协议,它提供基本的分组传送服务,定义数据报,定义 Internet 地址系统,把数据报分解或重组成易于在网络中传输的结构,在网络存取层和传输层之间传递数据,给远端主机的数据报指定路由,完成数据报的拥挤控制和信息控制。
- 传输层:为应用层提供可靠的或不可靠的端到端服务。TCP(传输控制协议)是一种可靠的、面向连接的、字节流协议,利用端到端错误检测与纠正功能提供可靠的数据传输服务;UDP(用户数据报协议)是一个不可靠的无连接数据报协议,为应用程序提供低开销的无连接数据报传输服务。
- 应用层:位于 TCP/IP 协议栈的顶层,为用户提供各种网络服务,如文件传输、远程登录、电子邮件等,同时包含与具体应用程序相关的所有细节。
(三)TCP/IP 模型的层间通信与数据封装
- 数据包在网络设备之间进行传输的过程中,为了保证数据包准确的发送到目的地,发送端会对数据包进行封装。在发送的数据包上附加 TCP 或者是 UDP 的包头形成数据段(segment),网络层会添加 IP 包头形成数据包(Packet),数据链路层会给数据添加以太网包头和 FCS 包尾,形成数据帧(Frame),最后转换成二进制的比特流通过物理线路传到接收方。这个操作过程就叫做数据封装,而对数据包进行处理时通信双方所遵循和协商好的规则称为协议。
- 接收端收到数据后会进行解封装,从物理层开始,进行与发送端相反的操作,一层层去掉包头,最终使应用层程序获取到数据信息,使得发送方和接收方数据通信完成。
(四)抓包了解数据结构
通过 Wireshark 抓取 HTTP 协议的报文,可以分析传输层、网络层和数据链路层封装的信息。
传输层封装的是 TCP 协议,可以看到源端口号,目标端口号。以访问百度为例,先通过三次握手建立连接,第一次握手:客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK (ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。
网络层封装的是 IP 包头,包括 IPV4 的版本,首部长度,协议类型是 TCP 协议,源 IP 地址,目标 IP 地址等。
数据链路层,协议类型是 OX0800 代表三层使用的是 IPV4 协议,源主机的 MAC 地址,目标主机的 MAC 地址等。
三、TCP/IP 各层功能和重要性
(一)应用层
- 列举应用层的各种协议,如文件传输类(HTTP、FTP、TFTP)、远程登录类(Telnet)、电子邮件类(SMTP)、网络管理类(SNMP)、域名解析类(DNS)等。
-
- 文件传输类:
-
-
- HTTP(超文本传输协议):适用于分布式超媒体信息系统,是 WWW 服务器使用的主要协议,允许用户在统一的界面下,采用不同的协议访问不同服务。
-
-
-
- FTP(文件传输协议):用于简化 IP 网络上系统之间文件传输,用户可以高效从 Internet 上的 FTP 服务器下载大量的数据文件,实现资源共享和传递信息。FTP 包含控制连接和数据连接两种连接模式,控制连接用于传递用户端的命令和服务器端对命令的响应,使用服务器的 21 端口;数据连接用于传输文件和其他数据,如目录列表等,这种连接在需要数据传输时建立,每次使用的端口不一定相同,且数据连接既可能是服务器端发起,也可能是客户端发起。FTP 服务器数据连接有主动模式和被动模式,主动模式从服务器端向客户端发起连接,被动模式是客户端向服务器端发起连接。
-
-
-
- TFTP(简单文件传输协议):是基于 UDP 的应用,对内存和处理器的要求很低,速度快,但功能不如 FTP 丰富,只能从文件服务器获得或写入文件,而不能列出目录,也不能进行认证,没有建立连接的过程及错误恢复的功能,适用范围不如 FTP 广泛。常见的应用例子是使用 TFTP 服务器来备份或恢复 Cisco 路由器、Catalyst 交换机的 IOS 镜像和配置文件。
-
-
- 远程登录类:Telnet,通过一个终端登陆到其他服务器,建立在可靠的传输协议 TCP 之上。但 Telnet 协议所有数据(包括用户名和密码)均以明文形式发送,有潜在的安全风险,如今已被更安全的 SSH 协议所取代。
-
- 电子邮件类:
-
-
- SMTP(简单邮件传输协议):基于 TCP 协议,用来发送电子邮件。当用户发送邮件时,通过 SMTP 协议将邮件交给自己的邮箱服务器,邮箱服务器发现目标邮箱是其他服务器后,使用 SMTP 协议将邮件转发到目标邮箱服务器。
-
-
-
- POP3/IMAP:邮件接收的协议,IMAP 协议相比于 POP3 更新,为用户提供的可选功能更多,几乎所有现代电子邮件客户端和服务器都支持 IMAP。
-
-
- 网络管理类:SNMP(简单网络管理协议),允许第三方的管理系统集中采集来自许多网络设备的数据,为网络管理系统提供了底层网络管理的框架。利用 SNMP,一个管理工作站可以远程管理所有支持这种协议的网络设备,包括监视网络状态、修改网络设备配置、接收网络事件警告等。
-
- 域名解析类:DNS(域名服务协议),基于 UDP,使用端口号 53。在浏览器中输入一个域名后,会有 DNS 服务器将域名解析为对应的 IP 地址。
- 分别介绍这些协议的作用,如 HTTP 是超文本传输协议,用于网页设计和数据传输等。
-
- HTTP:主要是为 Web 浏览器与 Web 服务器之间的通信而设计,当使用浏览器浏览网页时,网页就是通过 HTTP 请求进行加载的。HTTP 协议基于 TCP 协议,发送 HTTP 请求之前首先要建立 TCP 连接,即经历三次握手。目前使用的 HTTP 协议大部分都是 1.1,在 1.1 的协议里,默认开启了 Keep-Alive,建立的连接可以在多次请求中被复用。HTTP 协议是 “无状态” 的协议,无法记录客户端用户的状态,一般通过 Session 来记录客户端用户的状态。
-
- FTP:用于在 IP 网络上系统之间进行文件传输,用户可以高效下载或上传大量数据文件,实现资源共享和信息传递。FTP 服务器可以设置为公用、私有或两者兼之,用户可以为 FTP 帐号定义权限,访问特定区域。
-
- TFTP:基于 UDP 的应用,对内存和处理器要求低,速度快,常用于备份或恢复网络设备的 IOS 镜像和配置文件。
-
- Telnet:通过终端登录到其他服务器,进行远程操作。
-
- SMTP:用于发送电子邮件,将用户写好的邮件交给邮箱服务器,由邮箱服务器转发到目标邮箱服务器。
-
- POP3/IMAP:负责接收电子邮件,IMAP 协议功能更丰富,支持更多可选功能。
-
- SNMP:为网络管理系统提供底层框架,管理工作站可以远程管理支持该协议的网络设备。
-
- DNS:将域名解析为对应的 IP 地址,方便用户使用域名访问网站,而无需记住数字组成的 IP 地址。
(二)运输层
- 介绍运输层的主要协议 TCP 和 UDP 的特点和区别,如 TCP 是面向连接的可靠协议,UDP 是无连接的不可靠协议。
-
- TCP(传输控制协议)是面向连接的协议,需要在传输数据之前建立连接。它是一种可靠的协议,可以保证数据包的可靠传输,确保数据在传输过程中不丢失、不乱序、不重复。当网络出现拥塞或丢包时,TCP 会进行流量控制和拥塞控制,根据网络状况调整发送速率,避免网络拥塞。TCP 的数据包结构相对复杂,包括头部和序列号等字段,采用字节流传输,保证数据按顺序到达接收方。TCP 只能是 1 对 1 的连接,首部较大为 20 字节。TCP 适用于要求数据传输可靠的场景,如文件传输、邮件等。
-
- UDP(用户数据报协议)是无连接的协议,不需要建立连接,可以直接发送数据包。它是不可靠的协议,不保证数据包的可靠传输,可能会出现数据包丢失、重复、乱序等问题。UDP 不进行流量控制和拥塞控制,直接发送数据包,如果网络出现拥塞,UDP 数据包可能会丢失或延迟,甚至导致网络更加拥塞。UDP 的数据包结构相对简单,只包括源端口、目的端口、长度、校验和和数据等字段,采用数据报传输,不保证数据按顺序到达。UDP 适用于实时应用场景,如视频、音频、游戏等,对数据传输的实时性和延迟要求较高。UDP 支持 1 对 1、1 对多的连接。
- 详细解释 TCP 的可靠性实现方式,如三次握手建立连接和改进的三次握手断开连接。
-
- 三次握手建立连接:
- 第一次握手,客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
- 第二次握手,服务器收到 syn 包,必须确认客户的 SYN(ack=j+1),同时自己也发送一个 SYN 包(syn=k),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;
- 第三次握手,客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK (ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。
-
- 改进的三次握手断开连接:当一方想要断开连接时,会发送 FIN 包表示结束数据传输,另一方收到 FIN 包后回复 ACK 确认,然后也发送 FIN 包表示自己也准备断开连接,最后发起方再回复 ACK 确认,完成连接断开。
(三)网络层
- 引入 IP 协议,解释其作用是制定新地址(IP 地址),以便区分两台主机是否同属一个网络。
-
- IP 协议(Internet Protocol)是互联网中最基础的协议之一,负责在网络中寻址和路由数据包,将数据包从源主机传输到目标主机。它定义了数据包的格式、寻址方式和路由规则,是互联网通信的基础。IP 地址就是用来标识网络中的设备的地址,类似于现实生活中的门牌号码,通过 IP 地址可以唯一标识网络中的每个设备。IP 协议负责将数据包分割成适合网络传输的小块(分片),并在目标主机上将这些小块重新组合成完整的数据包(重组),以适应不同网络的传输需求,确保数据能够顺利传输到目标主机。IP 协议采用数据包交换的方式进行数据传输,将数据包从源主机传输到目标主机,而不需要建立专门的物理连接。IP 协议支持不同类型的网络,包括以太网、无线网络、广域网等,使得不同类型的网络能够互相通信和交换数据。
- 介绍网络层的其他协议,如 ARP 协议用于获取目标 MAC 地址,路由协议用于数据包的转发。
-
- ARP(Address Resolution Protocol)协议用于获取目标 MAC 地址。当一个设备要向另一个设备发送数据时,如果只知道目标设备的 IP 地址,就需要通过 ARP 协议来获取目标设备的 MAC 地址。ARP 协议通过广播的方式发送请求,询问目标 IP 地址对应的 MAC 地址,目标设备收到请求后会回复自己的 MAC 地址。
-
- 路由协议用于数据包的转发。路由器根据路由协议来确定数据包的转发路径,将数据包从一个网络转发到另一个网络。常见的路由协议有 OSPF、BGP 等。
(四)网络接口层
- 说明以太网协议对电信号进行分组并形成数据帧的过程。
-
- 当以太网软件从网络层接收到数据报之后,需要根据需要把网际层的数据分解为较小的块,以符合以太网帧数据段的要求。以太网帧的整体大小必须在 64~1518 字节之间(不包含前导码)。有些系统支持更大的帧,最大可以支持 9000 字节。然后把数据块打包成帧,每一帧都包含数据及其他信息,这些信息是以太网网络适配器处理帧所需要的。最后把数据帧传递给对应于 OSI 模型物理层的底层组件,后者把帧转换为比特流,并且通过传输介质发送出去。
- 强调 MAC 地址的唯一性和以太网采用广播形式发送数据包的方式。
-
- MAC 地址是 Media Access Control Address 的简称,由 IEEE 制定的一种网络通信协议,用以确定网络上各节点的位置。MAC 地址是用来标识网络设备唯一性的,它是一个网络层协议,可以用于确定设备通信的传输介质和节点。MAC 地址长度为 48 位,及 6 个字节,一般用 16 进制数字加上冒号的形式来表示。在网卡出厂时就确定了,不能修改,通常是唯一的(虚拟机中的 MAC 地址不是真实的 MAC 地址,可能会冲突;也有些网卡支持用户配置 MAC 地址)。
-
- 以太网采用广播形式发送数据包。当一个设备发送数据时,会将数据帧中的目的 MAC 地址设置为目标设备的 MAC 地址,然后将数据帧发送到网络中。网络中的其他设备接收到数据帧后,会检查其中的目的 MAC 地址。如果目的 MAC 地址与自己的 MAC 地址相匹配,适配器软件就会处理接收到的帧,把数据传递给协议栈中较高的层;如果目的 MAC 地址与自己的 MAC 地址不匹配,设备就会丢弃该数据帧。
四、TCP/IP 协议的实际应用案例
(一)文件传输案例
- 介绍使用 Java 中的 Socket 通信基于 TCP/IP 协议进行文件传输的案例。
Java 中可以使用ServerSocket和Socket类实现基于 TCP/IP 协议的文件传输。服务端创建一个ServerSocket对象,绑定到指定端口,等待客户端的连接请求。客户端创建一个Socket对象,指定服务端的 IP 地址和端口,发出连接请求。连接建立后,客户端通过文件输入流读取本地文件,然后通过Socket的输出流向服务端发送文件数据。服务端通过Socket的输入流接收文件数据,并保存到本地文件中。
- 展示服务端和客户端的代码示例,解释文件传输的过程。
服务端代码:
import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class TCPServer {public static void main(String[] args) throws IOException {// 获取 ServerSocket 对象,提供 TCP 连接服务ServerSocket serverSocket = new ServerSocket(8888);// 等待接收客户端的 TCP 连接申请Socket socket = serverSocket.accept();// 保存客户端发送的文件// 获取 TCP 连接提供的字节输入流InputStream is = socket.getInputStream();// 把数据存入指定文件FileOutputStream fos = new FileOutputStream(new File("D://receivedFile.txt"));byte[] b = new byte[1024];int len;// 读取数据,保存数据while ((len = is.read(b))!= -1) {fos.write(b, 0, len);}// 关闭资源fos.close();is.close();socket.close();serverSocket.close();}}
客户端代码:
import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;public class TCPClient {public static void main(String[] args) throws IOException {// 创建了一个匿名的 InetAddress 独享,创建了一个 socket,进行连接Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 8888);// 创建输出流,用来发送字节流OutputStream os = socket.getOutputStream();// 本身我需要获取到本地的一个文件,所以我需要 inputFileInputStream fis = new FileInputStream(new File("D://originalFile.txt"));// 具体读和写的过程byte[] buffer = new byte[1024];int len;while ((len = fis.read(buffer))!= -1) {// 再次提醒这样是为了防止读取到重复数据os.write(buffer, 0, len);}// 关闭资源fis.close();os.close();socket.close();}}
文件传输过程:首先服务端创建一个ServerSocket对象并绑定到指定端口,然后进入等待状态,等待客户端的连接请求。客户端创建一个Socket对象,指定服务端的 IP 地址和端口,发出连接请求。当服务端接收到客户端的连接请求后,建立连接,服务端通过accept()方法返回一个Socket对象。接着,客户端通过文件输入流读取本地文件,并通过Socket的输出流向服务端发送文件数据。服务端通过Socket的输入流接收文件数据,并保存到本地文件中。
(二)Python 通信案例
- 使用 Python 实现 TCP/IP 协议通信的示例,包括服务器端和客户端的代码。
服务器端代码:
import socketimport threading# 创建一个 socket 对象serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 获取本地主机名host = socket.gethostname()# 设置端口号port = 9999# 绑定端口号serversocket.bind((host, port))# 设置最大连接数,超过后排队serversocket.listen(5)client_list = []# 用于存储所有连接的客户端 socketdef handle_client(clientsocket):data = clientsocket.recv(1024).decode()print(f"收到数据:{data}")clientsocket.send("已收到您的消息".encode())clientsocket.close()client_list.remove(clientsocket)while True:# 建立客户端连接clientsocket, addr = serversocket.accept()print(f"连接地址:{str(addr)}")client_list.append(clientsocket)# 为每个新连接创建一个线程来处理threading.Thread(target=handle_client, args=(clientsocket,)).start()
客户端代码:
import socket# 创建一个 socket 对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 获取本地主机名host = socket.gethostname()# 设置端口号port = 9999# 连接服务,指定主机和端口s.connect((host, port))# 发送数据s.send("Hello, server".encode())# 接收响应数据response = s.recv(1024).decode()print(f"收到响应:{response}")# 关闭连接s.close()
- 扩展功能的介绍,如多用户支持、消息广播、客户端身份验证和持久化连接等。
-
- 多用户支持:服务器现在可以同时处理多个客户端的连接。通过使用线程或异步编程,可以为每个新连接的客户端创建一个独立的处理线程或任务,从而实现多用户支持。
-
- 消息广播:服务器可以将消息广播给所有连接的客户端。可以创建一个函数,遍历所有连接的客户端并发送消息。例如:
def broadcast_message(message):for client in client_list:client.send(message.encode())
- 客户端身份验证:服务器可以对客户端进行身份验证,只有通过验证的客户端才能发送消息。在handle_client函数中添加一个验证步骤,例如要求客户端在连接时发送一个特定的令牌或用户名,然后服务器可以检查该令牌或用户名是否有效。如果验证失败,服务器可以关闭连接或拒绝接收消息。
- 持久化连接:服务器和客户端可以保持连接打开,以便于连续通信。可以使用长轮询或心跳机制来保持连接打开。这意味着服务器和客户端应该定期发送消息以保持连接活动状态。如果服务器或客户端在一段时间内没有收到消息,它们可以认为连接已断开并采取适当的行动。
(三)公共聊天室案例
- 功能说明,如服务端设定客户端连接个数上限,客户端可与服务端单独通信。
公共聊天室的服务端可以设定客户端连接个数上限,当达到上限时,新的客户端连接将被拒绝。客户端可以向服务端发送消息,服务端可以将消息广播给所有连接的客户端,客户端也可以接收服务端广播的消息。同时,客户端可以与服务端单独通信,即客户端发送给服务端的消息只有服务端可以看到,服务端回复的消息也只有发送消息的客户端可以看到。
- 展示服务端和客户端的代码,解释公共聊天室的实现过程。
服务端代码:
// MyServer.cpp : 定义控制台应用程序的入口点。#include "stdafx.h"#include <iostream>#include <stdlib.h>#include <winsock2.h>// 引用头文件#pragma comment(lib,"ws2_32.lib")// 最大连接数#define g_MaxConnect 20int g_Connect = 0;struct sock_params {SOCKET hsock;int nSockIndex;};// 线程实现函数DWORD WINAPI threadpro(LPVOID pParam) {sock_params* sockPam = (sock_params*)pParam;SOCKET hsock = sockPam->hsock;int nSockIndex = sockPam->nSockIndex;char aIndex[4];_itoa_s(nSockIndex, aIndex, 10);char buffer[1024];char sendBuffer[1024];if (hsock!= INVALID_SOCKET) {std::cout << "客户端 " << nSockIndex << " 加入服务器!" << std::endl;}while (1) {// 循环接收发送的内容int num = recv(hsock, buffer, 1024, 0);// 阻塞函数,等待接收内容if (num > 0) {std::cout << "客户端 " << nSockIndex << ": " << buffer << std::endl;memset(sendBuffer, 0, 1024);char strValue[30] = "服务器 ";strcat_s(strValue, aIndex);strcpy_s(sendBuffer, strValue);char strSpace[30] = " 收到数据如下: ";strcat_s(sendBuffer, strSpace);strcat_s(sendBuffer, buffer);int ires = send(hsock, sendBuffer, sizeof(sendBuffer), 0);// 回送消息// cout << "Send to Client: " << sendBuffer << endl;}else {std::cout << "客户端 " << nSockIndex << " 关闭!" << std::endl;// cout << "Server Process " << nSockIndex << " Closed" << endl;return 0;}}return 0;}// 主函数void main() {WSADATA wsd;// 定义 WSADATA 对象WSAStartup(MAKEWORD(2, 2), &wsd);SOCKET m_SockServer;sockaddr_in serveraddr;sockaddr_in serveraddrfrom;SOCKET m_Server[g_MaxConnect];serveraddr.sin_family = AF_INET;// 设置服务器地址serveraddr.sin_port = htons(4600);// 设置端口号serveraddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");m_SockServer = socket(AF_INET, SOCK_STREAM, 0);int nStatus = bind(m_SockServer, (sockaddr*)&serveraddr, sizeof(serveraddr));if (nStatus == 0) {std::cout << "服务端启动成功!" << std::endl;}else {std::cout << "服务端启动失败!" << std::endl;return;}int iLisRet = 0;int len = sizeof(sockaddr);while (1) {iLisRet = listen(m_SockServer, 0);// 进行监听m_Server[g_Connect] = accept(m_SockServer, (sockaddr*)&serveraddrfrom, &len);// 同意连接if (m_Server[g_Connect]!= INVALID_SOCKET) {if (g_Connect > g_MaxConnect - 1) {char WarnBuf[50] = "客户端连接个数:超限!";int ires = send(m_Server[g_Connect], WarnBuf, sizeof(WarnBuf), 0);}else {char cIndex[4];_itoa_s(g_Connect, cIndex, 10);char buf[50] = "你的服务端 ID: ";strcat_s(buf, cIndex);int ires = send(m_Server[g_Connect], buf, sizeof(buf), 0);// 发送字符过去// cout << buf << endl;HANDLE m_Handel;// 线程句柄DWORD nThreadId = 0;// 线程 IDsock_params sockPam;sockPam.hsock = m_Server[g_Connect];sockPam.nSockIndex = g_Connect;m_Handel = (HANDLE)::CreateThread(NULL, 0, threadpro, &sockPam, 0, &nThreadId);CloseHandle(m_Handel);}++g_Connect;}}WSACleanup();}
客户端代码:
// MyClient.cpp : 定义控制台应用程序的入口点。#include "stdafx.h"#include <iostream>#include <stdlib.h>#include <stdio.h>#include "winsock2.h"#include <time.h>#pragma comment(lib,"ws2_32.lib")void main() {WSADATA wsd;// 定义 WSADATA 对象WSAStartup(MAKEWORD(2, 2), &wsd);SOCKET m_SockClient;sockaddr_in clientaddr;clientaddr.sin_family = AF_INET;// 设置服务器地址clientaddr.sin_port = htons(4600);// 设置服务器端口号clientaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");m_SockClient = socket(AF_INET, SOCK_STREAM, 0);if (m_SockClient == INVALID_SOCKET) {printf("Sock 初始化失败: %d\n", WSAGetLastError());WSACleanup();return;}// 获取发送缓冲区和接送缓冲区大小{int optlen = 0;int optval = 0;optlen = sizeof(optval);getsockopt(m_SockClient, SOL_SOCKET, SO_SNDBUF, (char*)&optval, &optlen);printf("send buf len is %d\n", optval);// 64 位 默认发送缓冲区 64kgetsockopt(m_SockClient, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen);printf("Recv buf len is %d\n", optval);// 64 位 默认接收缓冲区 64k}// 设定发送缓冲区大小// optval = 1024 * 2; // setsockopt(ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&optval, optlen);int nSuccess = connect(m_SockClient, (sockaddr*)&clientaddr, sizeof(clientaddr));// 连接超时if (nSuccess == 0) {std::cout << "连接服务器成功!" << std::endl;}else {std::cout << "连接服务器失败!" << std::endl;return;}char buffer[1024];char inBuf[1024];int num = 0;num = recv(m_SockClient, buffer, 1024, 0);// 阻塞if (num > 0) {std::cout << buffer << std::endl;char* pResult = strstr(buffer, "超限");if (pResult!= NULL) {std::cout << "服务器连接个数超限,不可用!" << std::endl;system("pause");return;}while (1) {std::cout << "请输入要发送的消息:" << std::endl;std::cin >> inBuf;if (strcmp(inBuf, "exit") == 0) {send(m_SockClient, inBuf, sizeof(inBuf), 0);// 发送退出指令return;}int send_len = send(m_SockClient, inBuf, sizeof(inBuf), 0);if (send_len < 0) {std::cout << "发送失败!请检查服务器是否开启!" << std::endl;system("pause");return;}int recv_len = recv(m_SockClient, buffer, 1024, 0);// 接收客户端发送过来的数据if (recv_len >= 0) {std::cout << buffer << std::endl;}}}}
公共聊天室的实现过程:服务端首先初始化WSADATA对象,创建一个SOCKET对象并绑定到指定的端口号。然后,服务端进入一个无限循环,监听来自客户端的连接请求。当一个客户端请求连接时,服务端接受请求并创建一个新的SOCKET对象来与该客户端通信。服务端为每个连接的客户端创建一个线程来处理通信,线程函数接收客户端发送的消息,并将消息加上服务器的标识后回送给客户端。如果客户端关闭连接,线程函数退出。服务端还可以设置客户端连接个数上限,当达到上限时,新的客户端连接将被拒绝。客户端首先初始化WSADATA对象,
五、结论
TCP/IP 协议作为互联网的基石,其重要性不言而喻。从分层模型来看,各层功能明确且相互协作,共同确保了数据在网络中的高效、准确传输。
应用层的众多协议为用户提供了丰富多样的网络服务,无论是文件传输、远程登录、电子邮件还是网络管理和域名解析,都极大地满足了人们在互联网时代的各种需求。这些协议的存在使得用户能够轻松地进行各种网络活动,实现资源共享和信息交流。
运输层的 TCP 和 UDP 协议各具特点,满足了不同应用场景的需求。TCP 的可靠性保证了数据传输的准确性和完整性,适用于对数据传输质量要求较高的场景;而 UDP 的高效性则使其在实时性要求高的应用中发挥了重要作用。
网络层的 IP 协议通过制定新地址,确保了两台主机能够准确区分彼此,并实现跨网络的数据传输。ARP 协议和路由协议的协同作用,使得数据包能够顺利地找到目标主机并进行转发。
网络接口层的以太网协议对电信号进行分组并形成数据帧,同时 MAC 地址的唯一性和以太网的广播形式确保了数据在局域网内的正确传输。
TCP/IP 协议的实际应用案例进一步展示了其强大的功能和灵活性。文件传输案例中,Java 和 Python 实现的基于 TCP/IP 协议的文件传输,为用户提供了高效的数据传输方式。Python 通信案例中的多用户支持、消息广播、客户端身份验证和持久化连接等扩展功能,丰富了网络通信的应用场景。公共聊天室案例则展示了 TCP/IP 协议在实现多人实时通信方面的能力。
总之,TCP/IP 协议的重要性不仅在于其作为互联网通信的基础,更在于其各层功能的协同作用和实际应用中的广泛适用性。它为我们的数字生活提供了坚实的支撑,推动了互联网技术的不断发展和进步。
相关文章推荐:
1、计算机网络基础全攻略:探秘网络构建块(1/10)
相关文章:
TCP/IP 协议:网络世界的基石(2/10)
一、引言 在当今数字化时代,互联网已经成为人们生活中不可或缺的一部分。而在互联网的背后,TCP/IP 协议扮演着至关重要的角色,堪称互联网的基石。 TCP/IP 协议是一组用于数据通信的协议集合,它的名字来源于其中最重要的两个协议…...
Lua--1.基础知识
Lua基础知识 变量简单的4种变量类型复杂的4种变量类型type函数 字符串操作长度获取--#多行打印字符串拼接别的类型转字符串-- tostring()字符串提供的公共方法 运算符算术运算符-- - * / % ^条件运算符-- > < > < ~(不等于 是 ~)逻辑运算符-- and or not位运算、…...
【GPT】力量训练的底层原理?
详细解读力量训练的每一个底层原理 力量训练之所以有效,是因为它利用了肌肉、神经系统和生物化学反应的基本机制。以下逐一详细解析,并解释相关概念。 1. 应力-恢复-适应理论 概念解析 应力(Stress):指训练带来的负…...
各种排序算法
前置知识 排序: 按照递增或者递减的顺序把数据排列好 稳定性: 值相等的元素在排序之后前后顺序是否发生了改变 内部排序: 数据放在内存上 外部排序: 数据放在磁盘上 内部排序 基于比较的排序 几大排序算法 1. 堆排序 特点: 思想: 1. 创建大根堆,把所有元素放在大根堆里…...
源码解读笔记:协程的 ViewModel.viewModelScope和LifecycleOwner.lifecycleScope
分析下ViewModel.viewModelScope public val ViewModel.viewModelScope: CoroutineScopeget() {val scope: CoroutineScope? this.getTag(JOB_KEY)if (scope ! null) {return scope}return setTagIfAbsent(JOB_KEY,CloseableCoroutineScope(SupervisorJob() Dispatchers.Ma…...
11.27周三F34-Day8打卡
文章目录 1. 学习让我感觉很棒。(什么关系?动作 or 描述?主语部分是?)解析答案:【解析答案分析】【对比分析】【拓展内容】2. 她忽然想起来钥匙放另一个包里了。解析答案:【拓展内容】3. 她来不来都没关系。(该由什么引导?这句话又属于什么关系,动作 or 描述?)解析答案…...
XG(S)-PON原理
前言 近年来,随着全球范围内接入市场的飞快发展以及全业务运营的快速开展,已有的PON技术标准在带宽需求、业务支撑能力以及接入节点设备和配套设备的性能提升等方面都面临新的升级需求XG(S)-PON(10G GPON)是在已有GPON技术标准上演进的增强下一代GPON技…...
C语言实例之9斐波那契数列实现
1. 斐波那契数列简介 斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多・斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为 “兔子数列”。 它的特点是从第三…...
YOLO系列论文综述(从YOLOv1到YOLOv11)【第1篇:概述物体检测算法发展史、YOLO应用领域、评价指标和NMS】
目录 1 前言2 YOLO在不同领域的应用3 物体检测指标和NMS3.1 mAP和IOU3.2 mAP计算流程3.2.1 VOC 数据集3.2.2 微软 COCO 数据集 3.3 NMS 1 前言 最近在做目标检测模型相关的优化,重新看了一些新的论文,发现了几篇写得比较好的YOLO系列论文综述࿰…...
数据结构--Map和Set
目录 一.二叉搜索树1.1 概念1.2 二叉搜索树的简单实现 二.Map2.1 概念2.2 Map常用方法2.3 Map使用注意点2.4 TreeMap和HashMap的区别2.5 HashMap底层知识点 三.Set3.1 概念3.2 Set常用方法3.3 Set使用注意点3.4 TreeSet与HashSet的区别 四.哈希表4.1 概念4.2 哈希冲突与避免4.3…...
计算机操作系统——进程控制(Linux)
进程控制 进程创建fork()函数fork() 的基本功能fork() 的基本语法fork() 的工作原理fork() 的典型使用示例fork() 的常见问题fork() 和 exec() 结合使用总结 进程终止与$进程终止的本质进程终止的情况正常退出(Exit)由于信号终止非…...
【前端】ES6基础
1.开发工具 vscode地址 :https://code.visualstudio.com/download, 下载对应系统的版本windows一般都是64位的 安装可以自选目录,也可以使用默认目录 插件: 输入 Chinese,中文插件 安装: open in browser,直接右键文件…...
【排序算法 python实现】
排序算法 python实现 / 默写 # 汉诺塔 import copy import randomdef hanuo(n, a, b, c):if n 1:print(f{a} --> {c})returnhanuo(n - 1, a, c, b)print(f{a} --> {c})hanuo(n - 1, b, a, c)hanuo(3, A, B, C)# 冒泡排序 def bubble_sort(arr):n len(arr)for i in ran…...
Java图书管理系统(简易保姆级)
前面学习了这么多知识,为了巩固之前的知识,我们就要写一个图书管理系统来帮助大家复习,让大家的知识融会贯通~~~ 话不多说,直接开始今天的内容~ 首先呢,我们要有一个大体的思路: 实现效果思路有两种情况&a…...
嵌入式硬件设计:从概念到实现的全流程
嵌入式硬件设计是现代电子技术中一个至关重要的领域,涉及从硬件架构设计到硬件调试的各个方面。它为我们日常生活中的各类智能设备、家电、工业控制系统等提供了强大的支持。本文将介绍嵌入式硬件设计的基本流程、关键技术、常用工具以及常见的挑战和解决方案&#…...
第 4 章 Java 并发包中原子操作类原理剖析
原子变量操作类 AtomicLong 是原子性递增或者递减类,其内部使用 Unsafe 来实现,AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的。这里的原子操作类都使用 CAS 非阻塞算法 private static final lon…...
从 0 到 1 掌握部署第一个 Web 应用到 Kubernetes 中
文章目录 前言构建一个 hello world web 应用项目结构项目核心文件启动项目 检查项目是否构建成功 容器化我们的应用编写 Dockerfile构建 docker 镜像推送 docker 镜像仓库 使用 labs.play-with-k8s.com 构建 Kubernetes 集群并部署应用构建 Kubernetes 集群环境编写部署文件 总…...
政安晨【零基础玩转各类开源AI项目】探索Cursor-AI Coder的应用实例
目录 Cusor的主要特点 Cusor实操 政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! Cursor 是 Visual Studio Code 的一个分支。这使我们能够…...
CentOS 7 安装部署 KVM
1.关闭虚拟机 打开相关选项 打开虚拟机centos7 连接xshell 测试网络,现在就是没问题的,因为我们要使用网络源 安装 GNOME 桌面环境 安装KVM 模块 安装KVM 调试工具 构建虚拟机的命令行工具 qemu 组件,创建磁盘、启动虚拟机等 输入这条命令,…...
ArcGIS 10.2软件安装包下载及安装教程!
今日资源:ArcGIS 适用系统:WINDOWS 软件介绍:ArcGIS是一款专业的电子地图信息编辑和开发软件,提供一种快速并且使用简单的方式浏览地理信息,无论是2D还是3D的信息。软件内置多种编辑工具,可以轻松的完成地…...
一个专为云原生环境设计的高性能分布式文件系统
大家好,今天给大家分享一款开源创新的分布式 POSIX 文件系统JuiceFS,旨在解决海量云存储与各类应用平台(如大数据、机器学习、人工智能等)之间高效对接的问题。 项目介绍 JuiceFS 是一款面向云原生设计的高性能分布式文件系统&am…...
基于深度学习CNN算法的花卉分类识别系统01--带数据集-pyqt5UI界面-全套源码
文章目录 基于深度学习算法的花卉分类识别系统一、项目摘要二、项目运行效果三、项目文件介绍四、项目环境配置1、项目环境库2、环境配置视频教程 五、项目系统架构六、项目构建流程1、数据集2、算法网络Mobilenet3、网络模型训练4、训练好的模型预测5、UI界面设计-pyqt56、项目…...
3174、清除数字
3174、[简单] 清除数字 1、题目描述 给你一个字符串 s 。你的任务是重复以下操作删除 所有 数字字符: 删除 第一个数字字符 以及它左边 最近 的 非数字 字符。 请你返回删除所有数字字符以后剩下的字符串。 2、解题思路 遍历字符串: 我们需要逐个遍…...
C++ 优先算法 —— 无重复字符的最长子串(滑动窗口)
目录 题目: 无重复字符的最长子串 1. 题目解析 2. 算法原理 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口(同向双指针) 3. 代码实现 Ⅰ. 暴力枚举 Ⅱ. 滑动窗口 题目: 无重复字符的最长子串 1. 题目解析 题目截图: 此题所说的…...
ADS学习笔记 6. 射频发射机设计
基于ADS2023 update2 更多ADS学习笔记:ADS学习笔记 1. 功率放大器设计ADS学习笔记 2. 低噪声放大器设计ADS学习笔记 3. 功分器设计ADS学习笔记 4. 微带分支定向耦合器设计ADS学习笔记 5. 微带天线设计 -1、射频发射机性能指标 在射频电路和系统中,发送…...
上海乐鑫科技一级代理商飞睿科技,ESP32-C61高性价比WiFi6芯片高性能、大容量
在当今快速发展的物联网市场中,无线连接技术的不断进步对智能设备的性能和能效提出了更高要求。为了满足这一需求,乐鑫科技推出了ESP32-C61——一款高性价比的Wi-Fi 6芯片,旨在为用户设备提供更出色的物联网性能,并满足智能设备连…...
QT QRadioButton控件 全面详解
本系列文章全面的介绍了QT中的57种控件的使用方法以及示例,包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizontalSpacer)、…...
51单片机从入门到精通:理论与实践指南(一)
单片机在智能控制领域的应用已非常普遍,发展也很迅猛,学习和使用单片机的人员越来越多。虽然新型微控制器在不断推出,但51单片机价格低廉、易学易用、性能成熟,在家电和工业控制中有一定的应用,而且学好了51单片机&…...
零基础3分钟快速掌握 ——Linux【终端操作】及【常用指令】Ubuntu
1.为啥使用Linux做嵌入式开发 能广泛支持硬件 内核比较高效稳定 原码开放、软件丰富 能够完善网络通信与文件管理机制 优秀的开发工具 2.什么是Ubuntu 是一个以桌面应用为主的Linux的操作系统, 内核是Linux操作系统, 具有Ubuntu特色的可视…...
C#中面试的常见问题007
1.在EF中实现一个实体对应多个表 1. 表拆分(Table Splitting) 表拆分是指将一个实体映射到两个或多个表中的行。这通常发生在实体的属性分布在不同的表中,但这些表通过外键关联到同一个主表。在EF Core中,可以通过Fluent API来配…...
阿里巴巴的网站建设与维护/seo线上培训班
(一)strcmp (例题:http://blog.csdn.net/yanyanwenmeng/article/details/77434453) strcmp(字符串名1,字符串名2):比较字符串1和字符串2的大小,比较的结果由函数带回; 1. 如果字符串…...
做网站的前途/广州网站优化工具
我在构思这篇文章。我想象自己能够在时机成熟时,把自己的想法都倾诉于纸端。但刚刚过去一个月,我就意识到这并非易事:随着工作的推进,我忘掉了很多自己刚刚学到的东西。这些东西快速内化,使我的大脑开始欺骗自己&#…...
中美网站建设/收录网站查询
C语言编译器报错C语言编译器报错及其解决编译器: TDM-GCC 4.7.1 32-bit执行 gcc.exe...gcc.exe "C:\Users\Administrator\Desktop\test1\2045.c" -o "C:\Users\Administrator\Desktop\test1\2045.exe" -m32 -g3 -I"F:\software(软件安装包)\Dev-cpp\M…...
苏州做网站公司有哪些/淘宝关键词优化技巧
Spring Boot 框架快速入门教程以大量示例讲解了 Spring Boot 在各类情境中的应用,让大家可以跟着老师的思维和代码快速理解并掌握。 适用于 Java 开发人员,尤其是初学 Spring Boot 的人员和需要从传统 Spring 转向 Spring Boot 开发的技术人员。 下边…...
国外乡村建设网站/东莞seo网络优化
Ant Design of React 3.10.9拉取项目 luwei.web.study-ant-design-pro, 切换至 add 分支,可看到 Form 表单实现效果实现一个新增表单思路Create表单:Form.create()表单数据绑定 getFieldDecorator渲染查询表单的查询条件 render 定义表单校验条件 rules设…...
宁波网站开发公司电话/电商最好卖的十大产品
二叉树一般都是和递归有联系的,二叉树的遍历包括了前序,后序,中序,大部分题目只要考虑清楚应该用那种遍历顺序,然后特殊情况的条件,题目就会迎刃而解。 1. 先来说说二叉树的遍历方式 其实二叉树的遍历很简…...