网络编程简介

什么是计算机网络?

计算机网络是将分散在不同地点的计算机设备通过传输介质(如光纤、电缆等)和通信设施(如路由器、交换机等)互连起来的系统。通过网络连接,计算机设备可以进行资源共享和数据传输,包括文件传输、远程登录、电子邮件、网页浏览等。

什么是网络编程?

网络编程就是编写程序,使互联网上的两个或多个设备(如计算机)之间进行数据传输。通过网络编程,可以实现客户端和服务器之间的通信,例如Web应用程序中的前端与后端之间的通信,或者两台计算机上的自定义应用程序之间的通信。

怎样实现网络编程?

  1. 选择编程语言:首先,选择一种适合网络编程的编程语言。常用的语言包括Java、Python、C++等,它们都提供了网络编程相关的库和API。

  2. 确定网络通信协议:根据应用需求,确定使用的网络通信协议,例如TCP/IP协议、HTTP协议等。不同的协议适用于不同的场景,选择与应用需求相匹配的协议。

  3. 建立连接:根据所选的编程语言和协议,使用相关的库或API建立网络连接。通常,客户端和服务器之间的网络连接是通过套接字(Socket)来实现的。客户端通过指定目标主机的IP地址和端口号发起连接请求,服务器则监听指定的端口等待连接请求。

  4. 数据传输:一旦建立了网络连接,客户端和服务器之间可以开始进行数据传输。根据协议的要求,可以使用相应的方法和函数发送和接收数据。例如,对于基于TCP协议的编程,可以使用Socket库提供的send()和recv()函数来发送和接收数据。

  5. 处理通信过程中的其他操作:在网络编程中,还需要处理通信过程中的其他操作,如错误处理、异常情况处理、连接管理、数据解析等。这些操作有助于确保通信的可靠性和安全性。

网络通信协议简介

什么是网络通信?

网络通信是指通过计算机网络进行数据交换和传输的过程。在网络通信中,数据以各种形式(如文本、图像、音频、视频等)从发送方传输到接收方。

什么是网络通信协议?

网络通信协议是指互联网上各个计算机之间,进行网络通信时需要遵循的一套规范

网络通信协议的分层思想

随着计算机网络的发展,网络通信的应用需求也在不断增多,随之而来的是网络通信协议的复杂性提升,为了解决这个问题,分层思想被引入。

  • 分层思想将网络通信划分为多个层次,每个层次都有明确定义的责任和功能。上层的协议可以建立在下层的协议之上,利用下层提供的服务进行通信。不同层次之间通过明确定义的接口进行交互,使得各个层次之间的功能划分更加清晰,且相互独立。
  • 分层结构的松耦合性使得网络通信协议的维护和升级更加容易。当需要引入新的功能或改进现有功能时,只需要对特定的层次进行修改,而无需对整个网络通信系统进行大规模的改动。这种灵活性和可扩展性使得网络通信变得更加简单、清晰,并且便于维护和升级。
  • 常见的分层体系结构有两种:一种是OSI(开放系统互联)参考模型,另一种是TCP/IP(传输控制协议/网络互联协议)模型。

OSI七层参考模型

1985年,国际标准化组织(ISO)颁布了开放系统互联(OSI)参考模型,将网络分成七层。该模型的目的是为了实现网络应用的普及化,让不同的公司遵循统一的规范来控制网络,从而实现网络的互联互通。然而,由于OSI模型过于理想化,未能在因特网上得到广泛推广。在因特网的发展过程中,TCP/IP参考模型逐渐成为了事实上的标准。

模型功能
应用层提供特定的服务和协议,与用户应用程序之间的直接接口,实现特定的应用功能。
表示层提供数据处理(编解码,加解密,压缩解压缩)功能。
会话层管理应用程序之间的会话和数据交换(建立、维护、重连、断开)
传输层提供端到端的可靠数据传输,包括分段、连接管理、流量控制和差错恢复等功能。常见的协议有TCP和UDP。
网络层负责在网络中寻址和路由,将数据从源节点传送到目标节点,包括IP寻址和路由选择等功能。
数据链路层提供可靠的点对点数据传输,负责在相邻节点之间可靠地传输数据帧
物理层负责传输比特流,即将数据转换为二进制位并通过物理介质进行传输,包括物理连接、电信号和传输介质等

TCP/IP五层参考模型

为了便于教学和理解,TCP/IP参考模型将网络分为五个层次,这个模型在实际应用中并不广泛使用

模型功能
应用层对应于OSI的应用层、表示层、会话层,提供各种网络应用服务,例如HTTP、FTP、SMTP等。
传输层对应于OSI的传输层,提供端到端的可靠数据传输,主要使用TCP和UDP协议。
网络层对应于OSI的网络层,负责进行数据包的选路和转发,使用IP协议进行网络寻址。
数据链路层对应于OSI的数据链路层,负责提供可靠的点对点数据传输。
物理层对应于OSI的物理层,负责传输比特流

TCP/IP四层参考模型

为了便于在实际应用中使用,TCP/IP参考模型将网络分为四个层次

模型功能
应用层对应于OSI的应用层、表示层、会话层,提供网络应用服务
传输层对应于OSI的传输层,提供端到端的通信服务,主要使用TCP和UDP协议。
网络层对应于OSI的网络层,负责进行数据包的选路和转发,使用IP协议进行网络寻址。
网络接口层对应于OSI的物理层和数据链路层,利用传输介质为数据链路层提供支持,实现计算机节点之间比特流的透明传送

参考模型间的关系

ISO七层模型、TCP/IP协议五层模型、TCP/IP协议四层模型之间的对应关系

应用层协议 HPPT

HTTP(Hypertext Transfer Protocol)超文本传输协议,是一种用于在Web上进行数据传输的应用层协议。定义了客户端和服务器之间进行通信的规则和格式

HTTP协议特点

  • 状态无关:HTTP协议是一种无状态协议,即每个请求和响应之间都是独立的,服务器不会保留之前请求的任何状态信息。
  • 请求-响应模型:HTTP采用了请求-响应模型。客户端向服务器发送HTTP请求,请求中包含请求方法(如GET、POST等)、URL地址、HTTP版本号、请求头部和可选的请求主体;服务器接收到请求后,返回一个HTTP响应,响应中包含状态码、响应头部和响应主体。
  • URL:URL用于标识互联网上的资源,包括协议类型、主机名、端口号和资源路径等信息。通过URL,客户端可以定位到特定的资源。
  • 请求方法:HTTP定义了多种请求方法,常见的有GET、POST、PUT、DELETE等。不同的方法对应着不同的操作,比如GET方法用于请求获取资源,POST方法用于提交数据等。
  • 状态码:HTTP响应中包含一个状态码,用于表示请求的处理结果。常见的状态码有200表示成功,404表示资源不存在,500表示服务器内部错误等。
  • 头部信息:HTTP请求和响应中可以包含头部信息,用于传递附加的元数据。头部信息包括通用头部、请求头部和响应头部,可以用于指定字符编码、缓存控制、身份验证等。
  • 安全性:HTTP本身是不安全的,数据传输是明文的。但可以通过HTTPS协议进行加密,确保数据的安全传输

HTTP协议的请求

客户端给服务器发送数据,叫Request请求,HTTP请求数据总共分为三部分内容,分别是请求行、请求头、请求体

请求简介
请求行由请求的方式、请求的URL、请求的协议(一般都是HTTP1.1)三部分组成,使用空格隔开
请求头请求头部用来描述客户端的基本信息,从而把客户端相关的信息告知服务器(key:value形式)
请求体请求体中存放提交到服务器的数据,只有POST请求才有请求体,GET请求没有请求体

