一.网络编程
1.什么是网络编程
网络编程指的是 网络上的主机 , 通过不同的进程 , 以编程的方式实现网络通信

2.网络编程中的概念
① 发送端和接收端
发送端 : 数据的 发送方 进程 ; 发送端主机为网络通信中的源主机
接收端 : 数据的 接收方 进程 ; 接收端主机位网络通信中的目的主机
收发端 : 发送端和接收端两端
注意 : 发送端和接收端只是相对的 , 只是一次网络数据传输产生数据流向 后的概念

② 请求和响应
一般情况下 , 获取一个网络资源 , 涉及到两次网络数据传输
第一次 : 请求数据的发送
第二次 : 响应数据的发送

③ 客户端和服务端
在常见的网络数据传输的场景下
服务端 : 提供服务的一方进程
客户端 : 获取服务的一方进程
④ 常见的客户端服务端模型
客户端是指给用户使用的程序 , 服务端是提供用户服务的程序 :
- 用户端先发送请求到服务端
- 服务端根据请求数据 , 执行相应的业务处理
- 服务端返回响应 : 发送业务处理结果
- 客户端根据响应数据 , 展示处理结果

二.Socket 套接字
Socket 是由系统提供用于网络通信的技术 , 是基于 TCP/IP 协议的网络通信的基本操作单元 ; 基于 Socket 套接字的网络程序开发就是网络编程
1. 分类
① 数据报套接字(使用传输层 UDP 协议)
UDP 协议特点 : 无连接(直接发数据) , 不可靠(丢包不重传) , 面向数据报(以一个数据报为单位) , 效率较高 , 有接收缓冲区但无发送缓冲区 , 由于以数据报为单位 边界清晰
简单理解 : 数据包以独立数据块为单位传输 , 一块数据需一次性发送 , 接收端也需完整接收该数据块 , 不能拆分接收
② 流套接字(使用传输层 TCP 协议)
TCP 协议特点 : 面向连接 , 可靠(重传丢失的数据包) , 面向字节流(以字节为单位) , 效率较低 , 有接收缓冲区和发送缓冲区 , 由于是流式数据(大小不限) 需要 去处理粘包问题
简单理解 : 字节流基于 IO 流传输 , 在 IO 流未被关闭时 , 数据无固定边界 , 可以分多次接收发送
3.UDP 数据报套接字编程模型(无连接,有边界)
① 概念
- 对于 UDP 来说 , 具有无连接 , 面向数据报的特征 , 即每次都没有建立连接 , 并且一次发送全部数据报 , 一次接收全部数据报
- Java 中使用 UDP 协议通信 , 主要基于 DatagramSocket 类来创建数据报套接字 , 并使用 DatagramPacket 作为发送或接收的 UDP 数据报
② 流程如下:

③ 核心 API
DatagramSocket
用于发送和接收 UDP 数据报
构造方法
|
方法 |
说明 |
|
DatagramSocket() |
创建一个 UDP 数据报套接字的 Socket , 绑定到本机的任意一个端口(客户端) |
|
DatagramSocket(int port) |
创建一个 UDP 数据报套接字的 Socket , 绑定到本机指定的端口(服务器) |
方法
|
方法 |
说明 |
|
void receive(DatagramPacket p) |
从此套接字接收数据报(若没有,则会阻塞等待) |
|
void send(DatagramPacket p) |
从此套接字发送数据报(不会阻塞 , 直接发送) |
|
void close() |
关闭此数据报套接字 |
DatagramPacket
是 UDPSocket 发送和接收的数据报
构造方法
|
方法 |
说明 |
|
DatagramPacket(byte[] buf , int length) |
构造一个 DatagramPacket 以用来接收数据报 , 接收的数据保存在字节数组中(buf) ,接收指定长度(length) |
|
DatagramPacket(byte[] buf , int offset , int length , SocketAddress address) |
构造一个 DatagramPacket 以用户来发哦是那个数据报 , 发送数据为字节数组(buf) ,从 offect 到指定长度 length ; address 指的是目的主句的 IP 和端口号 |
方法
|
方法 |
说明 |
|
InetAddress getAddress() |
从接收的数据报中 , 获取发送端主句 IP 和地址 ; 或从发送的数据报中 , 获取接收端主机 IP 地址 |
|
int getPort() |
从接收的数据报中 , 获取发送端主机的端口号 ; 或从发送的数据报中 , 获取接收端主机端口号 |
|
byte[] getData() |
获取数据报中的数据 |
④ 代码示例 :
😶🌫️ UDP Echo Server
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
public class UdpEchoServer {
private DatagramSocket socket = null;//代表一个UDP服务端
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);//处理异常
}
public void start() throws IOException{
System.out.println("服务器启动..");
while(true){
//1.读取请求并解析
// 创建一个UDP数据报,用于封装请求
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(requestPacket);
// 把数据报转换为字符串
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
//2.根据请求计算响应(关键步骤)
//此处是一个回显服务器
String response = process(request);
//3.把响应写入一个数据报,并返回给客户端
// 此处不能使用response.length(),因为response.length()返回的是字符串的长度,而不是字节的长度
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);//发送响应
System.out.printf("req: %s; resp: %s\n", request, response);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server = new UdpEchoServer(9090);
server.start();
}
}
😶🌫️ UDP Echo Client
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
//与服务器不同的是,客户端的构造函数需要指定服务器的IP和端口号
public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
this.serverIP = serverIP;
this.serverPort = serverPort;
socket = new DatagramSocket();
}
public void start() throws IOException {
System.out.println("客户端启动..");
Scanner sc = new Scanner(System.in);
while(true){
//1.从控制台读取请求
System.out.println("请输入要发送的请求:");
if (!sc.hasNext()) {//以空白符为分隔符
break;
}
String request = sc.next();
//2.将请求封装成DatagramPacket对象
// 构造过程中需要指定数据,数据长度,目标IP,目标端口
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(serverIP),serverPort);
//3.发送请求
socket.send(requestPacket);
//4.接收服务器响应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
//5.将响应转换为字符串
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
client.start();
}
}
核心组件拆解
|
阶段 |
接收端核心对象 |
发送端核心对象 |
数据传输载体 |
|
初始化 |
DatagramSocket(bindPort) |
DatagramSocket() | |
|
数据封装 |
DatagramPacket(buffer, len) |
DatagramPacket(data, len, ip, port) |
数据报(有边界) |
|
数据收发 |
receive (packet)(阻塞)send(packet) |
send(packet)receive (packet)(可选) |
整包数据报 |
|
数据解析 |
packet.getData()packet.getLength()packet.getAddress() |
字节数组 | |
|
资源释放 |
close() DatagramSocket |
close() DatagramSocket |
4.TCP 字节流套接字通信模型(有连接 , 无边界)
① 概念
- TCP 是基于面向连接,可靠的字节流的传输层协议 , 流套接字编程依赖于 Java 的 ServerSocket(服务端) 和 Socket(客户端) 两个类
- 核心可概括为 : 「服务端监听连接 → 客户端发起连接 → 双向字节流通信 → 关闭资源」
② 流程如下:

