前言
什么是网络编程呢?
网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。当然,即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程
一、网路编程中的基本概念
1.1 发送端和接收端
- 发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
- 接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
- 收发端:发送端和接收端两端,也简称为收发端。
注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。
请求和响应
一般来说,获取一个网络资源,涉及到两次网络数据传输:
- 第一次:请求数据的发送
- 第二次:响应数据的发送。
客户端和服务端
客户端和服务端:
- 服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
- 客户端:获取服务的一方进程,称为客户端。
二、Socket 套接字
2.1 基本概念
Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。
2.2数据报套接字:使用传输层UDP协议
UDP,即User Datagram Protocol(用户数据报协议),传输层协议。
UDP的主要特点:(详细内容会在后续的章节中详细介绍)
- 无连接
- 不可靠传输
- 面向数据报
- 有接收缓冲区,无发送缓冲区
- 大小受限:一次最多传输 64KB
对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。
三、UDP数据报套接字编程
3.1 Java数据报套接字通信模型
对于UDP协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且一次发送全部数据报,一次接收全部的数据报。
java中使用UDP协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使用DatagramPacket 作为发送或接收的UDP数据报。
对于一次发送及接收UDP数据报的流程如下:
3.2 DatagramSocket API
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。
DatagramSocket 构造方法:
DatagramSocket 方法:
3.3 DatagramPacket API
DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造方法:
构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddress 来创建。
InetSocketAddress ( SocketAddress 的子类 )构造方法:
DatagramPacket 方法:
以下为服务端和客户端代码:
UDP服务端:
package network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.channels.DatagramChannel;
/**
* @author Zhang
* @date 2024/5/2015:38
* @Description:
*/
public class UdpEchoServer {
//创建一个datagramSocket对象,这是后续操作网卡的基础
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
//此写法是手动指定端口
socket = new DatagramSocket(port);
// 此写法是让服务器自动分配端口
// socket = new DatagramSocket();
}
//通过这个方法来启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
// 1. 读取请求并解析
/**
* DatagramPacket这个队形来承载从网卡这边读取的数据,
* 收到的数据需要一个内存空间来保存这个数据
* DatagramPacket 内部不能自动分配空间,需要程序员手动创建,交给DatagramPacket进行处理
*/
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket); //当服务器一旦启动,就会立即执行receive方法,入伏哦客户端请求还没到,receive就会阻塞到客户端请求到达
// 当完成receive后,数据以二进制的形式存储到DatagramPacket
// 要想把这里的数据显示出来,好需要把这个二进制转变成字符串
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
// 2.根据请求计算响应(一般服务器都会经理的过程)
// 由于此处是回显服务器,请求是啥样,响应就是啥样
String response = process(request);
// 3. 把响应写会到客户端
// 写一个响应对象,DatagramPacket
// 往DatagramPacket 构造刚才数据,并通过 sent 返回
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());
// 构造这个数据报,需要指定数据内容,也指定下一个数据报要发给谁
socket.send(responsePacket);
// 4.打印一个日志,把这次数据交互的详情打印出来
System.out.printf("[%s:%d] req = %s, resp = %s\n",requestPacket.getAddress().toString(),
requestPacket.getPort(),request,response);
}
}
public String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server = new UdpEchoServer(9090);
server.start();
}
}
UDP客户端
package network;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
/**
* @author ZhangXu
* @date 2024/5/2015:38
* @Description:
*/
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIP = "";
private int serverPort = 0;
public UdpEchoClient(String ip,int port) throws SocketException {
// 创建这个对象,不能手动指定端口
socket = new DatagramSocket();
//由于UDP自身不会持有对端的信息,就需要在应用程序里,吧对端的情况给记录下来
serverIP = ip;
serverPort = port;
}
public void start() throws IOException {
System.out.println("客户端启动!");
Scanner scanner = new Scanner(System.in);
while (true){
// 1.从控制台读取数据,作为请求
System.out.print("->");
String request = scanner.next();
// 2. 把请求内容构造成 DatagramPacket 对象,发送给服务器
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
InetAddress.getByName(serverIP), serverPort);
socket.send(requestPacket);
// 3.尝试服务器返回的响应了
DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
// 4.把响应转换成字符串,并先显示出来
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();
}
}
启动客户端和服务端,在客户端发送内容,在服务端将会接收到相同的信息,运行结果:
UdpEchoClient:
客户端启动!
->xiaoming
xiaoming
->
UdpEchoClient:
服务器启动
[/127.0.0.1:51575] req = xiaoming, resp = xiaoming
我们在下一节内容将会介绍TCP流套接字编程相关内容。