UDP:User Datagram Protocol 用户报文协议
TCP:Transmission Control Protocol 传输控制协议
相同点:都是传输层协议,都是进程与进程之间的通信
不同点:
UDP没有做任何处理,保持网络的原生态(不可靠)
TCP对网络传输做了一些控制,是通信变得可靠(可靠)
所以将
UDP称为:不可靠,无连接,面向数据报文(字符流)的一种协议。
TCP可靠的有连接的面向字节流的协议。
网络层主要协议:IP协议 : Internet Protocol
网络层的重要概念:IP地址
传输层的重要概念:Port端口 0~65535 一个进程可以拥有很多端口,一个端口只能一个进程
数据链路层的重要概念:MAC地址
IP地址+端口 + 传输层协议 =》 五元组信息
1,站在通信数据角度:源IP,源端口,目的IP,目的端口,传输层协议(UDP,TCP)
2,站在通信双方:本地IP,本地端口,远端IP,远端端口,传输层协议(TCP,UDP)
站在应用层角度,要实现进程之间的通信,需要使用传入层提供的服务,传输层向应用层提供Socket,用来接收应用层发来的数据,
类比传输层有两家公司(TCP,UDP)应用层(用户)要向传输层沟通,只能通过传输层提供的Socket接口,实现沟通。

DatagramSocket():未指定端口,系统会分配一个端口
DatagramSocket(int port):绑定指定端口,方便客户端通信,存在端口号被占用的风险

发送数据报文:
接收数据报文:

一旦UDP在逻辑上通信成功,双方都可以发送和接收数据,双方地位平等(P2P模式)通信结束后,双方都应该调用close()方法,进行资源回收。


接收方:在接收数据时,只需要提供数据的容器和长度(byte[] buf,int length)
获取发送的数据包
Byte[] getData();接收方获取接收到的数据的内容
作为发送方:在发送数据时,需要提供发送的数据包,长度,偏移,目的地址/目的端口(byte[] buf + int offset + int lenght + (InternetAddress aimAdd + int port) -> 会被封装成SocketAddress对象);
服务器获取客户端的地址和端口

1,请求响应模式(客户端请求一次,服务器响应一次)
2,订阅推送模式(客户端订阅一次请求,服务器在今后每次有此类请求的更新时,服务器就会主动推送给客户端)