请求行

请求行主要包含请求方法、URI 、 HTTP协议版本号(一般都是HTTP1.1)

请求方式

请求方式简介
GET从服务器端获取数据
POST将数据保存到服务器端
PUT命令服务器对数据执行更新
DELETE命令服务器删除数据
其他其他请求方式

URI

URL(Uniform Resource Locator):统一资源定位符,表示互联网(Internet)上某一资源的地址。

1
http://www.example.com:8080/index?param1=value1&param2=value2#section1
协议域名(IP地址)端口号路径参数锚点
http/httpswww.example.com8080indexparam1=value1&param2=value2#
URI简介
http/https协议,用于确定客户端和服务器之间的通信规范
www.example.com域名(IP地址),标识网络上的一台计算机的位置。
8080端口号,用来区分是主机上的那个应用。
/index路径,表示要访问的服务器上的具体路径或资源位置
param1=value1&param2=2参数,用于向服务器传递额外的数据。参数通常以键值对的形式出现,用”&”进行分隔。
#section1锚点,指向页面内文档中的特定位置或标记。通过在URL中添加锚点,可以直接定位到指定位置。

HTTP协议与HTTP协议的区别

特征HTTPHTTPS
协议超文本传输协议超文本传输安全协议
安全性通信过程中的数据是明文传输,不提供加密保护使用TLS/SSL加密传输数据,提供通信的机密性和完整性保护
端口号默认使用80端口号默认使用443端口号
证书不需要使用数字证书进行验证需要服务器端使用有效的数字证书来验证身份
加密没有加密使用公钥加密对称密钥,然后使用对称密钥加密通信内容
数据完整性无数据完整性保护使用消息摘要算法(如SHA)确保数据在传输过程中不会被篡改或损坏
性能影响较快,因为不涉及加密和解密操作加密和解密操作会增加处理时间和资源消耗,性能相对较慢
使用场景适用于对信息安全性要求不高的场景适用于对信息安全性要求较高的场景,例如在线支付、网上银行等

IP地址:用于标识网络上的一台计算机的位置,分为:IPv4(2011年初已用尽)和IPv6(新一代协议)

域名:是用于在互联网上标识和定位计算机或服务器的可读性名称。

域名与IP的关系:域名和IP地址之间存在映射关系,通过DNS服务,可以将域名解析为对应的IP地址。

端口号:用来区分是主机上的那个应用,标识正在计算机上运行的进程,不同的进程有不同的端口号,被规定为一个 16 位的整数 0~65535

分类简介
公认端口范围在0~1023之间,被预先定义的服务通信占用。如:FTP默认占用21端口、HTTP默认占用80端口 、HTTPS默认占用443端口
注册端口范围在1024~49151之间,分配给用户进程或应用程序。如:Tomcat默认占用8080端口,MySQL默认占用3306端口
动态/私有端口范围在49152~65535之间,用于临时性的连接,当客户端与服务器进行通信时,动态端口会被临时分配给客户端使用。

HTTP协议版本号

常见的 HTTP 协议版本包括 HTTP/1.0、HTTP/1.1 和 HTTP/2 等,但是一般都是HTTP1.1

请求协议简介
HTTP0.9只有基本的文本GET功能
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法
HTTP2.0未普及,请求/响应首部的定义基本没有改变,但所有首部键必须全部小写,请求行要独立为:method、:scheme、:host、 :path 等键值对

请求头

主要包含与请求相关的元信息,请求头通常包括多个键值对,每个键值对各自表示一个请求属性

请求头简介
Host用来说明主机和端口号
Connection表示客户端与服务器的连接类型
Upgrade-Insecure-Requests表示升级不安全的请求,会在加载HTTP资源时自动替换成HTTPS请求,让浏览器不再显示HTTPS页面中的HTTP请求警报
User-Agent用来说明当前是什么类型的浏览器
Content-Type用来描述发送到服务器的数据格式
Accept用来描述客户端能够接受什么类型的返回内容
Accept-Language指出浏览器可以接受的语言种类
Accepl-Charset指出浏览器可以接受的字符编码
Referer页面跳转来源,表明产生请求的网页来自于哪个URL
Accept-Encoding指出浏览器可以接受的编码方式
Cookie记载和服务器相关的用户信息
Conten-Type指定POST请求中用来表示的内容类型

其中Content-Type用来描述发送到服务器的数据格式,不同的取值有不同的含义,常见的取值如下

常见Content-Type简介
text/htmlHTML格式
text/cssCSS格式
text/javascript application/javascriptJavaScript 格式
text/json application/jsonJSON数据格式
text/plain纯文本格式
text/xmlXML格式
image/gifgif图片格式
image/jpegjpg图片格式
image/pngpng图片格式

请求体

请求体中存放提交到服务器的数据,只有POST请求才有请求体,GET请求没有请求体

HTTP协议的响应

响应消息就是服务器响应给客户端的消息内容, 也叫做响应报文,主要由响应行、响应头部和响应体3个部分组成

响应简介
响应行由HTTP协议 、状态码、状态码的描述文本3个部分组成,使用空格隔开
响应头用来描述服务器的基本信息,响应头部由多行键值对构成,每行的键和值之间用英文的冒号分隔。
响应体服务器返回的数据主体,有可能是各种数据类型 比如:html、css、js、图片、视频等等

响应行

响应行由协议版本、状态码和状态描述组成,格式为:HTTP/1.1 200 OK

响应行简介
协议版本HTTP协议版本号
状态码服务器向客户端返回的响应状态
状态描述对 HTTP 状态码的文字性描述,它通常是英文单词或短语,用于说明状态码所代表的含义

状态码

HTTP 状态码也叫响应码,反映了 web 服务器处理 HTTP 请求状态,每一个响应码都代表了一种服务端反馈的响应状态,标识了本次请求是否成功。

状态码简介
1XX信息类(Information),表示收到 http 请求,正在进行下一步处理,通常是一种瞬间的响应状态
2XX成功类(Successful),表示用户请求被正确接收、理解和处理
3XX重定向类(Redirection),表示没有请求成功,必须采取进一步的动作
4XX客户端错误(Client Error),表示客户端提交的请求包含语法错误或不能正确执行
5XX服务端错误(Server Error),表示服务器不能正确执行一个正确的请求

状态描述

对 HTTP 状态码的文字性描述,它通常是英文单词或短语,用于说明状态码所代表的含义

短语简介
OK请求成功处理
Created请求已经成功被服务器接收并创建了新的资源。
No Content请求已经成功处理,但是没有返回任何内容。
Moved Permanently永久重定向,表示请求的资源已经被永久移到了新的 URL 上
Found临时重定向,表示请求的资源已被临时移动到新的 URL 上。
Bad Request请求有误,服务器无法处理此请求。
Unauthorized未经授权,需要提供身份验证信息以访问资源。
Forbidden已经被授权,但是访问被禁止。
Not Found请求的资源不存在。
Server Error服务器在处理请求时发生了错误。

响应头

HTTP响应头是服务器在响应HTTP请求时发送给客户端的元数据信息,以文本格式出现在响应报文的首部中,由一个或多个键值对组成,每行一个键值对,键和值之间用冒号和空格分隔,包含了HTTP响应的重要信息,如响应状态码、日期、服务器类型、内容类型等

