使用Java进行网络编程,多线程使用 TCP实现服务器客户端之间通信,并且服务器会给客户端返回一个响应
代码如下(示例):
package Server;
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 {
private ServerSocket serverSocket = null;
//实例化ServerSocket对象serverSocket用来和客户端通信
public TcpEchoServer(int port) throws IOException {
// TcpEchoServer的构造方法
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("启动服务器");
ExecutorService threadPool = Executors.newCachedThreadPool();
//实例化ExecutorService对象threadPool用来表示客户端
// 此处使用 CachedThreadPool, 使用 FixedThreadPool 不太合适 (线程数不太应该是有固定的....)
while (true) {
// 实例化Socket对象 clientSocket 用来和具体的客户端进行交流.
Socket clientSocket = serverSocket.accept();
// 此处使用多线程来处理.
// 这里的多线程版本的程序, 最大的问题就是可能会涉及到频繁申请释放线程.
// Thread t = new Thread(() -> {
// processConnection(clientSocket);
// });
// t.start();
// 使用线程池.
threadPool.submit(() -> {
processConnection(clientSocket);
});
}
}
// 使用这个方法来处理一个连接.
// 这一个连接对应到一个客户端. 但是这里可能会涉及到多次交互.
private void processConnection(Socket clientSocket) {
System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
//答应客户端上线信息
// 基于上述 socket 对象和客户端进行通信
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
// 由于要处理多个请求和响应, 也是使用循环来进行.
while (true) {
Scanner scanner = new Scanner(inputStream);
if (!scanner.hasNext()) {
//读取请求
// 没有下个数据, 说明读完了.
System.out.printf("[%s:%d] 客户端下线! \n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
break;
}
String request = scanner.next();
//创建一个String类型的request用来存放从客户端读到的信息
//此处使用 next 是一直读取到换行符/空格/其他空白符结束, 最终返回结果里不包含上述 空白符
String response = process(request);
//创建一个String类型的response用来存放计算的响应
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(response);
//返回响应结果.
//OutputStream 没有 write String 这样的功能. 可以把 String 里的字节数组拿出来, 进行写入;
//使用println让结果中带有一个 \n 换行. 方便对端来接收解析.
printWriter.flush();
// flush 用来刷新缓冲区, 保证当前写入的数据, 雀食是发送出去了.
System.out.printf("[%s:%d] req: %s; resp: %s \n", clientSocket.getInetAddress().toString(), clientSocket.getPort(),
request, response);
//打印响应信息
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
代码如下(示例):
package Server;
//第一个客户端
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 {
private Socket socket = null;
public TcpEchoClient(String serverIp, int serverPort) throws IOException {
// Socket 构造方法, 能够识别 点分十进制格式的 IP 地址. 比 DatagramPacket 更方便.
// new 这个对象的同时, 就会进行 TCP 连接操作.
socket = new Socket(serverIp, serverPort);
}
public void start() {
System.out.println("客户端启动!");
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()) {
while (true) {
//先从键盘上读取用户输入的内容
System.out.print("> ");
String request = scanner.next();
if (request.equals("exit")) {
System.out.println("goodbye");
break;
}
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(request);
// 把读到的内容构造成请求, 发送给服务器.
printWriter.flush();
// 此处加上 flush 保证数据确实发送出去了
Scanner respScanner = new Scanner(inputStream);
String response = respScanner.next();
//读取服务器的响应
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();
}
}
---