UDP协议不区分客户端与服务器,因此双方都可以作为发送方和接收方,所以可以双方都采用DatagramSocket类创建对象,通过双方的端口号建立通信,将发送和接收的数据分别存在DatagramPacket对象中,在通过字符编码的形式,转换为正确的数据报。
UDP服务器
- package 网络编程.udp;
-
- import 网络编程.Log;
-
- import java.io.IOException;
- import java.net.*;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Scanner;
-
- public class TranslateServer {
- //公开的IP和端口
- public final static int port = 9999;
- private static HashMap
map = new HashMap<>(); - static {
- map.put("apple","苹果");
- map.put("book","书");
- map.put("bicycle","自行车");
- Log.println("字典初始化完成");
- }
-
- public static void main(String[] args) throws Exception {
- Log.println("准备创建UDP Socklet ,端口是:" + port);
- DatagramSocket socket = new DatagramSocket(port);//参加Socket关键字
- Log.println("UDP Socket 创建完成");
- //服务器接受请求,被动接收,所以是一直处在运行态
- while (true){
- //1,参加Socket
- //参加容器接收数据,1024缓冲的大小
- byte[] buf = new byte[1024];
- //参加获取请求的数据包
- DatagramPacket receivePacket = new DatagramPacket(buf,buf.length);
- //获取从客户端发来的请求,如果客户端没有发送请求,就会一直再次阻塞
-
- //2,获取请求的信息
- Log.println("准备接收请求,容器大小" + buf.length);
- socket.receive(receivePacket);
- Log.println("接受到请求");
- //获取对方的IP和port
- InetAddress address = receivePacket.getAddress();
- int port1 = receivePacket.getPort();
- Log.println("请求方的端口 : " + port1);
- Log.println("请求方的IP : " + address);
- //获取对方的IP+port
- SocketAddress socketAddress = receivePacket.getSocketAddress();
- //获取请求内容
- byte[] data = receivePacket.getData();
- //获取接收数据的大小
- int length = receivePacket.getLength();
- Log.println("接收到请求的长度 : " + length);
- //3,解析请求
- //字符集解码
- String request = new String(data,0,length,"UTF-8");
- String engWord = request;
- Log.println("接受到请求的数据为 : " + engWord);
-
- //4,执行业务
- String chiWord = tranlaste(engWord);
- Log.println("翻译后的结果为: " + chiWord);
- //5,封装响应
- String response = chiWord;
- byte[] sendBuf = response.getBytes("UTF-8");
- //发送数据,要将接收方的信息一并封装到包中socketAddress
- DatagramPacket responsePacket = new DatagramPacket(sendBuf,0,sendBuf.length,socketAddress);
- Log.println("接收方发送响应 ... ");
-
- //6,发送响应
- socket.send(responsePacket);
- Log.println("接收方发送响应成功,发送的数据为 : " + chiWord);
- //此时请求完成,等待下一次响应
- }
- //服务器关闭时,关闭所有的套接字资源
- // socket.close();
- }
-
- private static String tranlaste(String engWord) {
- String chiWord = map.getOrDefault(engWord,"查无此词");
- return chiWord;
- }
- }
UDP客户端
- package 网络编程.udp;
-
- import 网络编程.Log;
-
- import java.io.UnsupportedEncodingException;
- import java.net.*;
- import java.util.Scanner;
-
- public class ClientVersion1 {
- public static final int port = 8888;
- public static void main(String[] args) throws Exception {
- //1,创建DatagramSocket
- Log.println("开始创建UDP Socket");
- DatagramSocket socket = new DatagramSocket(port);
- Log.println("UDP Socket创建完成");
- Scanner sc = new Scanner(System.in);
- System.out.print("输入要翻译的单词:");
-
- while (sc.hasNextLine()){
- //2,创建请求数据包
- String request = sc.nextLine();
- Log.println("请求的数据为" + request);
- byte[] buf = request.getBytes("UTF-8");
- Log.println("准备创建请求数据包");
- DatagramPacket requestPacket = new DatagramPacket(buf,0,buf.length, InetAddress.getLoopbackAddress(),TranslateServer.port);
- Log.println("请求数据包创建完成");
-
- //3,发送请求
- Log.println("发送请求");
- socket.send(requestPacket);
- Log.println("请求发送成功");
-
- //4,接收响应
- byte[] bytes = new byte[1024];
- DatagramPacket responsePacket = new DatagramPacket(bytes,bytes.length);
- Log.println("开始接收响应");
- socket.receive(responsePacket);
- byte[] data = responsePacket.getData();
- String res = new String(data,0,responsePacket.getLength(),"UTF-8");
- Log.println("请求结果为:" + res);
- Log.println("对方的IP为" + requestPacket.getAddress());
- Log.println("对方的端口为" + requestPacket.getPort());
- System.out.print("输入要翻译的单词:");
- }
-
- //5,关闭资源
- socket.close();
- Log.println("关闭Socket");
- }
- }
Log日志打印
- package 网络编程;
-
- import java.text.DateFormat;
- import java.time.LocalDateTime;
- import java.time.ZoneId;
- import java.time.format.DateTimeFormatter;
-
- //日志类
- public class Log {
- public static void println(Object o){
- LocalDateTime localDateTime =LocalDateTime.now(ZoneId.of("Asia/Shanghai"));//该类的构造方法私有,通过静态方法获取时间戳
- DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
- String time = format.format(localDateTime);
- //所以要打印的信息就是时间+对象信息
- System.out.println(time +" : "+ (o == null ? null : o.toString()));
- }
-
- public static void main(String[] args) {
- println(null);
- }
- }
tcp有连接,需要先建立连接,才能进行通信

构造方法,socket的构造方法是给客户端使用的,因为服务区的socket是通过accept()获取的
服务器,返回建立连接的socket对象,服务器的socket是通过accept()方法获取的

关闭连接

一旦建立连接,就只区分发送方和接收方
返回对方的连接地址

返回套接字的输入输出流