名称功能
Connection响应完是保持链接还是关闭链接
Content-Encoding告诉浏览器数据采用的压缩格式
Content-Type回送数据的类型
Date返回响应的时间
Server告诉浏览器服务器的类型
Transfer-Encoding告诉浏览器数据的传送格式
vary用于列出一个响应字段列表,告诉缓存服务器遇到同一个 URL 对应着不同版本文档的情况时,如何缓存和筛选合适的版本
Location配合302状态码使用,告诉用户端找谁
Content-Length告诉浏览器回送数据的长度
Content-Language告诉服务器的语言环境
Last-Modified告诉浏览器当前资源的缓存时间
Refresh告诉浏览器隔多长时间刷新一次
Content-Disposition告诉浏览器以下载的方式打开数据
ETag与缓存相关的头

响应体

用于传输服务器处理结果或者请求所需要的资源内容

  • 请求的资源内容:例如HTML页面、图片、视频、音频等。
  • 服务器处理结果:例如API返回的JSON数据、XML数据等

传输层协议 TCP 和 UDP

TCP(传输控制协议)

TCP是一种面向连接的基于字节流的传输层通信协议,提供了可靠的数据传输,保证数据的正确到达和顺序交付,适用于对数据准确性要求较高的应用,如文件传输、发送邮件、浏览网页等等

TCP主要特点

  • 可靠性:TCP提供可靠的数据传输机制。使用确认和重传机制来确保数据的完整性和正确性。接收方会对每个收到的数据报发送确认消息,发送方在一定时间内未收到确认消息时会重传数据。
  • 面向连接:TCP通过建立面向连接的通信会话来进行数据传输。使用TCP协议进行数据通信,必须先经过三次报文握手建立连接才能进行数据传输,传输结束后需要四次报文挥手释放连接
  • 流量控制:TCP利用滑动窗口机制进行流量控制,确保发送方与接收方之间的数据传输速率适应网络的状况,避免因发送速度过快而导致接收方缓冲区溢出。
  • 拥塞控制:TCP通过拥塞控制算法来调整发送速率,以避免网络拥塞。当网络出现拥塞时,TCP会降低发送速率以减轻网络负载,提高整体的网络性能。
  • 面向字节流:TCP将数据划分为字节流进行传输,不关心数据单位的界限。发送方将数据划分为合适的数据块进行发送,接收方则根据接收到的字节流进行数据的组装和重组。
  • 全双工通信:TCP支持全双工通信,即发送方和接收方可以同时进行数据传输。这使得双方可以在同一时间内进行发送和接收操作,提高了传输效率。

TCP报文段首部

一个TCP报文段由首部和数据载荷两部分构成,TCP的功能都体现在首部中各字段中,组成如下:

  • 源端口:16位字段,用于标识发送方使用的端口号(数据从哪个端口发送来)
  • 目的端口:16位字段,用于标识接收方使用的端口号(数据发送到哪个端口去)
  • 序号:32位字段,表示发送方设置的序列号,用于标识发送的字节流的顺序。
  • 确认号:32位字段,表示接收方期望接收的下一个字节的序列号
  • 数据偏移:4位字段,用于指示TCP头部的长度,以32位字(4字节)进行计算
  • 保留:6位字段,保留供将来使用,目前置为0
  • 标志位:6位字段,用于控制TCP连接和数据传输的一组标志位:URG(紧急)、ACK(确认)、PSH(推送)、RST(复位)、SYN(同步)、FIN(结束)
  • 窗口:16位字段,表示发送方当前可以接收的数据量,用于流量控制的机制
  • 校验和:16位字段,用于检测TCP报文是否在传输过程中发生了错误。
  • 紧急指针:16位字段,仅在紧急数据传输时使用,指示紧急数据的末尾位置
  • 选项:可选的TCP头部扩展,用于支持特定的功能或实现特定的要求
  • 填充字段:用于填充TCP头部,以确保头部长度为以32位字(4字节)的倍数,即确保报文段首部能被4整除

TCP三次握手(建立连接)

TCP是一种面向连接的协议,它要求在传输数据之前必须先建立连接。而建立连接的过程就是三次握手,在这个过程中,客户端和服务器会互相发送三个包,以确认彼此的通信能力和准备好后续的可靠传输。

  1. 客户端发送SYN报文:客户端向服务端发送一个同步报文(SYN),请求建立连接
  2. 服务端发送SYN-ACK报文:服务端收到客户端的同步报文(SYN)后,如果同意建立连接,则向客户端发送一个同步-确认报文(SYN-ACK)
  3. 客户端发送ACK报文:客户端收到服务端的同步-确认报文(SYN-ACK)后,向服务端发送一个确认报文(ACK),连接建立完毕。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1)我们将TCP建立连接的过程比喻成握手,握手需要在TCP客户端和服务器直接交换三个TCP报文段

2)最初,客户端和服务器都处于关闭状态(CLOSE)

3)服务器进程:创建传输控制块(TCB),用来存储TCP连接中的一些重要信息
此时,服务器进入 监听状态(LISTEN),时刻准备接受客户端进程的连接请求
TCP服务器进程不是主动发起请求,而是被动等待来自TCP客户端进程的连接请求

4)客户端进程:先创建传输控制块(TCB),然后向 服务器 发出同步报文(SYN),请求建立连接(连接建立是由客户端主动发起的,被称为主动打开连接)
此时,客户端进程进入 同步已发送状态(SYN-SENT)
报文首部中的 同步标志位(SYN)设置为1,用来在TCP连接建立时同步序号,表示这是一个连接请求
报文首部中的 序号字段(seq)设置为x,作为TCP客户进程所选择的初始序号
TCP规定,SYN=1 的报文段不能携带数据,但需要消耗掉一个序号

5)服务器进程,收到同步报文(SYN)之后,如果同意连接则向客户端发送一个同步-确认报文(SYN-ACK),
此时,服务器进入 同步已接收状态(SYN-RCVD)
报文首部中的 同步标志位(SYN)和 确认标志位(ACK)都设置为1,表明这是一个TCP连接请求确认报文段
报文首部中的 序号字段(seq)设置为y,作为TCP服务器进程所选择的初始序号
报文首部中的 确认号字段(ack)设置为x+1,这是对TCP客户进程所选择的初始序号的确认
由于报文首部中的 SYN=1,所以这个报文也不能携带数据,但是同样要消耗一个序号
ACK是TCP头部的一个标志位,表示确认报文段中的确认号字段是否有效。
ack是TCP报文段中的确认号字段,表示接收方期望下一次接收的字节位置。

6)客户端进程,收到同步-确认报文(SYN-ACK)之后
先将自己的状态会从 同步已发送状态(SYN-SENT)变成 已建立连接状态(ESTABLISHED)
然后向服务端发送一个 确认报文(ACK),表示连接通道已经建立成功,可以发送数据了
报文首部中的 确认标志位(ACK)设置为1,表明这是一个普通的TCP确认报文段
报文首部中的 序号字段(seq)设置为x+1,因为TCP客户端进程发送的第一个TCP报文段的序号为x,并且不携带数据
报文首部中的 确认号字段(ack)设置为y+1,这是TCP服务器进程所选择的初始序号的确认
TCP规定,普通的TCP确认报文段可以携带数据,但如果不携带数据,则不消耗序号,此时发送的下一个数据报文序号仍为x+1

7)服务器进程收到 确认报文(ACK)后,也进入 已建立连接状态(ESTABLISHED)
此时双方都进入了 已建立连接状态(ESTABLISHED),可以基于已建立好的TCP连接,进行可靠的数据传输

