提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- Tcp的api展示
- 服务端
- 客户端
- 存在的问题
- 解决问题
- 服务端和客户端的大致流程
前言
上一篇文章我们介绍了UCP的客户端与服务器的一个简单实例,这篇我们简单的来介绍TCP版本的server和client的一个简单交互
Tcp的api展示
服务端
服务端的要做的流程
1.读取请求
2.根据请求计算响应
3.把响应写回给客户端
下面是完整代码
public class TpcEchoServer {
//serverSocket就是外场拉课的小哥
//clientSocket就是内场服务的小姐姐
//serverSocket只有一个,clientSocket会给每个客户分配一个
private ServerSocket serverSocket = null;
public TpcEchoServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
//启动过程
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService executorService = Executors.newCachedThreadPool();
//因为我们服务器要反复读取一个服务,所以我们要使用循环
while (true) {
Socket clientSocket = serverSocket.accept();
processConnection(clientSocket);
private void processConnection(Socket clientSocket) throws IOException {
System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort());
//try()这种写法种,允许写多个流对象.使用;来分割
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
//我们使用prinwriter把字节流包装秤字符流
Scanner scanner = new Scanner(inputStream);
PrintWriter printWriter = new PrintWriter(outputStream);
while (true) {
//1.读取请求
if (!scanner.hasNext()) {
//读到流的结尾了.
System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress().toString(),
clientSocket.getPort());
break;
}
//直接使用scanner读取一段字符串
String request = scanner.next();
//2.根据请求计算响应
String response = process(request);
//3.把响应写回给客户端
printWriter.println(response);
printWriter.flush();
System.out.printf("[%s:%d] req: %s; resp : %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
clientSocket.close();
}
}
public String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TpcEchoServer tcpEchoServer = new TpcEchoServer(9090);
tcpEchoServer.start();
}
}
我针对几个方法具体解释一下.
private ServerSocket serverSocket = null;
Socket clientSocket = serverSocket.accept();
处理代码代码的解释:
客户端
客户端的一个流程
1.从键盘上读取用户输入内容
2.讲读取的内容放给服务器
3.从服务器读取响应内容
4.把响应内容显示在控制台上
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 port) throws IOException {
//这个操作相当于让客户端和服务器建立tcp连接.
//这里连接上了,socket就会返回
socket = new Socket(serverIp, port);
}
//开始启动客户端
public void start() {
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream())
{ PrintWriter printWriter = new PrintWriter(outputStream);
Scanner scannerFromSocket = new Scanner(inputStream);
while (true){
//1.从键盘上读取用户输入内容
System.out.println("->");
String request= scanner.next();
//2.讲读取的内容放给服务器
printWriter.println(request);
printWriter.flush();
//3.从服务器读取响应内容
String response=scannerFromSocket.next();
//4.把响应内容显示在控制台上
System.out.printf("req: %s; resp: %s\n", request, response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);
tcpEchoClient.start();
}
}
存在的问题
这里其实存在一个问题,就是当我们多个客户端去访问服务器的时候,服务端要处理完第一个客户端,在处理第二个客户端,具体看下图.
解决问题
先来看看我们针对服务端的处理客户端的请求代码
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService executorService = Executors.newCachedThreadPool();
//因为我们服务器要反复读取一个服务,所以我们要使用循环
while (true) {
Socket clientSocket = serverSocket.accept();
processConnection(clientSocket);
}
}
改造后的代码
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService executorService = Executors.newCachedThreadPool();
//因为我们服务器要反复读取一个服务,所以我们要使用循环
while (true) {
Socket clientSocket = serverSocket.accept();
Thread t = new Thread(() -> {
try {
processConnection(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}