【Java基础】网络编程
网络编程
网络编程概述
网络编程的目的:直接或简洁地通过网络协议与其他计算机实现数据交换,进行通讯。
网络编程的两个主要问题:
- 如果准确地定位网络上一台或多台主机,并定位主机上的特定应用;
- 找到主机后如何可靠高效地进行数据传输。
网络通信要素概述
通信双方地址:
- IP
- Port
网络协议:
- OSI 参考模型:模型过于理想化,未能在因特网上进行广泛推广
- TCP / IP 参考模型:事实上的国际标准
通信要素1:IP&Port
IP 地址(InetAddress):
- 唯一的标识 Internet 上的计算机(通信实体);
- 本地回环地址(hostAddress):
127.0.0.1
、主机名(hostName):localhost
- IP 地址分类方式1:IPV4 和 IPV6
- IPV4:4个字节组成,以点分十进制表示,如 192.168.0.1
- IPV6:16个字节组成,写成8个无符号整数,每个整数用四个十六进制位表示,如 3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
- IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)
Port:
- 标识正在计算机上运行的进程(程序),不同的进程有不同的端口号;
- 被规定为一个 16 位的整数 0~65535;
- 端口分类:
- 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP 占用端口
80,FTP 占用端口 21,Telnet 占用端口 23); - 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口 8080,MySQL 占用端口 3306,Oracle 占用端口 1521)。
- 私有端口:49152~65535。
- 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP 占用端口
InetAddress 类没有提供公共的构造器,而是提供了如下几个静态方法来获取
InetAddress 实例:
public static InetAddress getLocalHost()
public static InetAddress getByName(String host)
InetAddress 提供了如下几个常用的方法:
public String getHostAddress()
:返回 IP 地址字符串public String getHostName()
:获取此 IP 地址的主机名public boolean isReachable(int timeout)
:测试是否可以达到该地址
package parzulpan.com.java;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc :
*/
public class InetAdressTest {
public static void main(String[] args) {
InetAddress inet1 = null;
InetAddress inet2 = null;
InetAddress inet3 = null;
InetAddress inet4 = null;
try {
inet1 = InetAddress.getByName("www.parzulpan.cn");
System.out.println(inet1);
inet2 = InetAddress.getByName("61.135.185.32");
System.out.println(inet2);
inet3 = InetAddress.getByName("localhost");
System.out.println(inet3);
inet4 = InetAddress.getLocalHost();
System.out.println(inet4);
System.out.println(inet1.getHostAddress());
System.out.println(inet1.getHostName());
boolean reachable = false;
try {
reachable = inet1.isReachable(1000);
System.out.println(reachable);
} catch (IOException e) {
e.printStackTrace();
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
通信要素2:网络协议
计算机网络中实现通信必须有一些约定,即通信协议(网络协议),对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常
用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与
再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
传输层协议中有两个非常重要的协议:
- 传输控制协议 TCP(Transmission Control Protocol)
- 用户数据报协议 UDP(User Datagram Protocol)
TCP/IP 以其两个主要协议:传输控制协议(TCP)和 网络互联协议(IP,Internet Protocol,是网络层的主要协议,支持网间互连的数据通信)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
TCP/IP 协议模型从更实用的角度出发,形成了高效的四层体系结构,即
物理链路层、网络层、传输层和应用层。
Socket
客户端和服务器端工作的核心逻辑:
Socket:
- 网络上具有唯一标识的 IP地址 和 端口号 组合在一起才能构成唯一能识别的标识符套接字;
- 通信的两端都要有 Socket,是两台机器间通信的端点,网络通信其实就是 Socket 间的通信;
- Socket 允许程序把网络连接当成一个流,数据在两个 Socket 间通过 IO 传输;
- 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端;
- 分类:
- 流套接字(stream socket):使用 TCP 提供可依赖的字节流服务;
- 数据报套接字(datagram socket):使用 UDP 提供“尽力而为”的数据报服务;
在 Java 中,
Socket 类的常用构造器:
public Socket(InetAddress address,int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。public Socket(String host,int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket 类的常用方法:
public InputStream getInputStream()
返回此套接字的输入流。可以用于接收网络消息public OutputStream getOutputStream()
返回此套接字的输出流。可以用于发送网络消息public InetAddress getInetAddress()
此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 nullpublic InetAddress getLocalAddress()
获取套接字绑定的本地地址。 即本端的IP地址public int getPort()
此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0public int getLocalPort()
返回此套接字绑定到的本地端口。 如果尚未绑定套接字,则返回 -1。即本端的端口号public void close()
关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和OutputStreampublic void shutdownInput()
如果在套接字上调用shutdownInput()
后从套接字输入流读取内容,则流将返回 EOF(文件结束符)。 即不能在从此套接字的输入流中接收任何数据public void shutdownOutput()
禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用shutdownOutput()
后写入套接字输出流,
则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据
直观的理解 Socket:
- 把客户端和服务器工作想象成打电话,socket 就好比是我们的手机, connect 就好比是拿着手机拨号,服务器端的 bind 就好比是去电信公司开户,将号码和绑定,这样别人就可以通过号码联系你,listen 就好比是让手机处于可接听的状态,accept 就好比是被叫的一方拿起手机进行应答;
- 然后对方拨通手机号建立通话(connect),拨打电话的人说(write):你好,接听电话的人听到(write):并回答(write)你好。这样,就等同进入了 read/write 的数据传输过程;
- 最后,拨打电话的人完成了此次交流,挂上电话,对应的操作可以理解为 close,接听电话的人知道对方已挂机,也挂上电话,也是一次 close;
- 在整个通话过程中,手机是我们可以和外面通信的设备,对应到网络编程的世界里,socket 也是我们可以和外界进行网络通信的途径。
TCP 三次握手
TCP三次握手过程:
其中:
- 确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
- 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
- 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
- 复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
解读:
这里使用的网络编程模型都是阻塞式的。所谓阻塞式,就是调用发起后不会直接返回,由操作系统内核处理之后才会返回。相对的,还有一种叫做非阻塞式的。
最初的过程:服务器端通过 socket,bind 和 listen 完成了被动套接字的准备工作,被动的意思就是等着别人来连接,然后调用 accept,就会阻塞在这里,等待客户端的连接来临;客户端通过调用 socket 和 connect 函数之后,也会阻塞。接下来的事情是由操作系统内核完成的,更具体一点的说,是操作系统内核的网络协议栈在工作。
具体的过程:
- 客户端的协议栈向服务器端发送一个值为 j 的 SYN 包,客户端进入 SYN_SENT 状态;
- 服务器端的协议栈收到 SYN 包之后,服务器端发送一个值为 j + 1 的 ACK 应答包和 一个值为 k 的 SYN 包,服务器端进入 SYN_RCVD 状态;
- 客户端的协议栈收到 ACK+SYN 包之后,使得应用程序从 connect 调用返回,表示客户端到服务器端的单向连接建立成功,客户端进入 ESTABLISHED 状态,同时客户端协议栈向服务器端发送一个值为 k + 1 的 ACK 应答包;
- 服务器端的协议栈收到 ACK 应答包之后,使得 accept 阻塞调用返回,表示服务器端到客户端的单向连接也建立成功,服务器端进入 ESTABLISHED 状态。
形象的比喻:
- A 先对 B 说:“喂,你在么?我在的,我的口令是 j。”
- B 收到之后大声回答:“我收到你的口令 j 并准备好了,你准备好了吗?我的口令是 k。”
- A 收到之后也大声回答:“我收到你的口令 k 并准备好了,我们开始吧。”
TCP 四次挥手
TCP四次挥手过程:
具体的过程:
- TCP 连接终止时,主机1 发送值为 m 的 FIN 包,主机1 进入 FIN_WAIT_1(终止等待1) 状态。
- 主机2 收到 FIN 包后,发送值为 m + 1 的 ACK 应答包,主机2 进入 CLOSE_WAIT(关闭等待)状态。注意,此时处于半关闭的状态,主机1 到主机2 的方向释放了,但是主机2 到主机1 的方向还正常,即主机2 依然能向主机1 发送数据且主机1 能接收。主机1 接收到 ACK 应答包后,主机1 进入 FIN_WAIT_2(终止等待2) 状态。
- 主机2 准备好关闭连接时,发送值为 n 的 FIN 包,主机2 进入 LAST_ACK(最后确认)状态,等待主机1 的确认。
- 主机1 收到 FIN 包后,发送值为 n + 1 的 ACK 应答包,主机1 进入 TIME_WAIT(时间等待) 状态。注意,此时 TCP 连接还没有释放,必须经过 2MSL(Maximum Segment Lifetime,最长报文段寿命)的时间后,才进入 CLOSED(关闭)状态。
- 主机2 接收到 ACK 应答包后,进入 CLOSED(关闭)状态。
TCP 网络编程
TCP协议:
- 使用 TCP协议前,须先建立TCP连接,形成传输数据通道
- 传输前,采用“三次握手”方式,点对点通信,是可靠的
- TCP 协议进行通信的两个应用进程:客户端、服务端。
- 在连接中可进行大数据量的传输
- 传输完毕,需释放已建立的连接,效率低
客户端的工作过程包含以下四个基本的步骤:
- 创建 Socket:根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
- 客户端程序可以使用 Socket 类创建对象,创建的同时会自动向服务器方发起连接。Socket的构造器是:
Socket(String host,int port)throws UnknownHostException,IOException
:向服务器 (域名是 host。端口号为 port) 发起 TCP 连接,若成功,则创建Socket对象,否则抛出异常。Socket(InetAddress address,int port)throws IOException
:根据 InetAddress 对象所表示的 IP地址 以及 端口号port 发起连接。
- 客户端程序可以使用 Socket 类创建对象,创建的同时会自动向服务器方发起连接。Socket的构造器是:
- 打开连接到 Socket 的输入/输出流: 使用 getInputStream()方法获得输入流,使用 getOutputStream() 方法获得输出流,进行数据传输。
- 按照一定的协议对 Socket 进行读/写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程。
- 关闭 Socket:断开客户端到服务器的连接,释放线路。
服务器的工作过程包含以下四个基本的步骤:
- 调用 ServerSocket(int port):创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。
- ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字连接的 ServerSocket 对象。
- 所谓“接收”客户的套接字请求,就是accept()方法会返回一个 Socket 对象。
- 调用 accept():监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。
- 调用 该Socket类对象的 getOutputStream() 和 getInputStream ():获取输出流和输入流,开始网络数据的发送和接收。
- 关闭 ServerSocket 和 Socket 对象:客户端访问结束,关闭通信套接字。
package parzulpan.com.java;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Author : parzulpan
* @Time : 2020-11-28
* @Desc : TCP 网络编程
* 例子1:客户端发送消息给服务端,服务端将数据显示在控制台上
*/
public class TCPTest {
// 客户端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
// 1. 创建 Socket 对象,指明服务器端的 IP 和 Port
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet, 28888);
// 2. 获取一个输出流,用于输出数据
os = socket.getOutputStream();
// 3. 写出数据
os.write("我是客户端".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4. 关闭资源
try {
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 服务端
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
// 1. 创建服务器端的 ServerSocket,指明自己的端口号
ss = new ServerSocket(28888);
// 2. 调用 accept() 表示接收到来自于客户端的 Socket
socket = ss.accept();
// 3. 获取一个输入流,用于输入数据
is = socket.getInputStream();
// 4. 读取数据
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[5];
int data;
while ((data = is.read(buffer)) != -1) {
baos.write(buffer, 0, data);
}
System.out.println(baos.toString());
System.out.println("数据来自于:" + socket.getInetAddress().getHostAddress());
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5. 关闭资源
try {
if (baos != null) {
baos.close();
}
if (is != null) {
is.close();
}
if (socket != null) {
socket.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package parzulpan.com.java;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Author : parzulpan
* @Time : 2020-11-28
* @Desc : TCP 网络编程
* 例子2:客户端发送文件给服务端,服务端将文件保存在本地。
*/
public class TCPTest1 {
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
BufferedInputStream bis = null;
try {
// 1.
socket = new Socket(InetAddress.getByName("127.0.0.1"), 29999);
// 2.
os = socket.getOutputStream();
// 3.
bis = new BufferedInputStream(new FileInputStream(new File("tcp.png")));
// 4.
byte[] buffer = new byte[1024];
int data;h
while ((data = bis.read(buffer)) != -1) {
os.write(buffer, 0, data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5.
try {
if (bis != null) {
bis.close();
}
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
BufferedOutputStream bos = null;
try {
// 1.
ss = new ServerSocket(29999);
//2.
socket = ss.accept();
// 3.
is = socket.getInputStream();
// 4.
bos = new BufferedOutputStream(new FileOutputStream(new File("tcpServer.png")));
byte[] buffer = new byte[1024];
int data;
while ((data = is.read(buffer)) != -1) {
bos.write(buffer, 0, data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 5.
try {
if (bos != null) {
bos.close();
}
if (is != null) {
is.close();
}
if (socket != null) {
socket.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package parzulpan.com.java;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @Author : parzulpan
* @Time : 2020-11-28
* @Desc : TCP 网络编程
* 例子3:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。并关闭相应的连接。
*/
public class TCPTest2 {
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
InputStream is = null;
BufferedInputStream bis = null;
ByteArrayOutputStream baos = null;
try {
// 创建 Socket 对象,指明服务器端的 IP 和 Port
socket = new Socket(InetAddress.getByName("127.0.0.1"), 29998);
// 获取一个输出流,用于输出数据
os = socket.getOutputStream();
// 获取一个输入流,用于输入数据
is = socket.getInputStream();
// 写出和读入数据
bis = new BufferedInputStream(new FileInputStream(new File("tcp.png")));
baos = new ByteArrayOutputStream();
//
byte[] buffer1 = new byte[1024];
int data1;
while ((data1 = bis.read(buffer1)) != -1) {
os.write(buffer1, 0, data1);
}
// 关闭数据的输出,结束阻塞式的等待
socket.shutdownOutput();
byte[] buffer2 = new byte[1024];
int data2;
while ((data2 = is.read(buffer2)) != -1) {
baos.write(buffer2, 0, data2);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (baos != null) {
baos.close();
}
if (bis != null) {
bis.close();
}
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
OutputStream os = null;
BufferedOutputStream bos = null;
try {
ss = new ServerSocket(29998);
socket = ss.accept();
is = socket.getInputStream();
os = socket.getOutputStream();
bos = new BufferedOutputStream(new FileOutputStream(new File("tcpServer1.png")));
byte[] buffer1 = new byte[1024];
int data1;
while ((data1 = is.read(buffer1)) != -1) {
bos.write(buffer1, 0, data1);
}
System.out.println("图片传输完成!");
os.write("服务器端已经接收到客户端发送的图片!".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null) {
bos.close();
}
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
if (socket != null) {
socket.close();
}
if (ss != null) {
ss.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
UDP 网络编程
UDP协议:
- 将数据、源、目的封装成数据包,不需要建立连接
- 每个数据报的大小限制在 64K 内
- 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
- 可以广播发送
- 发送数据结束时无需释放资源,开销小,速度快
工作流程:
- 使用 DatagramSocket 与 DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用 Socket 的发送、接收方法
- 关闭 Socket
- 发送端与接收端是两个独立的运行程序
package parzulpan.com.java;
import org.junit.Test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @Author : parzulpan
* @Time : 2020-11-28
* @Desc : UDP 网络编程
*/
public class UDPTest {
// 发送端
@Test
public void sender() {
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
byte[] str = "我是 UDP 方式发送的数据!".getBytes();
DatagramPacket packet = new DatagramPacket(str, 0, str.length, InetAddress.getByName("127.0.0.1"), 28877);
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
@Test
public void receiver() {
DatagramSocket socket = null;
try {
socket = new DatagramSocket(28877);
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String string = new String(packet.getData(), 0, packet.getLength());
System.out.println(string + " -- " + packet.getAddress());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
socket.close();
}
}
}
}
URL 网络编程
URL(Uniform Resource Locator)即统一资源定位符,它表示 Internet 上某一资源的地址。不但可以用来标识一个资源,而且还指明了如何 locate 这个资源。
URL 的基本结构由五部分组成:
<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
- 例如:
http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
package parzulpan.com.java;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @Author : parzulpan
* @Time : 2020-11-28
* @Desc : URL 网络编程
*/
public class URLTest {
public static void main(String[] args) {
URL url = null;
try {
url = new URL("http://localhost:8080/examples/tcp.png?username=tomcat");
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (url != null) {
System.out.println("getProtocol() : "+url.getProtocol());
System.out.println("getHost() : "+url.getHost());
System.out.println("getPort() : "+url.getPort());
System.out.println("getPath() : "+url.getPath());
System.out.println("getFile() : "+url.getFile());
System.out.println("getQuery() : "+url.getQuery());
}
}
}
package parzulpan.com.java;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* @Author : parzulpan
* @Time : 2020-11-28
* @Desc : URL 网络编程,从 tomcat 下载数据保存到本地
*/
public class URLTest1 {
public static void main(String[] args) {
HttpURLConnection urlConnection = null;
InputStream is = null;
BufferedOutputStream bos = null;
try {
URL url = new URL("http://localhost:8080/examples/cat.png?username=tomcat");
urlConnection = (HttpURLConnection) url.openConnection(); // 针对 HTTP 协议的 URLConnection 类
urlConnection.connect();
is = urlConnection.getInputStream();
bos = new BufferedOutputStream(new FileOutputStream(new File("ch12/tomcat.png")));
byte[] buffer = new byte[1024];
int data;
while ((data = is.read(buffer)) != -1) {
bos.write(buffer, 0, data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
}
}
几个名词的区别:
- URI,是 uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。
- URL,是 uniform resource locator,统一资源定位符,它是一种具体
的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。 - URN,是 uniform resource name,统一资源命名,是通过名字来标识资源。
练习和总结
为什么要需要进行三次握手,不是两次或者四次呢?
是为了确定双方都具备接收发送能力,为后续可靠性传输做准备。
第一次握手,服务器端只能确定客户端的发送能力和服务器端的接收能力。
第二次握手,客户端可以确定客户端和服务器端具备接收发送能力。
第三次握手,服务器端就能确定客户端的接收能力和服务器端的发送能力。
为什么要需要进行四次挥手,不是两次或者三次呢?
因为 TCP 连接是双向传输的对等模式,关闭连接时,当收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方是否现在关闭发送数据通道,需要上层应用来决定,因此,己方 ACK 和 FIN 一般都会分开发送,这里就相对于三次握手多了一次。
三次握手连接阶段,最后一次ACK包丢失会进入什么样的一个状态?
对于服务器端,此时的状态为 SYN_RECV,它会根据 TCP 的超时重传机制,会等待3秒、6秒、12秒后重新发送 SYN+ACK 包,以便让客户端重新发送ACK包。如果重发指定次数之后,仍然未收到客户端的应答,那么一段时间后,服务器端自动关闭这个连接。
对于客户端,此时的状态为 ESTABLISHED,如果客户端向服务器端发生数据,服务器端将以 RST 包响应,客户端感知到错误。
【Java基础】网络编程的更多相关文章
- java基础-网络编程(Socket)技术选型入门之NIO技术
java基础-网络编程(Socket)技术选型入门之NIO技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.传统的网络编程 1>.编写socket通信的MyServer ...
- 二十三、Java基础--------网络编程
Java中另一个重要技术就是网络编程了,为了更好的学习web方向的知识,有必要对java之网络编程好好学习,本文将围绕网络编程技术进行分析. 常见的网络协议:UDP.TCP UDP 1. 将数据源和目 ...
- Java基础——网络编程(二)
一.套接字 Socket 网络驱动程序提供给应用程序编程的接口和一种机制,可以比喻成一个港口码头 应用程序只要把货放在这,就算完成了货物的运送.它在应用程序中创建,通过一种绑定机制与驱动程序建立关系, ...
- Java基础——网络编程(一)
本文主要记录网络编程的一些基础知识,学了前班部分,对专业术语有些蒙,但是,收货也是很多很多的.观察了自己计算机的进程,查找其他网络地址的IP,对互联网的层次关系有了更深一步的了解.下面多是概念的摘录, ...
- Java基础——网络编程
一.网络编程概述 概述: Java是 Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序. Java提供的网络类库,可以实现无痛的网络连接,联网 ...
- java基础—网络编程
一.网络基础概念 首先理清一个概念:网络编程 != 网站编程,网络编程现在一般称为TCP/IP编程.
- JAVA基础——网络编程之网络链接
一.网络编程基本概念 1.OSI与TCP/IP体系模型 2.IP和端口 解决了文章最开始提到的定位的问题. IP在互联网中能唯一标识一台计算机,是每一台计算机的唯一标识(身份证):网络编程是和远程计算 ...
- Java基础——网络编程(三)
TCP 网络编程 -- tcp 分为客户端和服务端 -- 客户端对应的对象是 Socket -- 服务端对应的对象是 ServerSocket -- 如果客户端先启动,则出现 connection r ...
- 梦入IBM之java基础-网络编程
如今我们来谈谈最后的内容:网络编程: 1):TCP中是线程与线程进行通讯!内部的执行机制是这种:先有一个线程去监听某个port.然后假设有Socket连接上来了以后,server会生成一个Socket ...
- Java基础-网络编程1
网络编程 Socket 基本概念 C/S结构 :全称为Client/Server结构,是指客户端和服务器结构.常见程序有QQ.迅雷等软件. B/S结构 :全称为Browser/Server结构,是指浏 ...
随机推荐
- 能否让APP永不崩溃—小光与我的对决
前言 关于拦截异常,想必大家都知道可以通过Thread.setDefaultUncaughtExceptionHandler来拦截App中发生的异常,然后再进行处理. 于是,我有了一个不成熟的想法.. ...
- Vue--子组件互相传值,子组件来回传值,传值反复横跳
Vue--子组件传值,子组件来回传值,子组件传值反复横跳 我不不仅要子组件之间直接传值,我还要传过去再传回来,传回来再传过去,子组件直接反复横跳 解决问题 给组件传值,并不知道改值的校验结果 同一个组 ...
- uni-app开发中的各种问题处理
特别注意: ※:在components下的组件,图片路径用 /static/img/back.png 这样的根路径形式,不要用../static 或者 ../../static 的形式,不然很坑, ...
- hive行存储与列存储
首先判断hive表是行存储还是列存储 判断方法: 1.使用hiveSQL"show create table table_name",这种方式,可以查看建表时候指定的那种方式; 2 ...
- 1款开源工具,实现自动化升级K3S集群!
即便你的集群能够平稳运行,Kubernetes升级依旧是一项艰难的任务.由于每3个月Kubernetes会发布一个新版本,所以升级是十分必要的.如果一年内你不升级你的Kubernetes集群,你就会落 ...
- Python读写EXCEL文件常用方法大全
前言 python读写excel的方式有很多,不同的模块在读写的讲法上稍有区别,这里我主要介绍几个常用的方式. 用xlrd和xlwt进行excel读写: 用openpyxl进行excel读写: 用pa ...
- matplotlib的学习2-基本用法
import matplotlib.pyplot as plt import numpy as np x = np.linspace(-1, 1, 50)#范围-1 到 1,个数是50 y = 2*x ...
- 来吧,自己动手撸一个分布式ID生成器组件
在经过了众多轮的面试之后,小林终于进入到了一家互联网公司的基础架构组,小林目前在公司有使用到架构组研究到分布式id生成器,前一阵子大概看了下其内部的实现,发现还是存在一些架构设计不合理之处.但是又由于 ...
- linux目录和Windows目录对比
linux目录和Windows目录对比 我们应该知道 Windows 有一个默认的安装目录专门用来安装软件.Linux 的软件安装目录也应该是有讲究的,遵循这一点,对后期的管理和维护也是有帮助的. / ...
- python序列(四)成员资格判断
判断是否存在指定的值 1.count()方法,如果存在则返回大于0的数,如果返回0则表示不存在. 2."in"关键字来判断一个值是否存在于列表中,返回结果为"True&q ...