首先我们要知道传输层提供的协议主要有两种,TCP协议和UDP协议,先来介绍一下它们的区别:
1、TCP是面向连接的,UDP是无连接的。
连接的本质是双方分别保存了对方的关键信息,而面向连接并不意味着数据一定能正常传输到对方电脑中,只是说在遇到问题时会采取一些办法重新传输这个数据,而无连接意味着一方只需要发送数据即可,不需要提前取得对方的同意,至于这个数据发出去之后怎么样了它不会关心。
2、TCP是可靠传输的,UDP是不可靠传输的
可靠传输其实就是第一点说的,A主机知道B主机有没有收到自己发送的消息,在发送失败时,会采取一定的措施(尝试重传之类的),但是并不代表这个消息能100%送达。
可靠传输需要付出什么代价:
1、机制更复杂
2、传输效率会降低
3、TCP是面向字节流的,UDP是面向数据报的
此处说的字节流和文件说的字节流是一个意思,TCP是以字节为单位进行数据传输,而UDP是通过数据报为单位进行数据传输
4、TCP和UDP都是全双工的
全双工的意思是双方都既可以发送数据,也可以接收数据,数据的流动不是单向的。
接下来,我们来编写一个简单的UDP客户端/服务器通信的程序,这里的服务器我们采用回显服务器(echo server),不执行复杂的逻辑,客户端发送什么,服务器就会原封不动的返回什么。
客户端的代码:
UdpEchoClient
package network;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
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();
serverIp = ip;
serverport = port;
}
public void start() throws IOException {
System.out.println("客户端启动!");
Scanner scanner = new Scanner(System.in);
while(true){
//1、从客户端读取要发送的数据
System.out.println("请输入你想发送的内容");
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();
}
}
服务器的代码:
UdpEchoServer
package network;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpEchoServer {
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){
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
//1、服务器接收到来自客户端的消息并且将内容放在输出型参数datagramPacket中
String request = new String(requestPacket.getData(),0, requestPacket.getLength());
//将二进制形式的datagramPacket数据变为字符串类型的数据
//2、根据请求计算响应(大部分服务器都会经历这个步骤)
String response = process(request);
//把响应返回客户端,通过send发送信息
DatagramPacket reponsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(reponsePacket);
//4、打印一个日志,输出这次数据交互的详情
System.out.printf("[%s:%d] req=%s, resp=%s\n", requestPacket.getAddress().toString(),
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();
}
}
运行截图:
代码的几个重点:
1、可以看到DatagramPacker的构造方法有三种:
- 只指定字符数据缓冲区的(客户端收响应的时候要用,服务器收请求的时候也要用)
- 指定字符数组缓冲区,同时只能一个InetAddress对象(这个对象同时包含了IP和端口)(服务器返回响应给客户端)
- 指定字符数组缓冲区,同时指定IP+端口号
其实都是让它们在发送数据的时候不仅要指定数据的内容,同时和指定这个数据要发送到的地址。
2、通信流程