什么是网络编程
⽹络编程,指⽹络上的主机,通过不同的进程,以编程的⽅式实现⽹络通信(或称为⽹络数据传输)。
当然,我们只要满⾜进程不同就⾏;所以即便是同⼀个主机,只要是不同进程,基于⽹络来传输数据,也属于⽹络编程。
特殊的,对于开发来说,在条件有限的情况下,⼀般也都是在⼀个主机中运⾏多个进程来完成⽹络编程。
但是,我们⼀定要明确,我们的⽬的是提供⽹络上不同主机,基于⽹络来传输数据资源:
• 进程A:编程来获取⽹络资源
• 进程B:编程来提供⽹络资源
客户端和服务端
服务端:在常⻅的⽹络数据传输场景下,把提供服务的⼀⽅进程,称为服务端,可以提供对外服务。
客⼾端:获取服务的⼀⽅进程,称为客⼾端。
对于服务来说,⼀般是提供:
• 客⼾端获取服务资源
• 客⼾端保存资源在服务端
常见的客户端服务端模型
最常⻅的场景,客⼾端是指给⽤⼾使⽤的程序,服务端是提供⽤⼾服务的程序:
- 客⼾端先发送请求到服务端
- 服务端根据请求数据,执⾏相应的业务处理
- 服务端返回响应:发送业务处理结果
- 客⼾端根据响应数据,展⽰处理结果(展⽰获取的资源,或提⽰保存资源的处理结果)
Socket套接字
概念
Socket套接字,是由系统提供⽤于⽹络通信的技术,是基于TCP/IP协议的⽹络通信的基本操作单元。
基于Socket套接字的⽹络程序开发就是⽹络编程。
分类
Socket套接字主要针对传输层协议划分为如下三类:
流套接字:使⽤传输层TCP协议
TCP,即Transmission Control Protocol(传输控制协议),传输层协议。
以下为TCP的特点(细节后续再学习):
• 有连接
• 可靠传输
• ⾯向字节流
• 有接收缓冲区,也有发送缓冲区
• ⼤⼩不限
对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的
情况下,是⽆边界的数据,可以多次发送,也可以分开多次接收。
数据报套接字:使⽤传输层UDP协议
UDP,即User Datagram Protocol(⽤⼾数据报协议),传输层协议。
以下为UDP的特点(细节后续再学习):
• ⽆连接
• 不可靠传输
• ⾯向数据报
• 有接收缓冲区,⽆发送缓冲区
• ⼤⼩受限:⼀次最多传输64k
对于数据报来说,可以简单的理解为,传输数据是⼀块⼀块的,发送⼀块数据假如100个字节,必须⼀次发送,接收也必须⼀次接收100个字节,⽽不能分100次,每次接收1个字节。
原始套接字
原始套接字⽤于⾃定义传输层协议,⽤于读写内核没有处理的IP协议数据。
我们不学习原始套接字,简单了解即可。
Java数据报套接字通信模型
对于UDP协议来说,具有⽆连接,⾯向数据报的特征,即每次都是没有建⽴连接,并且⼀次发送全部数据报,⼀次接收全部的数据报。
java中使⽤UDP协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使⽤DatagramPacket 作为发送或接收的UDP数据报。对于⼀次发送及接收UDP数据报的流程如下:
UDP数据报套接字编程
DatagramSocket
DatagramSocket 是UDP Socket,⽤于发送和接收UDP数据报。
DatagramSocket 构造⽅法:
DatagramPacket
DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造⽅法:
代码⽰例
UDPEchoServer
public class UdpEchoServer {
private DatagramSocket socket = null;
// 参数是服务器要绑定的端⼝
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
// 使⽤这个⽅法启动服务器.
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
// 反复的, ⻓期的执⾏针对客⼾端请求处理的逻辑.
// ⼀个服务器, 运⾏过程中, 要做的事情, 主要是三个核⼼环节.
// 1. 读取请求, 并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 40
socket.receive(requestPacket);
// 这样的转字符串的前提是, 后续客⼾端发的数据就是⼀个⽂本的字符串.
String request = new String(requestPacket.getData(), 0, requestPacke
// 2. 根据请求, 计算出响应
String response = process(request);
// 3. 把响应写回给客⼾端
// 此时需要告知⽹卡, 要发的内容是啥, 要发给谁.
DatagramPacket responsePacket = new DatagramPacket(response.getBytes
requestPacket.getSocketAddress());
socket.send(responsePacket);
// 记录⽇志, ⽅便观察程序执⾏效果.
System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAd
request, response);
}
}
// 根据请求计算响应. 由于是回显程序, 响应内容和请求完全⼀样.
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server = new UdpEchoServer(9090);
server.start();
}
UDPEchoClient
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
// 服务器的 ip 和 服务器的端⼝.
public UdpEchoClient(String ip, int port) throws SocketException {
serverIp = ip;
serverPort = port;
// 这个 new 操作, 就不再指定端⼝了. 让系统⾃动分配⼀个空闲端⼝.
socket = new DatagramSocket();
}
// 让这个客⼾端反复的从控制台读取⽤⼾输⼊的内容. 把这个内容构造成 UDP 请求, 发给服务器
// 最终再显⽰在客⼾端的屏幕上.
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("客⼾端启动!");
while (true) {
// 1. 从控制台读取⽤⼾输⼊的内容
System.out.print("-> "); // 命令提⽰符, 提⽰⽤⼾要输⼊字符串.
String request = scanner.next();
// 2. 构造请求对象, 并发给服务器.
DatagramPacket requestPacket = new DatagramPacket(request.getBytes()
InetAddress.getByName(serverIp), serverPort);
socket.send(requestPacket);
// 3. 读取服务器的响应, 并解析出响应内容.
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePa
// 4. 显⽰到屏幕上.
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
// UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);
client.start();
}
}