导言:
公司的代码,本来客户端client是前端的unity发送请求的,后面自己写的时候需要在本地进行测试,所以也用Java实现了前端,就写出来记录一下。
本文主要展示客户端client跟服务端server基础代码,里面的一些业务逻辑没有进行展示
正文
1.创建client端
首先我们需要创建一个client端进行发送消息,以下是实现的步骤:
- 创建一个Socket对象,并指定Server端的IP地址和端口号。
- 实现业务逻辑,发送需要传输的消息。
- 使用Socket对象的getInputStream()方法获取输入流,可以读取Server端的响应消息。
- 关闭Socket连接和相关资源。
2.创建server端
创建一个server端进行处理client发过来的请求,以下是实现的步骤:
- 创建一个ServerSocket对象,并指定监听的端口号。
- 使用accept()方法监听客户端的连接请求,并为每个连接创建一个新的线程进行处理。
- 在线程中,使用Socket对象的getInputStream()方法获取输入流,可以读取客户端发送的消息。
- 实现业务逻辑,处理收到的消息。
- 使用Socket对象的getOutputStream()方法获取输出流,可以向客户端发送响应消息。
- 关闭Socket连接和相关资源。
3.多线程模式
Server端实现中,为每个连接创建了一个新的线程来处理消息。这样可以并发地处理多个客户端的请求,提高通信效率。以下是实现的步骤:
- 创建一个Runnable接口的实现类,实现run()方法。
- 在run()方法中实现Server端的逻辑:监听客户端连接、接收消息、处理消息、发送响应。
- 在Server端的主线程中,创建一个线程池(ThreadPoolExecutor)来管理线程的执行。
- 使用线程池的execute()方法提交任务,每次有新的连接请求时,创建一个新的任务并执行。
- 关闭线程池和相关资源。
代码举例
接收TCP消息
import java.io.*;
import java.net.*;
@Component
public class TcpServer {
public void startServer() {
try {
ServerSocket serverSocket = new ServerSocket(1234);
System.out.println("Server started. Waiting for client to connect...");
while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected: " + clientSocket);
// 创建线程处理客户端连接
Thread thread = new Thread(new ClientHandler(clientSocket));
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static class ClientHandler implements Runnable {
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String message = reader.readLine();
System.out.println("Received message from client: " + message);
// 处理消息(这里仅做回显)
// 一般情况下是执行一些业务代码
String response = "Server: " + message.toUpperCase() + "\n";
writer.write(response);
writer.flush();
System.out.println("Sent response to client: " + response);
// 关闭连接
socket.close();
System.out.println("Connection closed");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
同步发送TCP消息
import java.io.*;
import java.net.*;
@Component
public class Client {
public void startClient() {
try (Socket socket = new Socket("localhost", 1234);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"))) {
System.out.println("Connected to server");
String message = "Hello from client";
writer.write(message);
writer.newLine();
writer.flush();
System.out.println("Sent message to server: " + message);
String response = reader.readLine();
System.out.println("Received response from server: " + response);
System.out.println("Connection closed");
} catch (IOException e) {
e.printStackTrace();
}
}
}
异步发送TCP消息
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
@COmponent
public class Client {
public void startClient() {
try {
// localhost是server端的ip地址,可以通过Windows+R输入cmd,然后输入ipconfig查询到
Socket socket = new Socket("localhost", 12345);
System.out.println("Connected to server");
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Void> sendFuture = executorService.submit(() -> {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))) {
String message = "Hello from client";
writer.write(message);
writer.newLine();
writer.flush();
System.out.println("Sent message to server: " + message);
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
Future<String> receiveFuture = executorService.submit(() -> {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String response = reader.readLine();
System.out.println("Received response from server: " + response);
return response;
} catch (IOException e) {
e.printStackTrace();
return null;
}
});
// 等待发送和接收完成
sendFuture.get(5, TimeUnit.SECONDS); // 设置超时时间为5秒
String response = receiveFuture.get(5, TimeUnit.SECONDS);
// 关闭连接
socket.close();
executorService.shutdown();
System.out.println("Connection closed");
} catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
}
}
在这个异步模式的示例中,我们使用了ExecutorService来创建一个线程池,并利用submit()方法异步地执行发送和接收任务。Future对象用于获取异步任务的结果。
通过这种方式,客户端可以并行地发送消息和接收响应,提高通信效率。请注意,在实际应用中,您可能需要根据具体需求对代码进行修改和调整以满足项目的要求。
在上述示例中,Server端监听1234端口,接受客户端的连接请求,并为每个连接创建一个新的线程进行处理。Client端连接到Server端,发送消息,并接收Server端的响应。Server端仅将客户端发来的消息转换为大写并回显。
请注意,在实际应用中,需要根据具体需求对代码进行修改和扩展,以满足更复杂的场景和功能要求。
以上代码是在开发场景中,不能直接启动,可以将代码中方法名改为mian方法,也可以运用到实际项目中,然后在启动类上注入类,然后调用类的方法名。
代码分析
server端分析
-
TcpServer 类:
TcpServer
类负责启动服务器,并监听来自客户端的连接请求。startServer
方法通过创建一个ServerSocket
对象来监听端口,然后在一个无限循环中等待客户端连接。- 每当有客户端连接时,会创建一个新的
ClientHandler
线程来处理该客户端的通信。 main
方法用于启动服务器,创建TcpServer
实例并调用startServer
方法。
-
ClientHandler 类:
ClientHandler
类实现了Runnable
接口,用于处理单个客户端的通信。- 在构造函数中接收一个
Socket
对象,该对象代表与客户端的连接。 - 在
run
方法中,通过BufferedReader
从客户端读取数据,并使用BufferedWriter
向客户端发送响应。 - 处理逻辑中,服务器简单地将客户端发送的消息转换为大写,并添加前缀 "Server: " 后发送回客户端。
- 最后关闭与客户端的连接。
-
线程处理:
- 每个客户端连接都会在单独的
ClientHandler
线程中进行处理,这样可以同时处理多个客户端的请求。 - 通过在
TcpServer
类的startServer
方法中创建新线程来处理客户端连接,确保了服务器可以同时处理多个客户端请求。
- 每个客户端连接都会在单独的
-
异常处理:
- 代码中进行了基本的异常处理,捕获了可能发生的
IOException
,并在出现异常时打印堆栈跟踪信息。
- 代码中进行了基本的异常处理,捕获了可能发生的
-
数据传输:
服务器从客户端读取一行数据,然后将处理后的响应发送回客户端,并在响应末尾添加换行符以确保数据完整性
client端分析
-
Socket 连接和线程池:
- 通过
Socket socket = new Socket("localhost", 12345);
建立到本地主机端口 12345 的 socket 连接。 - 使用
ExecutorService executorService = Executors.newCachedThreadPool();
创建了一个具有缓存的线程池,用于执行发送和接收消息的任务。
- 通过
-
发送消息任务:
sendFuture
是一个Future<Void>
对象,表示发送消息的任务。在该任务中,通过BufferedWriter
向 socket 输出流写入消息。
-
接收响应任务:
receiveFuture
是一个Future<String>
对象,表示接收响应的任务。在该任务中,通过BufferedReader
从 socket 输入流读取响应消息。
-
等待任务完成和超时处理:
- 通过
sendFuture.get(5, TimeUnit.SECONDS);
和receiveFuture.get(5, TimeUnit.SECONDS);
等待发送和接收任务完成,并设置了 5 秒的超时时间。
- 通过
-
关闭连接和线程池:
- 在任务完成后,关闭了 socket 连接和线程池。
-
异常处理:
- 使用
try-catch
块捕获了可能抛出的IOException
、InterruptedException
、ExecutionException
和TimeoutException
,并在捕获到异常时打印异常堆栈信息。
- 使用
结论
希望本文对您理解如何在Java中实现TCP方式发送和接收Socket消息以及多线程模式有所帮助。如有任何疑问,请随时向我提问。如果您喜欢本文,欢迎点赞、收藏。