③ 核心 API
ServerSocket
创建 TCP 服务端 Socket 的 API
构造方法
|
方法 |
说明 |
|
ServerSocket(int port) |
创建一个服务端流套接字 Socket , 并绑定到指定端口 |
方法
|
方法 |
说明 |
|
Socket accept() |
开始监听指定端口(创建时绑定的端口) , 有客户端连接后 , 返回一个服务端 Socket 对象 , 并基于该 Socket 建立于客户端的连接 , 否则阻塞等待 |
|
void close() |
关闭此套接字 |
Socket
主动连接服务端 , 于服务端建立双向字节流通信
构造方法
|
方法 |
说明 |
|
Socket(String host , int port) |
创建一个客户端流套接字 Socket , 并与对应 IP 的主机的端口进程建立连接 |
方法
|
方法 |
说明 |
|
InetAddress getInetAddress() |
返回套接字所连接的地址 |
|
InputStream getInputStream() |
返回此套接字的输入流 |
|
OutputStream getOutStream() |
返回此套接字的输出流 |
④ 代码示例
😶🌫️TcpEchoServer
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoServer {
private ServerSocket serverSocket = null;
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动...");
while(true){
Socket clientSocket = serverSocket.accept();
processConnection(clientSocket);
}
}
private void processConnection(Socket clientSocket) throws IOException {
System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());
try(InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
//针对 InputStream 套了一个 Scanner
Scanner scanner = new Scanner(inputStream);
//针对 OutputStream 套了一个 PrintWriter
PrintWriter writer = new PrintWriter(outputStream);
//1.读取请求并解析
while(true){
if (!scanner.hasNext()) {
System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());
break;
}
String request = scanner.next();
//2.根据请求计算响应(关键步骤)
String response = process(request);
//3.返回响应到客户端
writer.println(response);
writer.flush();
System.out.printf("req: %s; resp: %s\n", request, response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
clientSocket.close();//需要处理异常
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
😶🌫️TcpEchoClient
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoClient {
private Socket socket = null;
public TcpEchoClient(String serverIP,int serverPoint) throws IOException {
socket = new Socket(serverIP,serverPoint);
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
try(OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream()){
Scanner sc = new Scanner(inputStream);
PrintWriter writer = new PrintWriter(outputStream);
while(true){
//1.从控制台读取请求
System.out.println("请输入要发送的请求:");
String request = scanner.next();
//2.将请求写入Socket并发送
writer.println(request);
writer.flush();
//3.读取响应
String response = sc.next();
//4..将响应打印到控制台
System.out.println(response);
}
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}
核心组件拆解
|
阶段 |
服务端核心对象 |
客户端核心对象 |
数据传输载体 |
|
连接建立 |
ServerSocket(port) |
Socket(ip, port) | |
|
阻塞等待 |
accept() → Socket |
三次握手 | |
|
数据传输 |
InputStream(读)OutputStream(写) |
OutputStream(写)InputStream(读) |
字节流(无边界) |
|
资源释放 |
close () 流 / Socket/ServerSocket |
close () 流 / Socket |
3133

被折叠的 条评论
为什么被折叠?



