【70天稳扎稳打学完JavaEE初阶】
- TCP和UDP的区别及相关的不同应用
- UDP实现回显功能
- 一、UdpEchoServer服务器
- 二、UdpEchoClient 客户端
- 写一个 翻译服务器(继承服务器)
- UDP示例二:展示服务器本地某个目录的下一级子文件列表服务
- UDP服务器
- UDP客户端
- TCP示例一:一发一收(短连接)
- TcpEchoClient 客户端
- TcpEchoServer
- TCP示例二:请求响应(短连接)
- 服务器
- 客户端
TCP和UDP的区别及相关的不同应用
UDP实现回显功能
一、UdpEchoServer服务器
package UdpEchoClient;
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);
}
// 通过这个方法启动服务器.
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
// 循环里面处理一次请求.
// 1. 读取请求并解析
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(requestPacket);
// 把这个 DatagramPacket 对象转成字符串, 方便去打印.
String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
// 2. 根据请求计算响应
String response = process(request);
// 3. 把响应写回到客户端
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();
}
}
二、UdpEchoClient 客户端
package UdpEchoClient;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
// 两个参数一会会在发送数据的时候用到.
// 暂时先把这俩参数存起来, 以备后用.
public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
// 这里并不是说就没有端口, 而是让系统自动指定一个空闲的端口~~
socket = new DatagramSocket();
// 假设 serverIP 是形如 1.2.3.4 这种点分十进制的表示方式 (关于点分十进制, 后面详细说)
this.serverIP = serverIP;
this.serverPort = serverPort;
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true) {
// 1. 从控制台读取用户输入的内容.
System.out.print("-> ");
String request = scanner.next();
// 2. 构造一个 UDP 请求, 发送给服务器.
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
InetAddress.getByName(this.serverIP), this.serverPort);
socket.send(requestPacket);
// 3. 从服务器读取 UDP 响应数据. 并解析
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
// 4. 把服务器的响应显示到控制台上.
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
client.start();
}
}
写一个 翻译服务器(继承服务器)
package UdpEchoClient;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
public class UdpTranslateServer extends UdpEchoServer {
// 翻译是啥? 本质上就是 key -> value
private Map<String, String> dict = new HashMap<>();
public UdpTranslateServer(int port) throws SocketException {
super(port);
dict.put("cat", "小猫");
dict.put("dog", "小狗");
dict.put("fuck", "卧槽");
// 在这里就可以填入很多很多的内容. 像有道这样的词典程序, 也就无非如此, 只不过这里有一个非常大的哈希表, 包含了几十万个单词.
}
// 重写 process 方法, 实现查询哈希表的操作
@Override
public String process(String request) {
return dict.getOrDefault(request, "词在词典中未找到");
}
// start 方法和父类完全一样, 不用写了.
public static void main(String[] args) throws IOException {
UdpTranslateServer server = new UdpTranslateServer(9090);
server.start();
}
}
UDP示例二:展示服务器本地某个目录的下一级子文件列表服务
UDP服务器
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.nio.charset.StandardCharsets;
public class udpserver {
//服务器socket要绑定固定的端口
private static final int PORT = 8888;
//本地文件目录要展示的根路径
private static final String BASE_PATH = "E:/TMP";
public static void main(String[] args) throws IOException {
// 1.创建服务端Datagramsocket,指定端口,可以发送及接收UDP数据报
DatagramSocket socket = new DatagramSocket(PORT);
//不停的接收客户端udp数据报
while (true) {
byte[] requestData = new byte[1024];
DatagramPacket requestPacket = new DatagramPacket(requestData,requestData.length);
System.out.println("------------------------");
System.out.println("等待接收UDP数据包...");
socket.receive(requestPacket);
System.out.printf("客户端IP:%s%n",requestPacket.getAddress().getHostAddress());
System.out.printf("客户端端口号:%s%n",requestPacket.getPort());
// 7.接收到的数据作为请求,根据请求数据执行业务,并返回响应
for (int i = 0; i < requestData.length; i++) {
byte b = requestData[i];
if (b == '\3') {
String request = new String(requestData,0,i);
System.out.printf("客户端请求的文件列表路径为:%s%n",BASE_PATH+request);
File dir = new File((BASE_PATH + request));
File[] children = dir.listFiles();
StringBuilder response = new StringBuilder();
if (children != null) {
for (File child : children) {
response.append(child.getName() + "\n");
}
}
response.append("\3");
byte[] responseData = response.toString().getBytes(StandardCharsets.UTF_8);
DatagramPacket responsePacket = new DatagramPacket(responseData, responseData.length,requestPacket.getSocketAddress());
socket.send(responsePacket);
break;
}
}
}
}
}
UDP客户端
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class udpserver {
//服务端socket地址,包含域名或IP,及端口号
private static final SocketAddress ADDRESS = new InetSocketAddress("1ocalhost",8888);
public static void main(String[] args) throws IOException {
// 4.创建客户端Datagramsocket,开启随机端口就行,可以发送及接收UDP数据报
DatagramSocket socket = new DatagramSocket();
//5-1.准备要发送的数据: 这里调整为键盘输入作为发送的内容
Scanner scanner = new Scanner(System.in) ;
while (true) {
System.out.println("--------------------------");
System.out.println("请输入要展示的目录");
String request = scanner.nextLine() + "\3";
byte[] requestData = request.getBytes(StandardCharsets.UTF_8);
DatagramPacket requestPacket = new DatagramPacket(requestData,requestData.length,ADDRESS);
socket.send(requestPacket);
byte[] responseData = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(responseData,responseData.length);
socket.receive(responsePacket);
System.out.println("该目录下的文件列表为:");
int next = 0;
for (int i = 0; i < responseData.length; i++) {
byte b = responseData[i];
if (b == '\3') {
break;
}
if (b == '\n') {
String fileName = new String(responseData, next, i-next);
System.out.println(fileName);
next = i+1;
}
}
}
}
}
TCP示例一:一发一收(短连接)
TcpEchoClient 客户端
package net_1013;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TcpEchoClient {
// 客户端需要使用这个 socket 对象来建立连接.
private Socket socket = null;
public TcpEchoClient(String serverIP, int serverPort) throws IOException {
// 和服务器建立连接. 就需要知道服务器在哪了.
// 这里和上节课写的 UDP 客户端差别较大了.
socket = new Socket(serverIP, serverPort);
}
public void start() {
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
while (true) {
// 1. 从控制台读取数据, 构造成一个 请求
System.out.print("-> ");
String request = scanner.next();
// 2. 发送请求给服务器
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(request);
// 这个 flush 不要忘记. 否则可能导致请求没有真发出去.
printWriter.flush();
// 3. 从服务器读取响应
Scanner respScanner = new Scanner(inputStream);
String response = respScanner.next();
// 4. 把响应显示到界面上
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
client.start();
}
}
TcpEchoServer
package net_1013;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpEchoServer {
// 代码中会涉及到多个 socket 对象. 使用不同的名字来区分.
private ServerSocket listenSocket = null;
public TcpEchoServer(int port) throws IOException {
listenSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
ExecutorService service = Executors.newCachedThreadPool();
while (true) {
// 1. 先调用 accept 来接受客户端的连接.
Socket clientSocket = listenSocket.accept();
// 2. 再处理这个连接. 这里应该要使用多线程. 每个客户端连上来都分配一个新的线程负责处理~
// 此处使用多线程, 雀食能解决问题, 但是会导致频繁创建销毁多次线程!!
// Thread t = new Thread(() -> {
// try {
// processConnection(clientSocket);
// } catch (IOException e) {
// e.printStackTrace();
// }
// });
// t.start();
service.submit(new Runnable() {
@Override
public void run() {
try {
processConnection(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
private void processConnection(Socket clientSocket) throws IOException {
System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
// 接下来就处理客户端的请求了.
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
while (true) {
// 1. 读取请求并解析.
Scanner scanner = new Scanner(inputStream);
if (!scanner.hasNext()) {
// 读完了, 连接可以断开了.
System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort());
break;
}
String request = scanner.next();
// 2. 根据请求计算响应
String response = process(request);
// 3. 把响应写回给客户端
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(response);
// 刷新缓冲区确保数据确实是通过网卡发送出去了.
printWriter.flush();
System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort(), request, response);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 为啥这个地方要关闭 socket ? 而前面的 listenSocket 以及 udp 程序中的 socket 为啥就没 close??
clientSocket.close();
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
TCP示例二:请求响应(短连接)
服务器
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import static javax.servlet.http.MappingMatch.PATH;
class Tcpserver {
//服务器socket要绑定固定的端口
private static final int PORT = 8888;
//本地文件目录要展示的根路径
private static final String BASE_PATH = "E:/TMP";
public static void main(String[] args) throws IOException {
//1.创建一个服务端serversocket,用于收发TCP报文
ServerSocket server = new ServerSocket(PORT);
// 不停的等待客户端连接
while (true) {
System.out.println("-------------");
System.out.println("等待客户端建立TCP连接...");
Socket socket = server.accept();
System.out.printf("客户端IP: %s%n",socket.getInetAddress().getHostAddress());
System.out.printf("客户端端口号: %s%n",socket.getPort());
//5.接收客户端的数据,需要从客户端socket中的输入流获取
InputStream is = socket.getInputStream();
//为了方便获取字符串 内容,可以将以上字节流包装为字符流
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
//客户端请求只发送一行数据,我们也只需要读取一行
String request = br.readLine();
//6.根据请求处理业务:本地目录根路径+请求路径,作为要展示的目录,列出下一级子文件
System.out.printf("客户端请求的文件列表路径为: %s%n",BASE_PATH + request);
File dir = new File(BASE_PATH + request);
File[] children = dir.listFiles();
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,"UTF-8"));
if(children != null){
for (File child : children) {
pw.println(child.getName());
}
}
pw.flush() ;
socket.close();
}
}
}
客户端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import static javax.servlet.http.MappingMatch.PATH;
class TcpClient {
//服务端IP或域名
private static final String SERVER_HOST = "localhost";
//服务端socket进程的端口号
private static final int SERVER_PORT = 8888;
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("---------------");
System.out.println("请输入要展示的目录:");
String request = scanner.nextLine();
Socket socket = new Socket(SERVER_HOST,SERVER_PORT);
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(os,"UTF-8"));
pw.println(request);
pw.flush();
System.out.println("接收到服务端响应:");
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line) ;
}
socket.close();
}
}
}