TCP四次挥手(断开连接)

TCP连接的释放也称为四次挥手,连接的释放必须是一方主动释放,另一方被动释放

  1. 客户端发送FIN报文:当客户端确定不再发送数据时,会向服务器发送一个结束报文(FIN),表示要关闭连接。
  2. 服务器发送ACK报文:服务器接收到客户端的结束报文(FIN)后,会发送一个确认报文(ACK)给客户端,表示收到了关闭请求。
  3. 服务器发送FIN报文:服务器也向客户端发送一个结束报文(FIN),表示服务器也准备关闭连接。
  4. 客户端发送ACK报文:客户端接收到服务器的结束报文(FIN)后,会发送一个确认报文(ACK)给服务器,表示收到了关闭请求的确认。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1)第一次挥手:
主动断开方(可以是客户端或服务器端)发送一个结束报文(FIN)给对方。
发送完成后,主动断开方 进入 终止等待1状态(FIN_WAIT_1),表示没有数据要发送给对方,准备关闭连接。
报文首部中的 结束标志位(FIN)设置为1,通知对方关闭连接。
报文首部中的 确认标志位(ACK)设置为v,v是TCP连接中已收到的数据的最后一个字节的序号加1
报文首部中的 序号字段(seq)设置为u,u是TCP连接中已传送的数据的最后一个字节的序号加1
TCP规定, FIN=1的报文段即使不携带数据,也要消耗掉一个序号

2)第二次挥手:
被动断开方收到主动断开方发送的 结束报文(FIN)后,发送一个 确认报文(ACK)并结束连接已建立状态(ESTABLISHED),进入关闭等待状态(CLOSE-WAIT
主动断开方在收到确认报文(ACK)后,由终止等待1状态(FIN_WAIT_1)转换成终止等待2状态(FIN_WAIT_2)
在主动断开方 发送完成结束报文(FIN)后,就表示已经没有数据要发送了,但被动断开方 如果有数据未发送完毕,可以继续发送剩余的数据,主动断开方依然会接受
这个状态要持续一段时间,也就是整个 关闭等待状态(CLOSE-WAIT)持续的时间。
报文首部中的 确认标志位(ACK)设置为1,表示这是一个普通的TCP确认报文段。
报文首部中的 序号字段(seq)设置为v,v是TCP连接中已传送的数据的最后一个字节的序号加1
报文首部中的 确认号字段(ack)设置为u+1,表示对TCP连接释放报文段的确认。

3)第三次挥手:
被动断开方在发送完剩余的数据后,或关闭等待状态(CLOSE-WAIT)时间截止后
发送 结束报文(FIN)给主动断开方,表示数据已发送完毕,并进入 最后确认状态(LAST_ACK)。
报文首部中的 确认标志位(ACK)和 结束标志位(FIN)都设置为1,表示这是一个TCP连接释放报文段,并对之前收到的报文进行确认。
报文首部中的 序号字段(seq)设置为w,因为TCP在半关闭状态下可能还发送了一些数据。
报文首部中的 确认号字段(ack)设置为u+1,表示对之前收到的TCP连接释放报文段的重复确认。

4)第四次挥手:
主动断开方 收到断开响应报文后,向被动断开方发送 确认报文(ACK),然后进入 时间等待状态(TIME_WAIT),等待超时后最终关闭连接。
被动断开方 收到主动断开方的 确认报文(ACK)后,关闭连接并进入关闭状态(CLOSE)自己什么也不管了。
处于时间等待状态(TIME_WAIT)的主动断开方,在等待完成2MSL的时间内没有收到其他报文,则证明对方已正常关闭,关闭连接进入关闭状态(CLOSE)。
报文首部中的 确认标志位(ACK)设置为1,表明这是一个普通的TCP确认报文段
报文首部中的 序号字段(seq)设置为u+1,因为之前发送的TCP连接释放报文段虽然不携带数据,但需要消耗一个序号。
报文首部中的 确认号字段(ack)设置为w+1,表示对所收到的TCP连接释放报文段的确认。
MSL意思为最长报文段寿命,RFC793建议为2分钟,但对于现在的网络,两分钟时间太长了,因此TCP允许根据具体情况使用更小的MSL值

相关问题

为什么是三次握手呢?而不是两次、四次?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(1)主要是为了防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

(2)如果采用的是两次握手建立连接
假设当客户端发送 同步报文(SYN)请求连接后,由于网络延迟或其他原因,同步报文(SYN)在传输过程中丢失
客户端长时间没收到 同步-确认报文(SYN-ACK),于是再次发送连接请求,并经过两次握手成功完成了连接、传输数据、关闭连接。
因为网络通畅了或其他原因,第一次发送的 同步报文(SYN)最终又到达了服务器,服务器会发出同步-确认报文(SYN-ACK),新的连接建立了
第一次的 同步报文(SYN)本应该是失效的,但是两次握手的机制会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。

(3)如果采用的是三次握手建立连接
就算第一次的 同步报文(SYN)传送过来了,服务端接收到了那条失效报文并且回复了 同步-确认报文(SYN-ACK)
客户端收到服务端的同步-确认报文(SYN-ACK)后,不会直接建立连接,而是会继续发送 确认报文(ACK)给服务端,以确保双方建立起可靠的连接
服务端接收后,会判断 确认报文(ACK)是否合法,如果不合法(即该连接请求已失效),服务端会发送 复位报文(RST)给客户端,表示连接失败并关闭连接。

(4)如果采用的是四次握手建立连接
四次握手理论上是可行的,但是多余的一次握手并不是必要的,三次握手已经足够满足建立连接的需求。

为什么关闭连接的需要四次挥手,而建立连接却只要三次握手呢?

1
2
3
4
5
1)建立连接时,被动方服务器端结束CLOSED阶段进入“握手”阶段并不需要任何准备,可以直接返回SYN和ACK报文,开始建立连接。

2)释放连接时,被动方服务器,突然收到主动方客户端释放连接的请求时并不能立即释放连接,
因为还有必要的数据需要处理,所以服务器先返回ACK确认收到报文,
经过CLOSE-WAIT阶段准备好释放连接之后,才能返回FIN释放连接报文。

为什么主动断开方在时间等待状态(TIME_WAIT)必须等待2MSL的时间?

原因之一:主动断开方等待2MSL的时间,是为了确保两端都能最终关闭。

1
2
3
4
5
6
7
8
9
10
1)假设网络是不可靠的,被动断开方发送报文后,其主动方的响应报文有可能丢失

2)如果主动断开方在发送完响应报文后,不是进入时间等待状态(TIME_WAIT)去等待2MSL时间,而是立即释放连接,
则将无法收到被动方重传的报文,所以不会再发送一次确认报文,
此时处于最后确认状态(LAST_ACK)的被动断开方,无法正常进入到关闭状态(CLOSE),
在这种场景下,被动断开方会超时重传断开响应报文

3)如果主动断开方在2MSL时间内,收到这个重传的报文,会重传一次报文后再一次重新启动2MSL计时等待,
这样,就能确保被动断开方能收到ACK报文,从而能确保被动方顺利进入到CLOSED状态。
只有这样,双方都能够确保关闭

原因之二:防止“旧连接的已失效的数据报文”出现在新连接中。

1
2
3
4
5
(1)主动断开方在发送完最后一个ACK报文后,再经过2MSL才能最终关闭和释放端口,
这就意味着相同端口的新TCP新连接,需要在2MSL的时间之后,才能够正常的建立。

