1、核心类
ServerSocket :服务器使用的socket
Socket : 服务器和客户端都会使用的socket
accept进行的工作是拉客
对应操作系统来说,建立TCP连接是内核的工作
accept要干的就是等连接建立好了,把这个连接给拿到应用程序中。
如果当前连接还没建立,accept就会阻塞,accept相当于,有人给你打电话了,你按下接听键!如果没人打电话,就阻塞等待。
一、回显服务器
1、服务器
注:
1、代码中,使用了 clientSocket 用完之后,需要关闭。
和代码中 ServerSocket 生命周期不一样,ServerSocket的生命周期跟随整个程序, clientSocket生命周期,只是当前连接。就应该在连接之后,把这里的 socket 关闭,不关就会造成资源泄露。ServerSocket只有一个,clientSocket 会有无数个,每个客户端的连接,都是一个。
2、利用多线程,来实现多个客户端同时运行
一个线程调用一个processConnect。
3、通过线程池,来解决频繁创建销毁线程的问题
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;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!");//创建线程池ExecutorService service = Executors.newCachedThreadPool();while (true){Socket clientSocket = serverSocket.accept();//[版本1] 单线程版本,存在bug,无法处理多个客户端//不是直接调用,而是创建一个新的线程,让新的线程来调用。//processConnect(clientSocket);//[版本2] 多线程版本,主线程负责拉客,新线程负责通信//虽然比版本1有提升,但是涉及到频繁创建销毁线程,在高并发的情况下,负担是比较重的
// Thread T = new Thread(() -> {
// try {
// processConnect(clientSocket);
// } catch (IOException e) {
// e.printStackTrace();
// }
// });
// T.start();//[版本3] 使用线程池,来解决频繁创建销毁线程的问题service.submit(new Runnable() {@Overridepublic void run() {try {processConnect(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}//通过这个方法,给当前连上的这个客户端,提供服务//一个连接过来了,服务方式可能有两种://1、一个连接只进行一次数据交互(一个请求+一个响应) 短连接//2、一个连接进行多次数据交互(N个请求 + N个响应) 长连接//此处写长连接的版本,短连接就是长连接去掉while循环public void processConnect(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 建立连接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){Scanner scanner = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);//这里是长连接的写法,需要while来获取多次交互情况while (true){if(!scanner.hasNext()){//读完了,就断开连接。即当客户端断开连接的时候,此时 hasnext 就会返回 false//读到EOF的时候(end of file)返回false,当客户端进程退出的时候,也就会触发socket文件关闭,此时服务器这边尝试读取请求,就会读到EOFSystem.out.printf("[%s:%d] 断开连接\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}//1、读取请求并解析String request = scanner.next();//2、根据请求计算响应String response = process(request);//3、把响应写回给客户端printWriter.println(response);//4、刷新一下缓冲区,避免数据没有真的发出去printWriter.flush();System.out.printf("[%s:%d] req:%s;resp:%s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}}finally {clientSocket.close();}}public String process(String req){return req;}public static void main(String[] args) throws IOException {TcpEchoServer echoServer = new TcpEchoServer(8000);echoServer.start();}
}
2、客户端
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;public class TcpEchoClient {private Socket socket = null;public TcpEchoClient() throws IOException {//new这个对象的时候,需要和服务器建立连接 建立连接,就得知道,服务器在哪里//new Socket的过程就是建立连接的过程socket = new Socket("127.0.0.1",8000);}public void start() throws IOException {//由于实现的是长连接,一个连接会处理 N 个请求和响应,需要加上while循环//从键盘输入Scanner scanner = new Scanner(System.in);try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//从网络上输入Scanner scannerNet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while(true){//1、从控制台读取用户输入System.out.print("> ");String request = scanner.next();//2、把请求发送给服务器printWriter.println(request);printWriter.flush();//3、从服务器读取响应String response = scannerNet.next();//4、把结果显示到界面上System.out.printf("req:%s resp:%s\n",request,response);}}}public static void main(String[] args) throws IOException {TcpEchoClient echoClient = new TcpEchoClient();echoClient.start();}
}
二、翻译服务器
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class TcpDictServer extends TcpEchoServer{private Map<String,String> dict = new HashMap<>();public TcpDictServer(int port) throws IOException {super(port);dict.put("cat","小猫");dict.put("dog","小狗");dict.put("宝贝","瑾瑾");}@Overridepublic String process(String req){return dict.getOrDefault(req,"俺也不知道");}public static void main(String[] args) throws IOException {TcpDictServer server = new TcpDictServer(8000);server.start();}
}