一:服务器
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpEchoServer {
private DatagramSocket socket = null;
//网络编程的前提是操作网卡,操作网卡是通过socket对象来操作的
public UdpEchoServer(int port) throws SocketException {
//服务器程序,要在服务器启动的时候,就把端口号确定下来,
//因为客户端是主动的一方,服务器是被动的一方,客户端要知道服务器在哪,才能"主动的了"
/**
* 实际运行的时候,端口号指定的规则:
* 1)端口号是合法的:1-65535
* 2)不能和别的进程的端口号冲突
*/
socket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
// 1) 读取请求并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
/**
* 这个PatagramPacket 对象就是一个UDP数据报,包含两个部分
* 1)报头:通过类的属性来表示的
* 2)载荷:通过构造方法传递的字符数组,作为持有载荷的空间
*/
//数组是真正存储数据,持有数据的空间,里面保存了载荷
//一个DatagramPacket就是一个数据报,但本质上是二进制数据,
//既然是二进制数据,就可以用字符数据表示
socket.receive(requestPacket);
// 为了方便在 java 代码中处理 (尤其是后面进行打印) 可以把上述数据报中的二进制数据, 拿出来, 构造成 String
String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
// 2) 根据请求计算响应
String response = process(request);
// 3) 把响应写回到客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), 0, response.getBytes().length,
requestPacket.getSocketAddress());
//发送的时候,不光有数据,还要有数据要发给谁
//由于这是UDP,UDP socket自身没有记录对方的IP,端口号 等信息,但 DatagramPacket requestPacket里有
//getSocketAddress()方法,返回的对象了 就包含了IP ,端口号
socket.send(responsePacket);
System.out.printf("[%s:%d] req=%s, resp=%s\n", requestPacket.getAddress(), requestPacket.getPort(),
request, response);
}
}
// 由于当前写的是 "回显服务器"
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoServer server = new UdpEchoServer(9090);
server.start();
}
}
二:客户端
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
socket = new DatagramSocket();
//客户端,都是需要通过额外的途径知道,服务器,IP, 端口号是什么
//因此,服务器的IP ,端口号都得是固定的
// 这俩信息需要额外记录下来, 以备后续使用.
this.serverIp = serverIp;
this.serverPort = serverPort;
}
public static void main(String[] args) throws SocketException {
UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
//服务器和客户端在一个主机上,IP地址就固定写127.0.0.1
//如果在不同的主机上,服务器IP是啥就写啥
}
}
在一次通信的过程中,需要知道至少4个核心指标.
1)源IP==>发件人地址
2)源端口号==>发件人电话
3)目的IP==>收件人地址
4)目的端口号==>收件人电话
对于客户端来说,给服务器发送请求,刚才指定的服务器IP,服务器端口号,就是目的IP,目的端口号,源IP,也是客户端所在的IP(127.0.0.1),那么源端口号是什么?
构造Socket对象的时候,没有指定端口号,没指定并不意味着没有,而是操作系统,自动分配了一个空间的(和别的程序不冲突)的端口号,这个自动分配的端口号,每次启动程序都可能不一样.
为什么服务需要有固定的端口号,而客户端就需要让系统自动分配,反过来行不行?
1)服务器要有固定的端口号,是因为,客户端需要主动的服务器发送请求,如果服务器端口号不是固定的,(假设每次都变,此时客户端就不知道请求发给谁了)
2)如果给客户端固定的端口号,是可能和客户端所在的电脑上的其他程序的端口号发生冲突.