(2)2MSL这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,
从而确保了下一个新的连接中不会出现这种旧连接请求报文。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,客户端端如果出现故障,服务端不能一直等下去,这样会浪费系统资源。

1
2
3
4
5
6
(1)TCP服务器进程每收到一次TCP客户进程的数据,就重新设置并启动保活计时器,通常设置为2小时。
如果觉得保活计时器的两个多小时的间隔太长,可以自行调整TCP连接的保活参数。

(2)若2小时还没有收到客户进程的任何数据(超时),TCP服务端就会发送一个探测报文段,以后每隔75秒钟发送一次。

(3)若一连发送10个探测报文仍然没反应,服务端就认为客户端出了故障,接着就关闭连接。

UDP(用户数据报协议)

UDP是一种无连接的协议,提供不可靠的数据传输,不能保证数据的正确到达和顺序交付,传输数据可能产生丢包,但传输效率高。适用于对实时性要求较高、数据丢失可以容忍的应用,如语音通话,视频直播等

UDP主要特点

  • 无连接性:UDP是一种无连接的传输协议,发送数据之前不需要建立连接。每个UDP数据报都是独立的,独立于其他数据报,因此没有顺序要求和依赖关系。
  • 不可靠性:UDP不提供可靠交付机制,数据报可能会在传输过程中丢失、重复、乱序。UDP不使用确认和重传机制,发送方无法获知数据是否成功传送给接收方
  • 轻量级:UDP协议头部相对较小,只包含必要的信息,没有复杂的控制字段。这使得UDP的开销比TCP更小,传输效率更高。
  • 高传输速度:由于没有流量控制和拥塞控制机制,UDP的传输速度较快。适用于实时应用和对传输速度要求较高的场景,如音频、视频流传输等。
  • 支持广播和多播:UDP支持将数据广播到网络中的多个接收者,也支持将数据发送到多个目的地组的接收者。这使得UDP在一对多通信和多对多通信中具有优势。
  • 适用于单向通信场景:UDP适用于单向通信场景,其中不需要接收方发回响应。例如,DNS查询、日志收集等场景常常使用UDP传输。

UDP报文段首部

  • 源端口:16位字段,用于标识发送方应用程序的端口号。

  • 目的端口:16位字段,用于标识接收方应用程序的端口号。

  • 长度:16位字段,指示UDP报文段的总长度,包括首部和数据载荷。

  • 校验和:16位字段,用于检测UDP报文段在传输过程中是否发生了错误。

  • 数据:存储了需要传输的数据,占据了UDP报文段的剩余部分。

TCP和UDP的对比

对比TCP传输控制协议UDP用户数据报协议
连接方式无连接面向连接
通信方式支持单播(一对一)、多播(一对多)、广播(一对全)仅支持单播(一对一)
报文处理对应用层交付的报文直接打包面向字节流
是否可靠不可靠,不使用流量控制和拥塞控制可靠,使用流量控制和拥塞控制
首部大小仅8字节,首部开销小20~60字节

Socket网络通信

网络通信有什么要素?

  1. IP地址(IP Address):IP地址是用于标识网络中设备的唯一地址。它由32位或128位二进制数字组成,用点分十进制或冒号分隔的形式表示。IP地址用于确定消息的源地址和目的地址,以便数据包能够正确地路由到目标设备。
  2. 端口号(Port Number):端口号用于标识在一个设备上运行的具体应用程序或服务。它是一个16位的数字,范围从0到65535。通过端口号,数据包可以被正确地发送到目标设备上的特定应用程序。
  3. 网络通信协议(Protocol):协议是指在网络通信中约定的规则和标准,用于确保数据在发送和接收之间的正确传输。常见的网络协议包括TCP(Transmission Control Protocol)和UDP(User Datagram Protocol),它们提供了不同的传输方式和特性。

什么是Socket?

Socket(套接字)是计算机网络编程中的一种抽象概念,可以看作是应用程序与网络协议之间的桥梁,使得应用程序能够通过网络协议(如TCP或UDP)与远程主机上的应用程序进行双向通信。

Socket(套接字)包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

  1. 连接使用的协议:如TCP或UDP。
  2. 本地主机的IP地址:指示本地主机在网络中的唯一标识。
  3. 本地进程的协议端口:用于标识本地主机上正在运行的特定进程。
  4. 远程主机的IP地址:指示远程主机在网络中的唯一标识。
  5. 远程进程的协议端口:用于标识远程主机上正在运行的特定进程。

每个Socket都与一个特定的IP地址和端口号相关联,这样可以在网络中准确地定位对应的程序或进程。Socket可以被理解为客户端与服务端之间进行通信的中间层工具,通过Socket可以在客户端和服务器之间建立一条双向通信的通道,实现应用程序之间的数据交换。

  • 客户端套接字:客户端套接字用于主动发起连接,并向服务器发送请求数据。客户端首先创建一个Socket对象,然后使用该Socket对象连接到服务器的IP地址和端口号。一旦连接建立,客户端可以向服务器发送请求数据,并等待服务器的响应数据或来自服务器的其他数据。
  • 服务器套接字:服务器套接字用于监听指定的端口,并等待客户端的连接请求。服务器首先创建一个Socket对象,并将其绑定到特定的IP地址和端口号。然后,服务器套接字开始监听连接请求。一旦有客户端发起连接请求,服务器套接字接受连接,并为每个连接创建一个新的Socket对象。通过这个新的Socket对象,服务器可以接收客户端发送的请求数据,并根据请求作出响应。

Socket与TCP/IP的关系?

TCP/IP协议簇是指一组与互联网通信相关的网络协议集合,最常见的有IP(Internet协议)、TCP(传输控制协议)、UDP(用户数据报协议)等。

  • IP(Internet协议):IP协议是互联网上最基础的协议之一,它负责在网络中传输数据包。IP协议定义了数据包的格式和寻址方式,并且通过路由器将数据包从源地址传输到目标地址。
  • TCP(传输控制协议):TCP协议是建立在IP协议之上的一种可靠的传输协议。它提供了面向连接的、可靠的数据传输服务,确保数据的有序传输、无差错接收和流量控制。
  • UDP(用户数据报协议):UDP协议也是建立在IP协议之上的传输协议,但相比于TCP,它是一种无连接的协议。UDP提供了一种简单的、无需建立连接的数据传输服务,适用于对实时性要求较高、但不需要确保可靠性的应用场景。

Socket(套接字)对常见TCP/IP协议簇进行了封装,如TCP(传输控制协议)和UDP(用户数据报协议)等进行了封装和处理,隐藏了底层协议的细节,简化了网络通信的复杂性,提供了一组函数和接口来实现应用程序之间的通信功能。

什么是Socket编程?

Socket编程是指开发人员利用Socket接口的API创建Socket对象,并通过Socket对象与其他设备或程序建立连接,实现数据的发送和接收(网络通信)的过程。

Socket API通常是由操作系统提供一组函数或方法,使得应用程序可以通过Socket对象进行各种网络操作,如绑定、监听、接受连接、建立连接、发送和接收数据等,Socket编程可以使用多种编程语言和库来实现,不同的编程语言和库可能在语法和函数/方法调用上有些许差异,但是整体的流程和操作步骤是类似的。一般常见的函数如下:

