习题
以下是这些 TCP 通信练习题的 Java 代码实现及解析:
TCP 通信练习 1 - 多发多收
客户端(Client1.java)
java
import java.io.IOException; import java.io.OutputStream; import java.net.Socket; public class Client1 {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 8888)) {OutputStream os = socket.getOutputStream();String[] messages = {"Hello", "World", "Java"};for (String message : messages) {os.write(message.getBytes());os.flush();}} catch (IOException e) {e.printStackTrace();}} }
服务器(Server1.java)
java
import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class Server1 {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888);Socket socket = serverSocket.accept();InputStream is = socket.getInputStream()) {byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {String message = new String(buffer, 0, len);System.out.println("收到客户端消息: " + message);}} catch (IOException e) {e.printStackTrace();}} }
解析:
-
客户端:创建 Socket 连接到本地主机的 8888 端口,获取输出流,循环发送多个字符串消息。
-
服务器:创建 ServerSocket 监听 8888 端口,接受客户端连接后获取输入流,通过循环读取输入流数据,直到没有数据可读,打印接收到的消息。
TCP 通信练习 2 - 接收和反馈
客户端(Client2.java)
java
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class Client2 {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 8888)) {OutputStream os = socket.getOutputStream();os.write("你好".getBytes());os.flush(); InputStream is = socket.getInputStream();byte[] buffer = new byte[1024];int len = is.read(buffer);String feedback = new String(buffer, 0, len);System.out.println("收到服务器反馈: " + feedback);} catch (IOException e) {e.printStackTrace();}} }
服务器(Server2.java)
java
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Server2 {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888);Socket socket = serverSocket.accept();InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream()) {byte[] buffer = new byte[1024];int len = is.read(buffer);String message = new String(buffer, 0, len);System.out.println("收到客户端消息: " + message); os.write("已收到".getBytes());os.flush();} catch (IOException e) {e.printStackTrace();}} }
解析:
-
客户端:连接到服务器,发送一条消息后,等待接收服务器反馈并打印。
-
服务器:接受客户端连接,读取客户端发送的消息并打印,然后向客户端发送反馈消息。
TCP 通信练习 3 - 上传文件
客户端(Client3.java)
java
import java.io.*; import java.net.Socket; public class Client3 {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 8888);FileInputStream fis = new FileInputStream("test.txt");OutputStream os = socket.getOutputStream()) {byte[] buffer = new byte[1024];int len;while ((len = fis.read(buffer)) != -1) {os.write(buffer, 0, len);}socket.shutdownOutput(); InputStream is = socket.getInputStream();byte[] feedbackBuffer = new byte[1024];int feedbackLen = is.read(feedbackBuffer);String feedback = new String(feedbackBuffer, 0, feedbackLen);System.out.println(feedback);} catch (IOException e) {e.printStackTrace();}} }
服务器(Server3.java)
java
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server3 {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888);Socket socket = serverSocket.accept();InputStream is = socket.getInputStream();FileOutputStream fos = new FileOutputStream("received.txt")) {byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {fos.write(buffer, 0, len);}OutputStream os = socket.getOutputStream();os.write("文件上传成功".getBytes());os.flush();} catch (IOException e) {e.printStackTrace();}} }
解析:
-
客户端:连接服务器,读取本地文件并通过 Socket 输出流发送给服务器,发送完毕后关闭输出流,然后接收服务器反馈。
-
服务器:监听端口接受连接,通过输入流读取客户端发送的数据写入新文件,文件接收完毕后向客户端发送反馈。
TCP 通信练习 4 - 上传文件(文件名重复问题)
解决方案思路
在服务器端接收文件时,检查文件名是否已存在,如果存在可以在文件名后添加序号或时间戳等方式进行重命名。以下是简单示例代码在 Server3 基础上修改:
java
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; public class Server4 {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888);Socket socket = serverSocket.accept();InputStream is = socket.getInputStream()) {String fileName = "received.txt";File file = new File(fileName);int count = 1;while (file.exists()) {String filePrefix = fileName.substring(0, fileName.lastIndexOf('.'));String fileSuffix = fileName.substring(fileName.lastIndexOf('.'));fileName = filePrefix + "(" + count + ")" + fileSuffix;file = new File(fileName);count++;}try (FileOutputStream fos = new FileOutputStream(file)) {byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {fos.write(buffer, 0, len);}OutputStream os = socket.getOutputStream();os.write("文件上传成功".getBytes());os.flush();}} catch (IOException e) {e.printStackTrace();}} }
解析:在服务器接收文件前,先检查文件名是否存在,若存在则通过添加序号的方式生成新文件名,再进行文件接收操作。
TCP 通信练习 5 - 上传文件(多线程版)
服务器(Server5.java)
java
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server5 {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888)) {while (true) {Socket socket = serverSocket.accept();new Thread(() -> {try (InputStream is = socket.getInputStream();FileOutputStream fos = new FileOutputStream("received_" + System.currentTimeMillis() + ".txt")) {byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {fos.write(buffer, 0, len);}System.out.println("文件接收成功");} catch (IOException e) {e.printStackTrace();}}).start();}} catch (IOException e) {e.printStackTrace();}} }
客户端(可复用 Client3.java)
解析:服务器通过无限循环接受客户端连接,每接收到一个连接就创建一个新线程来处理文件接收,这样可以同时处理多个客户端的上传请求。
TCP 通信练习 6 - 上传文件(线程池优化)
服务器(Server6.java)
java
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Server6 {private static final int THREAD_POOL_SIZE = 5;private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE); public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888)) {while (true) {Socket socket = serverSocket.accept();executorService.submit(() -> {try (InputStream is = socket.getInputStream();FileOutputStream fos = new FileOutputStream("received_" + System.currentTimeMillis() + ".txt")) {byte[] buffer = new byte[1024];int len;while ((len = is.read(buffer)) != -1) {fos.write(buffer, 0, len);}System.out.println("文件接收成功");} catch (IOException e) {e.printStackTrace();}});}} catch (IOException e) {e.printStackTrace();}} }
客户端(可复用 Client3.java)
解析:使用线程池(这里是固定大小线程池)来管理线程,避免频繁创建和销毁线程,提高系统资源利用率,当有客户端连接时,将任务提交给线程池处理。
TCP 通信练习 7 - BS(接收浏览器的消息并打印)
服务器(Server7.java)
java
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server7 {public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8080);Socket socket = serverSocket.accept();InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is))) {String line;while ((line = br.readLine()) != null) {System.out.println(line);if (line.isEmpty()) {break;}}} catch (IOException e) {e.printStackTrace();}} }
解析:服务器监听 8080 端口(常用 HTTP 端口),接受浏览器连接后,通过 BufferedReader 逐行读取浏览器发送的 HTTP 请求消息并打印,当读取到空行时(HTTP 请求头结束标志)停止读取。
TCP 通信练习 8 - 控制台版的聊天室
客户端(ChatClient.java)
java
import java.io.*; import java.net.Socket; import java.util.Scanner; public class ChatClient {public static void main(String[] args) {try (Socket socket = new Socket("localhost", 8888);OutputStream os = socket.getOutputStream();PrintWriter pw = new PrintWriter(os, true);InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));Scanner scanner = new Scanner(System.in)) {new Thread(() -> {try {String message;while ((message = br.readLine()) != null) {System.out.println(message);}} catch (IOException e) {e.printStackTrace();}}).start(); String input;while ((input = scanner.nextLine()) != null) {pw.println(input);if ("exit".equals(input)) {break;}}} catch (IOException e) {e.printStackTrace();}} }
服务器(ChatServer.java)
java
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class ChatServer {private static List<PrintWriter> writers = new ArrayList<>(); public static void main(String[] args) {try (ServerSocket serverSocket = new ServerSocket(8888)) {while (true) {Socket socket = serverSocket.accept();new Thread(() -> {try (InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));OutputStream os = socket.getOutputStream();PrintWriter pw = new PrintWriter(os, true)) {writers.add(pw);String message;while ((message = br.readLine()) != null) {for (PrintWriter writer : writers) {writer.println(message);}if ("exit".equals(message)) {writers.remove(pw);break;}}} catch (IOException e) {e.printStackTrace();}}).start();}} catch (IOException e) {e.printStackTrace();}} }
解析:
-
客户端:创建 Socket 连接服务器,启动一个线程用于接收服务器转发的消息并打印,通过 Scanner 读取控制台输入,将输入消息发送给服务器,输入 “exit” 时退出。
-
服务器:监听端口接受客户端连接,为每个连接创建新线程,将每个客户端的输出流(PrintWriter)保存到列表中,读取客户端发送的消息并转发给所有客户端,当客户端发送 “exit” 时从列表中移除该客户端的输出流。
以上代码基于 Java 语言实现,在实际运行时,需根据情况调整端口等配置,并且要注意异常处理等方面的优化。