共计 6384 个字符,预计需要花费 16 分钟才能阅读完成。
tcp 协议图解
第二章 TCP 通信程序
2.1 概述
TCP 通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
两端通信时步骤:
- 服务端程序,需要事先启动,等待客户端的连接。
- 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。
在 Java 中,提供了两个类用于实现 TCP 通信程序:
- 客户端:
java.net.Socket
类表示。创建Socket
对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。 - 服务端:
java.net.ServerSocket
类表示。创建ServerSocket
对象,相当于开启一个服务,并等待客户端的连接。
2.2 Socket 类
Socket
类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。
构造方法
-
public Socket(String host, int port)
: 创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的 host 是 null,则相当于指定地址为回送地址。小贴士:回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。
构造举例,代码如下:
Socket client = new Socket("127.0.0.1", 6666);
成员方法
-
public InputStream getInputStream()
:返回此套接字的输入流。- 如果此 Scoket 具有相关联的通道,则生成的 InputStream 的所有操作也关联该通道。
- 关闭生成的 InputStream 也将关闭相关的 Socket。
-
public OutputStream getOutputStream()
:返回此套接字的输出流。- 如果此 Scoket 具有相关联的通道,则生成的 OutputStream 的所有操作也关联该通道。
- 关闭生成的 OutputStream 也将关闭相关的 Socket。
-
public void close()
:关闭此套接字。- 一旦一个 socket 被关闭,它不可再使用。
- 关闭此 socket 也将关闭相关的 InputStream 和 OutputStream。
-
public void shutdownOutput()
:禁用此套接字的输出流。- 任何先前写出的数据将被发送,随后终止输出流。
## 2.3 ServerSocket 类
ServerSocket
类:这个类实现了服务器套接字,该对象等待通过网络的请求。
构造方法
-
public ServerSocket(int port)
:使用该构造方法在创建 ServerSocket 对象时,就可以将其绑定到一个指定的端口号上,参数 port 就是端口号。
构造举例,代码如下:
ServerSocket server = new ServerSocket(6666);
成员方法
-
public Socket accept()
:侦听并接受连接,返回一个新的 Socket 对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。
2.4 简单的 TCP 网络程序
TCP 通信分析图解
- 【服务端】启动, 创建 ServerSocket 对象,等待连接。
- 【客户端】启动, 创建 Socket 对象,请求连接。
- 【服务端】接收连接, 调用 accept 方法,并返回一个 Socket 对象。
- 【客户端】Socket 对象,获取 OutputStream,向服务端写出数据。
- 【服务端】Scoket 对象,获取 InputStream,读取客户端发送的数据。
到此,客户端向服务端发送数据成功。
自此,服务端向客户端回写数据。
- 【服务端】Socket 对象,获取 OutputStream,向客户端回写数据。
- 【客户端】Scoket 对象,获取 InputStream,解析回写数据。
- 【客户端】释放资源,断开连接。
客户端向服务器发送数据
服务端实现:
public class ServerTCP {public static void main(String[] args) throws IOException {System.out.println("服务端启动 , 等待连接 ...."); | |
// 1. 创建 ServerSocket 对象,绑定端口,开始等待连接 | |
ServerSocket ss = new ServerSocket(6666); | |
// 2. 接收连接 accept 方法, 返回 socket 对象. | |
Socket server = ss.accept(); | |
// 3. 通过 socket 获取输入流 | |
InputStream is = server.getInputStream(); | |
// 4. 一次性读取数据 | |
// 4.1 创建字节数组 | |
byte[] b = new byte[1024]; | |
// 4.2 据读取到字节数组中. | |
int len = is.read(b);// 4.3 解析数组, 打印字符串信息 | |
String msg = new String(b, 0, len); | |
System.out.println(msg); | |
//5. 关闭资源. | |
is.close(); | |
server.close();} | |
} |
客户端实现:
public class ClientTCP {public static void main(String[] args) throws Exception {System.out.println("客户端 发送数据"); | |
// 1. 创建 Socket (ip , port) , 确定连接到哪里. | |
Socket client = new Socket("localhost", 6666); | |
// 2. 获取流对象 . 输出流 | |
OutputStream os = client.getOutputStream(); | |
// 3. 写出数据. | |
os.write("你好么? tcp , 我来了".getBytes()); | |
// 4. 关闭资源 . | |
os.close(); | |
client.close();} | |
} |
服务器向客户端回写数据
服务端实现:
public class ServerTCP {public static void main(String[] args) throws IOException {System.out.println("服务端启动 , 等待连接 ...."); | |
// 1. 创建 ServerSocket 对象,绑定端口,开始等待连接 | |
ServerSocket ss = new ServerSocket(6666); | |
// 2. 接收连接 accept 方法, 返回 socket 对象. | |
Socket server = ss.accept(); | |
// 3. 通过 socket 获取输入流 | |
InputStream is = server.getInputStream(); | |
// 4. 一次性读取数据 | |
// 4.1 创建字节数组 | |
byte[] b = new byte[1024]; | |
// 4.2 据读取到字节数组中. | |
int len = is.read(b);// 4.3 解析数组, 打印字符串信息 | |
String msg = new String(b, 0, len); | |
System.out.println(msg); | |
// ================= 回写数据 ======================= | |
// 5. 通过 socket 获取输出流 | |
OutputStream out = server.getOutputStream(); | |
// 6. 回写数据 | |
out.write("我很好, 谢谢你".getBytes()); | |
// 7. 关闭资源. | |
out.close(); | |
is.close(); | |
server.close();} | |
} |
客户端实现:
public class ClientTCP {public static void main(String[] args) throws Exception {System.out.println("客户端 发送数据"); | |
// 1. 创建 Socket (ip , port) , 确定连接到哪里. | |
Socket client = new Socket("localhost", 6666); | |
// 2. 通过 Scoket, 获取输出流对象 | |
OutputStream os = client.getOutputStream(); | |
// 3. 写出数据. | |
os.write("你好么? tcp , 我来了".getBytes()); | |
// ============== 解析回写 ========================= | |
// 4. 通过 Scoket, 获取 输入流对象 | |
InputStream in = client.getInputStream(); | |
// 5. 读取数据数据 | |
byte[] b = new byte[100]; | |
int len = in.read(b); | |
System.out.println(new String(b, 0, len)); | |
// 6. 关闭资源 . | |
in.close(); | |
os.close(); | |
client.close();} | |
} |
以下是实际代码
package com.itheima.demo01.TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
TCP 通信的服务器端: 接收客户端的请求, 读取客户端发送的数据, 给客户端回写数据 | |
表示服务器的类: | |
java.net.ServerSocket: 此类实现服务器套接字。构造方法: | |
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。服务器端必须明确一件事情, 必须的知道是哪个客户端请求的服务器 | |
所以可以使用 accept 方法获取到请求的客户端对象 Socket | |
成员方法: | |
Socket accept() 侦听并接受到此套接字的连接。服务器的实现步骤: | |
1. 创建服务器 ServerSocket 对象和系统要指定的端口号 | |
2. 使用 ServerSocket 对象中的方法 accept, 获取到请求的客户端对象 Socket | |
3. 使用 Socket 对象中的方法 getInputStream()获取网络字节输入流 InputStream 对象 | |
4. 使用网络字节输入流 InputStream 对象中的方法 read, 读取客户端发送的数据 | |
5. 使用 Socket 对象中的方法 getOutputStream()获取网络字节输出流 OutputStream 对象 | |
6. 使用网络字节输出流 OutputStream 对象中的方法 write, 给客户端回写数据 | |
7. 释放资源(Socket,ServerSocket) |
*/
public class TCPServer {
public static void main(String[] args) throws IOException { | |
//1. 创建服务器 ServerSocket 对象和系统要指定的端口号 | |
ServerSocket server = new ServerSocket(8888); | |
//2. 使用 ServerSocket 对象中的方法 accept, 获取到请求的客户端对象 Socket | |
Socket socket = server.accept(); | |
//3. 使用 Socket 对象中的方法 getInputStream()获取网络字节输入流 InputStream 对象 | |
InputStream is = socket.getInputStream(); | |
//4. 使用网络字节输入流 InputStream 对象中的方法 read, 读取客户端发送的数据 | |
byte[] bytes = new byte[1024]; | |
int len = is.read(bytes); | |
System.out.println(new String(bytes,0,len)); | |
//5. 使用 Socket 对象中的方法 getOutputStream()获取网络字节输出流 OutputStream 对象 | |
OutputStream os = socket.getOutputStream(); | |
//6. 使用网络字节输出流 OutputStream 对象中的方法 write, 给客户端回写数据 | |
os.write("收到谢谢".getBytes()); | |
//7. 释放资源(Socket,ServerSocket) | |
socket.close(); | |
server.close();} |
}
package com.itheima.demo01.TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
TCP 通信的客户端: 向服务器发送连接请求, 给服务器发送数据, 读取服务器回写的数据 | |
表示客户端的类: | |
java.net.Socket: 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。套接字: 包含了 IP 地址和端口号的网络单位 | |
构造方法: | |
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。参数: | |
String host: 服务器主机的名称 / 服务器的 IP 地址 | |
int port: 服务器的端口号 | |
成员方法: | |
OutputStream getOutputStream() 返回此套接字的输出流。InputStream getInputStream() 返回此套接字的输入流。void close() 关闭此套接字。实现步骤: | |
1. 创建一个客户端对象 Socket, 构造方法绑定服务器的 IP 地址和端口号 | |
2. 使用 Socket 对象中的方法 getOutputStream()获取网络字节输出流 OutputStream 对象 | |
3. 使用网络字节输出流 OutputStream 对象中的方法 write, 给服务器发送数据 | |
4. 使用 Socket 对象中的方法 getInputStream()获取网络字节输入流 InputStream 对象 | |
5. 使用网络字节输入流 InputStream 对象中的方法 read, 读取服务器回写的数据 | |
6. 释放资源(Socket) | |
注意: | |
1. 客户端和服务器端进行交互, 必须使用 Socket 中提供的网络流, 不能使用自己创建的流对象 | |
2. 当我们创建客户端对象 Socket 的时候, 就会去请求服务器和服务器经过 3 次握手建立连接通路 | |
这时如果服务器没有启动, 那么就会抛出异常 ConnectException: Connection refused: connect | |
如果服务器已经启动, 那么就可以进行交互了 |
*/
public class TCPClient {
public static void main(String[] args) throws IOException { | |
//1. 创建一个客户端对象 Socket, 构造方法绑定服务器的 IP 地址和端口号 | |
Socket socket = new Socket("127.0.0.1",8888); | |
//2. 使用 Socket 对象中的方法 getOutputStream()获取网络字节输出流 OutputStream 对象 | |
OutputStream os = socket.getOutputStream(); | |
//3. 使用网络字节输出流 OutputStream 对象中的方法 write, 给服务器发送数据 | |
os.write("你好服务器".getBytes()); | |
//4. 使用 Socket 对象中的方法 getInputStream()获取网络字节输入流 InputStream 对象 | |
InputStream is = socket.getInputStream(); | |
//5. 使用网络字节输入流 InputStream 对象中的方法 read, 读取服务器回写的数据 | |
byte[] bytes = new byte[1024]; | |
int len = is.read(bytes); | |
System.out.println(new String(bytes,0,len)); | |
//6. 释放资源(Socket) | |
socket.close();} |
}