方法描述
socket()通用,创建一个Socket对象
bind()通用,将Socket对象绑定到特定的IP地址和端口号
close()通用,关闭Socket连接并释放资源
listen()TCP专用,监听连接请求(仅在服务器端使用)
accept()TCP专用,接受客户端的连接请求,并返回一个新的Socket对象,用于与客户端进行通信(仅在服务器端使用)
connect()TCP专用,发起与服务器端的连接请求(仅客户端使用)
send()TCP专用,发送数据
recv()TCP专用,接收数据
sendto()UDP专用,向指定目标IP地址和端口号发送数据包
recvfrom()UDP专用,从指定源IP地址和端口号接收数据包

TCP Socket编程基本流程

通过Socket API,可以使用TCP Socket来建立可靠的、面向连接的数据传输,保证数据按照发送顺序到达目的地,且数据不会丢失或重复。

客户端:创建Socket对象,向服务器发送数据,读取服务器的响应数据。

  1. 创建Socket对象:使用Socket API提供的socket()函数创建一个Socket对象,可以是TCP Socket或UDP Socket。
  2. 绑定Socket对象到特定IP地址和端口号:使用bind()函数将Socket对象绑定到指定的IP地址和端口号,用于接收和发送数据。
  3. 尝试建立连接(仅客户端):客户端Socket对象使用connect()函数指定服务器的IP地址和端口号,发起与服务器端的连接请求。
  4. 建立连接:服务器接受客户端的连接请求,两端建立起连接。
  5. 数据传输:一旦连接建立,进行数据的传输,通过Socket对象使用send()函数向对方发送数据,使用recv()函数接收对方发送的数据。
  6. 关闭连接:通信结束后,应用程序使用close()函数,终止Socket连接并释放相关资源。

服务器端:创建Socket对象,读取客户端发送的数据,向客户端发送响应数据

  1. 创建Socket对象:使用Socket API提供的socket()函数创建一个Socket对象,可以是TCP Socket或UDP Socket。
  2. 绑定Socket对象到特定IP地址和端口号:使用bind()函数将Socket对象绑定到指定的IP地址和端口号,用于接收和发送数据。
  3. 监听连接请求(仅服务器端):服务器端Socket对象使用listen()函数开始监听来自客户端的连接请求,等待客户端的连接。
  4. 接受连接请求(仅服务器端):当有客户端发起连接请求时,服务器端使用accept()函数接受来自客户端的连接请求,并为每个连接创建一个新的Socket对象返回以进行通信。
  5. 数据传输:一旦连接建立,进行数据的传输,通过Socket对象使用recv()函数接收对方发送的数据,使用send()函数向对方发送数据。
  6. 关闭连接:通信结束后,应用程序使用close()函数,终止Socket连接并释放相关资源。

UDP Socket编程基本流程

基于UDP协议,提供无连接、不可靠的数据传输。它允许以较低的延迟发送独立的数据包,但不能保证数据的可靠性和顺序,可能丢失部分数据。

通过Socket API,可以使用UDP Socket进行无连接、不可靠的数据传输,以较低的延迟发送独立的数据包,不保证数据的可靠性和顺序,可能丢失部分数据

客户端:创建Socket对象,发送数据包到服务器,接收服务器的响应数据包。

  1. 创建Socket对象:使用Socket API提供的socket()函数创建一个UDP Socket对象。
  2. 数据传输:通过Socket对象使用sendto()函数向服务器发送数据包,使用recvfrom()函数从服务器接收数据包。
  3. 关闭Socket对象:通信结束后,应用程序使用close()函数或相应的方法关闭Socket对象。

服务器端:创建Socket对象,接收客户端发送的数据包,向客户端发送响应数据包。

  1. 创建Socket对象:使用Socket API提供的socket()函数创建一个UDP Socket对象。
  2. 绑定Socket对象到特定IP地址和端口号:使用bind()函数将Socket对象绑定到指定的IP地址和端口号,用于接收和发送数据包。
  3. 数据传输:通过Socket对象使用recvfrom()函数接收来自客户端的数据包,使用sendto()函数向客户端发送响应数据包。
  4. 关闭Socket对象:通信结束后,应用程序使用close()函数或相应的方法关闭Socket对象。

Java中的Socket编程

在Java中,java.net 包对底层操作系统提供的Socket API进行了封装和抽象,提供了一些类和接口来进行网络编程,使得应用程序可以通过网络与其他应用程序进行通信。主要有两个核心操作类:Socket类和ServerSocket类。

Socket类

java.net.Socket类用于实现客户端套接字(Socket)。提供了一种与服务器进行通信的机制,通过TCP协议建立稳定可靠的连接,并可以在客户端和服务器之间进行数据的发送和接收。

方法说明
Socket(String host, int port)创建一个新的套接字并指定远程主机的地址和端口号。
void connect(SocketAddress endpoint)连接到套接字的指定远程地址。
InputStream getInputStream()返回套接字的输入流,用于从套接字读取数据。
OutputStream getOutputStream()返回套接字的输出流,用于向套接字写入数据。
void close()关闭套接字连接及其相关的流,并释放与套接字关联的所有资源。
boolean isConnected()检查套接字是否已连接。
boolean isClosed()检查套接字是否已关闭。
InetAddress getInetAddress()获取远程主机的IP地址。
int getPort()获取远程主机的端口号。
SocketAddress getRemoteSocketAddress()获取套接字连接的远程地址。

ServerSocket类

java.net.ServerSocket类用于实现服务器端套接字(Socket)。ServerSocket基于TCP协议,负责监听并接受客户端的连接请求,并为每个客户端创建一个新的Socket对象来进行通信。

方法说明
ServerSocket(int port)创建一个绑定到特定端口的服务器套接字。
Socket accept()侦听并接受到此套接字的连接。
void close()关闭服务器套接字。
boolean isClosed()检查服务器套接字是否已关闭。
InetAddress getInetAddress()获取服务器套接字绑定的本地IP地址。
int getLocalPort()获取服务器套接字绑定的本地端口号。
void setSoTimeout(int timeout)设置通过accept()方法等待客户端连接的超时时间。
void setReuseAddress(boolean on)设置是否允许地址复用。
int getSoTimeout()获取通过accept()方法等待客户端连接的超时时间。
boolean getReuseAddress()检查是否允许地址复用。
String toString()返回服务器套接字的字符串表示形式。

DatagramSocket类

java.net.DatagramSocket类用于实现基于UDP(用户数据报协议)的网络通信。可以创建一个数据报套接字,用于发送和接收数据包

方法说明
DatagramSocket()创建一个未绑定到任何特定本地地址和端口的数据报套接字。
DatagramSocket(int port)创建一个绑定到指定本地端口的数据报套接字。
void send(DatagramPacket packet)发送数据报包。
void receive(DatagramPacket packet)接收数据报包。
void close()关闭数据报套接字。
void connect(SocketAddress endpoint)连接到远程套接字地址。
void disconnect()断开与远程套接字的连接。
InetAddress getInetAddress()返回连接的远程主机的IP地址。
int getPort()返回连接的远程主机的端口号。
SocketAddress getRemoteSocketAddress()返回连接的远程套接字的地址。
boolean isConnected()检查数据报套接字是否连接到远程主机。
boolean isClosed()检查数据报套接字是否已关闭。
void setSoTimeout(int timeout)设置通过receive()方法等待接收数据报包的超时时间。
int getSoTimeout()返回通过receive()方法等待接收数据报包的超时时间。
void setBroadcast(boolean on)设置是否启用发送广播数据报包的能力。
boolean getBroadcast()检查是否启用发送广播数据报包的能力。

DatagramPacket类

java.net.DatagramPacket类是Java网络编程中用于封装数据报的对象。数据报是在UDP协议中发送和接收的数据单元,包含了要发送或接收的数据以及相关的源地址和目标地址信息。

