【网络编程】——基于TCP协议实现回显服务器及客户端
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;
private ExecutorService service = Executors.newCachedThreadPool();
// 绑定端口号
public TcpEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
// 启动服务器
public void start() throws IOException {
System.out.println("服务器启动!!!");
while(true) {
// 注意这里和Udp不同的是,Tcp进入循环之后并不是读取客户端请求,而是先处理客户端的连接
// Tcp是一个有连接的协议(Udp是没有连接的协议),所以有连接的话会优先处理连接
// 连接可以理解为客户端和服务器彼此之间保留对方的信息
// 一个服务器要应对很多客户端,在服务器内核中有很多客户端的连接,在应用程序层面我们需要对这些连接进行一一处理
// 这里服务器内核中的连接就像一个一个的待办事项一样,这些待办事项在队列这样的数据结构中
// 所以应用程序需要一一完成这样的任务
Socket clientSocket = serverSocket.accept();
// Thread t = new Thread(() -> {
// processConnection(clientSocket);
// });
// ();
service.submit(new Runnable() {
@Override
public void run() {
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 scanner = new Scanner(inputStream);
if(!scanner.hasNext()) {
// 读取完毕客户端下线
System.out.printf("[%s:%d] 客户端下线!!!\n]",clientSocket.getInetAddress().toString(),clientSocket.getPort());
break;
}
// 这里我们约定客户端输入来的请求是文本数据,同时以空白符作为分割
String request = scanner.next();
// 第二步:根据请求计算响应
String response = process(request);
// 第三步:把响应写回给客户端,把OutputStream使用PrinterWriter进行包装,方法进行数据的传输
PrintWriter writer = new PrintWriter(outputStream);
writer.println(response);
// 刷新缓冲区
writer.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) {
// 这里必须确保Socket能够被关闭
e.printStackTrace();
}
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}