因为TCP区分客户端与服务器,
服务器需要参加SeverSocket对象来获取客户端的Socket连接对象,通过serverSocket.accept()获取Socket,在获取socket的输入输出流,通过输入输出流进行现场通信
客户端参加链接则需要使用Socket类,将自己的IP和对方的端口号构造一个socket对象,来建立连接,发送请求使用输出流,接收响应使用输入流。
TCP 长连接多线程服务器:
- package 网络编程.tcp;
-
- import 网络编程.Log;
-
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.HashMap;
- import java.util.Scanner;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
-
- public class TCPService {
- public static final int port = 9999;
- private static HashMap
map = new HashMap<>(); - static {
- map.put("apple","苹果");
- map.put("book","书");
- map.put("bicycle","自行车");
- Log.println("字典初始化完成");
- }
-
- private static class Task implements Runnable{
- private static Socket socket;
- public Task(Socket socket){
- this.socket = socket;
- }
- @Override
- public void run() {
- try {
- Log.println("准备获取请求方法信息");
- Log.println("请求方IP:" + socket.getInetAddress());//获取客户端的IP)
- Log.println("请求方端口:" + socket.getPort());//获取客户端的端口
- Log.println("请求方IP + port :" + socket.getRemoteSocketAddress());//获取客户端的IP+port
-
- //3,获取客户端的输入流
- InputStream inputStream = socket.getInputStream();
- Log.println("请求方的输入流:" + inputStream);
- Scanner sc = new Scanner(inputStream);//将得到的输入流封装到Scanner,输入流都用Scanner就可以接受
-
- //5,响应请求
- OutputStream outputStream = socket.getOutputStream();
- Log.println("请求方的输出流 :" + outputStream);
- OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
- PrintWriter writer = new PrintWriter(outputStreamWriter);
- while (true) {
- if (!sc.hasNextLine()) {
- break;
- }
- String request = sc.nextLine();//获取到数据
- String engLish = request;
- Log.println("请求内容 :" + request);
-
- //4,执行业务
- String chiWord = translate(engLish);
- String response = chiWord;
-
- //5
- writer.printf("%s\r\n", response);
- writer.flush();
- Log.println("开始响应请求");
- Log.println("响应内容:" + response);
- }
- //6,释放socket资源
- socket.close();
- Log.println("释放请求方socket");
- }catch (Exception e){
- throw new RuntimeException(e);
- }
- }
- }
-
- public static void main(String[] args) throws Exception{
-
- //1.创建TCP套接字
- ServerSocket serverSocket = new ServerSocket(port);
- //2,获取客户端的套接字,没有请求时会阻塞
-
- ThreadPoolExecutor pool = new ThreadPoolExecutor(3,3,10, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
- while (true){
- Log.println("等待连接建立");
- Socket socket = serverSocket.accept();
- Log.println("客户段连接建立,将新的连接提交到线程池");
- //创建新的任务提交到线程池
- pool.submit(new Task(socket));
- }
- }
- private static String translate(String engWord) {
- String chiWord = map.getOrDefault(engWord,"查无此词");
- return chiWord;
- }
- }
TCP客户端
- package 网络编程.tcp;
-
- import 网络编程.Log;
-
- import java.io.*;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Scanner;
-
- public class TCPClient {
- public static void main(String[] args) throws Exception{
- //客户端不需要SeverSocket,直接创建Socket对象
- //1,创建Socket并将其连接到指定的IP下的指定端口,即连接到服务器
- Log.println("准备与服务器创建连接");
- Socket socket = new Socket("127.0.0.1",9999);
-
- InputStream inputStream = socket.getInputStream();
- Scanner socInput = new Scanner(inputStream,"UTF-8");
-
- OutputStream outputStream = socket.getOutputStream();
- OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,"UTF-8");
- PrintWriter writer = new PrintWriter(outputStreamWriter);
-
- Log.println("连接建立成功,请输入英文:");
- //2,输入请求
- Scanner sc = new Scanner(System.in);
- while (true){
- if (!sc.hasNextLine()){
- break;
- }
- String engWord = sc.nextLine();
- String request = engWord + "\r\n";
-
- //3,将请求保存到输出流,发送请求
- Log.println("准备发送请求");
- writer.write(request);
- writer.flush();
- Log.println("请求发送成功");
-
- //4,接受响应
- Log.println("准备接受响应");
- String response = socInput.nextLine();//nextLine返回的值,直接将换行去掉
- Log.println("请求:" + engWord);
- Log.println("响应:" + response);
- }
- //5,关闭资源
- socket.close();
- Log.println("关闭资源");
- }
- }