方法说明
DatagramPacket(byte[] buf, int length)创建一个从指定缓冲区发送或接收数据的数据报包。
DatagramPacket(byte[] buf, int offset, int length)创建一个从指定缓冲区的指定偏移量开始发送或接收数据的数据报包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)创建一个从指定缓冲区发送或接收数据的数据报包,并指定目的地的IP地址和端口号。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)创建一个从指定缓冲区的指定偏移量开始发送或接收数据的数据报包,并指定目的地的IP地址和端口号。
byte[] getData()返回数据报包的数据缓冲区。
int getLength()返回数据报包的有效数据长度。
void setAddress(InetAddress address)设置数据报包的目的地IP地址。
InetAddress getAddress()返回数据报包的目的地IP地址。
void setPort(int port)设置数据报包的目的地端口号。
int getPort()返回数据报包的目的地端口号。

TCP Socket编程(面向字节流)

流程梳理

服务器端:创建ServerSocket对象,读取客户端发送的数据,向客户端发送响应数据

  1. 创建ServerSocket对象:服务器端创建一个ServerSocket对象,并指定一个监听的端口号。该端口号用于标识服务器上的特定服务。
  2. 监听连接请求:服务器调用ServerSocket对象的accept()方法,开始监听客户端的连接请求。调用之后服务器会一直阻塞,直到有客户端请求连接。
  3. 与客户端建立连接:当有客户端连接请求到达时,accept()方法会返回一个Socket对象,表示与该客户端的连接。服务器使用这个Socket对象与客户端进行通信。
  4. 读取客户端发送的数据:服务器可以通过Socket对象的输入流和输出流进行数据交换。通过输入流,服务器可以读取客户端发送的数据;通过输出流,服务器可以向客户端发送响应数据
  5. 向客户端发送响应数据:服务器根据具体的业务逻辑处理客户端发送的数据,可能需要进行相应的计算、查询数据库或调用其他接口。
  6. 关闭连接:处理完客户端的请求后,可以调用Socket对象的close()方法关闭与该客户端的连接,释放资源。

客户端:创建Socket对象,向服务器发送数据,读取服务器的响应数据。

  1. 创建Socket对象:客户端通过创建一个Socket对象,并指定服务器的IP地址和端口号,向服务器发起连接请求。
  2. 尝试建立连接:客户端的Socket对象会尝试与服务器建立连接。
  3. 建立连接:服务器接受客户端的连接请求,两端建立起连接。
  4. 向服务端发送数据:客户端可以使用Socket对象的输入流和输出流与服务器进行数据通信。通过输入流,客户端可以读取服务器发送的数据;通过输出流,客户端可以向服务器发送请求数据。
  5. 发送请求并等待响应:客户端根据具体的需求发送请求数据,并等待服务器的响应。
  6. 关闭连接:客户端可以随时关闭与服务器的连接,释放资源,通过调用Socket对象的close()方法。

代码示例

服务端代码:创建ServerSocket对象,读取客户端发送的数据,向客户端发送响应数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Server {
public static void main(String[] args) {
try {
// 创建ServerSocket对象并指定监听的端口号
ServerSocket serverSocket = new ServerSocket(8888);

System.out.println("服务器启动,等待客户端连接...");

// 监听客户端连接请求
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接,地址:" + clientSocket.getInetAddress().getHostAddress());

// 创建输入流和输出流
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);

// 读取客户端发送的数据
String message = in.readLine();
System.out.println("客户端发送的消息:" + message);

// 向客户端发送响应
out.println("Hello, Client!");

// 关闭流和连接
in.close();
out.close();
clientSocket.close();
serverSocket.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端代码:创建Socket对象,向服务器发送数据,读取服务器的响应数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Client {
public static void main(String[] args) {
try {
// 创建Socket对象并指定服务器的IP地址和端口号
Socket socket = new Socket("localhost", 8888);

// 创建输入流和输出流
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

// 向服务器发送消息
out.println("Hello, Server!");

// 读取服务器的响应
String response = in.readLine();
System.out.println("服务器的响应:" + response);

// 关闭流和连接
in.close();
out.close();
socket.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}

UDP Socket编程(面向数据报)

流程梳理

服务端:创建DatagramSocket对象,接收客户端发送的数据报,向客户端发送响应数据报

  1. 创建DatagramSocket对象:服务器端创建一个DatagramSocket对象,并指定一个监听的端口号。该端口号用于标识服务器上的特定服务。
  2. 接收客户端发送的数据报:服务器通过DatagramSocket对象的receive()方法接收客户端发送的数据报。该方法会进入阻塞状态,直到有客户端发送数据报到达为止。
  3. 处理客户端发送的数据报:服务器根据具体的业务逻辑处理客户端发送的数据报,可能需要进行相应的计算、查询数据库或调用其他接口。
  4. 向客户端发送响应数据报:服务器使用DatagramSocket对象的send()方法向客户端发送响应数据报。
  5. 关闭连接:处理完客户端的请求后,可以调用DatagramSocket对象的close()方法关闭连接,释放资源。

客户端:创建DatagramSocket对象,向服务器发送数据报,接收服务器的响应数据报。

  1. 创建DatagramSocket对象:客户端通过创建一个DatagramSocket对象,并指定服务器的IP地址和端口号,用于发送数据报。
  2. 向服务器发送数据报:客户端可以使用DatagramSocket对象的send()方法向服务器发送数据报。
  3. 接收服务器的响应数据报:客户端通过DatagramSocket对象的receive()方法接收服务器发送的响应数据报。该方法会进入阻塞状态,直到服务器发送数据报到达为止。
  4. 处理服务器的响应数据报:客户端根据具体的需求处理服务器发送的响应数据报。
  5. 关闭连接:客户端可以随时关闭连接,释放资源,通过调用DatagramSocket对象的close()方法。

代码示例

服务端代码:创建DatagramSocket对象,接收客户端发送的数据报,向客户端发送响应数据报

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Server {
public static void main(String[] args) {
try {
// 创建DatagramSocket对象并指定监听的端口号
DatagramSocket serverSocket = new DatagramSocket(8888);

System.out.println("服务器启动,等待客户端连接...");

// 创建接收数据报的缓冲区
byte[] buffer = new byte[1024];

// 创建接收数据报的对象
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);

// 接收客户端发送的数据报
serverSocket.receive(receivePacket);
System.out.println("接收到客户端的数据:" + new String(receivePacket.getData(), 0, receivePacket.getLength()));

// 处理客户端发送的数据报
// ...

// 创建发送数据报的对象,并指定目标地址和端口号
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
byte[] responseData = "Hello, Client!".getBytes();
DatagramPacket sendPacket = new DatagramPacket(responseData, responseData.length, clientAddress, clientPort);

// 向客户端发送响应数据报
serverSocket.send(sendPacket);

// 关闭连接
serverSocket.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端代码:创建DatagramSocket对象,向服务器发送数据报,接收服务器的响应数据报。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Client {
public static void main(String[] args) {
try {
// 创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();

// 创建发送数据报的对象,并指定目标地址和端口号
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 8888;
byte[] requestData = "Hello, Server!".getBytes();
DatagramPacket sendPacket = new DatagramPacket(requestData, requestData.length, serverAddress, serverPort);

// 向服务器发送数据报
socket.send(sendPacket);

// 创建接收数据报的缓冲区
byte[] buffer = new byte[1024];

// 创建接收数据报的对象
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);

// 接收服务器的响应数据报
socket.receive(receivePacket);
System.out.println("接收到服务器的数据:" + new String(receivePacket.getData(), 0, receivePacket.getLength()));

// 处理服务器的响应数据报
// ...

// 关闭连接
socket.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}

URL相关类

URL类

为了表示URL,java.net 中实现了 URL类

URL类提供了一些基本操作和方法,如获取URL的协议、主机名、端口号、路径等信息,以及打开连接等操作

方法简介
getProtocol( )获取该URL的协议名
getHost( )获取该URL的主机名
getPort( )获取该URL的端口号
getPath( )获取该URL的文件路径
getFile( )获取该URL的文件名
getQuery( )获取该URL的查询名
getAuthority()返回URL的权限
getDefaultPort()返回URL协议的默认端口
getRef()返回URL的引用部分
openConnection()打开与URL的连接,允许客户端与资源通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Example {
public static void main(String[] args) {
String urlString = "http://www.example.com:8080/path/file.html?param1=value1&param2=value2";

try {
// 创建URL对象
URL url = new URL(urlString);

// 获取协议名
String protocol = url.getProtocol();
System.out.println("协议名:" + protocol);

// 获取主机名
String host = url.getHost();
System.out.println("主机名:" + host);

// 获取端口号
int port = url.getPort();
System.out.println("端口号:" + port);

// 获取文件路径
String path = url.getPath();
System.out.println("文件路径:" + path);

// 获取文件名
String file = url.getFile();
System.out.println("文件名:" + file);

// 获取查询名
String query = url.getQuery();
System.out.println("查询名:" + query);

// 获取权限
String authority = url.getAuthority();
System.out.println("权限:" + authority);

// 获取协议的默认端口
int defaultPort = url.getDefaultPort();
System.out.println("协议的默认端口:" + defaultPort);

// 获取引用部分
String ref = url.getRef();
System.out.println("引用部分:" + ref);

// 打开与URL的连接
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 这里只演示获取URL的各个部分信息,未进行具体的与服务器通信操作

} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}

URLConnection类

URLConnection类是Java中用于创建和管理与URL之间的连接的类。它提供了一系列方法,可以用于发送HTTP请求、读取服务器响应、设置请求头、处理重定向等操作

URLConnection类有许多方法可用于设置或确定有关连接的信息

方法描述
openConnection()创建一个到指定URL的连接对象,并返回URLConnection实例。
connect()建立与服务器的连接。调用该方法后,会尝试建立与URL对应服务器的网络连接。
disconnect()关闭连接。调用该方法会关闭与服务器的连接。
getInputStream()获取与URL连接的输入流,用于读取服务器响应数据。
getOutputStream()获取与URL连接的输出流,用于向服务器发送请求数据。
getContentEncoding()获取响应内容的编码方式。
getRequestMethod()获取请求方法,常见的方法有GET、POST、PUT、DELETE等。
getResponseCode()获取HTTP响应码。根据返回的响应码,可以判断请求是否成功或出现错误。
getHeaderField()获取指定名称的响应头值。
getContentLength()获取响应内容的长度。
setRequestMethod()设置请求方法,常见的方法有GET、POST、PUT、DELETE等。
setRequestProperty()设置请求头属性,例如”Content-Type”、”Authorization”等请求头信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class Example {
public static void main(String[] args) {
// 定义目标URL
String urlString = "https://www.example.com/api";

try {
// 创建URL对象并打开连接
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

// 设置请求方法为POST
connection.setRequestMethod("POST");

// 设置请求头属性
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Authorization", "Bearer 你的访问令牌");

// 允许向服务器发送请求数据
connection.setDoOutput(true);

// 获取输出流并发送请求数据
OutputStream outputStream = connection.getOutputStream();
String requestData = "{\"key\":\"value\"}";
outputStream.write(requestData.getBytes());
outputStream.flush();

// 建立与服务器的连接
connection.connect();

// 获取响应码
int responseCode = connection.getResponseCode();
System.out.println("响应码:" + responseCode);

if (responseCode == HttpURLConnection.HTTP_OK) {
// 获取响应数据
InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
StringBuilder response = new StringBuilder();

while ((line = reader.readLine()) != null) {
response.append(line);
}

reader.close();
System.out.println("响应数据:" + response.toString());
} else {
System.out.println("发生错误。响应码:" + responseCode);
}

// 关闭连接
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}

URLEncoder与URLDdcoder类

URLEncoder和URLDecoder是Java中用于对URL进行编码和解码的工具类

  • URLEncoder编码:用于将URL中的特殊字符转换为百分号编码形式,以便在URL中传递参数时能够正确解析
  • URLDecoder解码:用于对经过编码的URL进行解码操作,将编码后的字符恢复为原始的字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Example {
public static void main(String[] args) {
try {
// 使用URLEncoder进行编码
String encodedValue = URLEncoder.encode("你好,世界!", StandardCharsets.UTF_8.toString());
System.out.println("编码后的值: " + encodedValue);// %E4%BD%A0%E5%A5%BD%EF%BC%8C%E4%B8%96%E7%95%8C%EF%BC%81

// 使用URLDecoder进行解码
String decodedValue = URLDecoder.decode(encodedValue, StandardCharsets.UTF_8.toString());
System.out.println("解码后的值: " + decodedValue);// 你好,世界!
} catch (Exception e) {
e.printStackTrace();
}
}
}

IP相关类

InetAddress类

InetAddress类位于java.net包中,是Java标准库中的一个类,用于表示和操作网络上的IP地址

InetAddress类提供了一系列关于IP地址和域名的相关操作方法

方法描述
getLocalHost()返回表示本地主机的InetAddress对象。
getByName(String host)根据给定的主机名(域名或IP地址)返回对应的InetAddress对象。
getAllByName(String host)根据给定的主机名返回一个由该主机的所有IP地址组成的数组。
getHostName()返回与该InetAddress对象关联的IP地址对应的主机名。
getCanonicalHostName()返回与该InetAddress对象关联的IP地址的完全限定域名。
getHostAddress()返回与该InetAddress对象关联的IP地址的字符串表示。
getAddress()返回与该InetAddress对象关联的IP地址的字节数组形式。
isReachable(int timeout)判断该InetAddress对象所表示的主机是否可达,在指定的超时时间内进行检测。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Example {
public static void main(String[] args) {
try {
// 获取本地主机的IP地址和主机名
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("本地主机的IP地址:" + localHost.getHostAddress());
System.out.println("本地主机的主机名:" + localHost.getHostName());
System.out.println("本地主机的完全限定域名:" + localHost.getCanonicalHostName());

// 根据主机名获取IP地址
String host = "www.example.com";
InetAddress address = InetAddress.getByName(host);
System.out.println(host + " 的IP地址:" + address.getHostAddress());
System.out.println(host + " 的主机名:" + address.getHostName());
System.out.println(host + " 的完全限定域名:" + address.getCanonicalHostName());

// 根据主机名获取所有IP地址
InetAddress[] addresses = InetAddress.getAllByName(host);
for (InetAddress addr : addresses) {
System.out.println(host + " 的IP地址:" + addr.getHostAddress());
}

// 判断主机是否可达
boolean isReachable = address.isReachable(5000); // 超时时间为5秒
if (isReachable) {
System.out.println(host + " 可达");
} else {
System.out.println(host + " 不可达");
}

// 获取IP地址的字节数组形式
byte[] byteAddress = address.getAddress();
System.out.print(host + " 的IP地址的字节数组形式:");
for (byte b : byteAddress) {
System.out.print(b + " ");
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}