客户端代码的实现
代码:
public class TcpEchoClient {
private Socket socket = null;
public TcpEchoClient(String serverIp,int serverPort) throws IOException {
socket = new Socket(serverIp,serverPort);
}
public void start(){
System.out.println("客户端启动!");
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
Scanner scannerConsole = new Scanner(System.in);
Scanner scannerNetwork = new Scanner(inputStream);
PrintWriter writer = new PrintWriter(outputStream)){
//这里的流程和UDP类似
while (true){
//1.从控制台读取输入的字符串
if (!scannerConsole.hasNext()){
break;
}
System.out.println("->");
String request = scannerConsole.next();
//2.把请求发给服务器,这里需要使用println来发送,为了让发送的末尾有\n
writer.println(request);
//通过flush主动刷新缓冲区,确保数据真的发出去
writer.flush();
//3.从服务器读取响应
String response = scannerNetwork.next();
//4.把响应显示出来
System.out.println(response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}
分析:
客户端退出之后,服务器就能感知到“客户端下线”操作。
客户端退出的时候,就会触发tcp的“断开连接”,服务器这边的代码能感知到,对应的Scanner能够在hasNext中返回false。
writer.println(request):IO操作都是比较低效的操作,希望能够让低效操作进行的尽量少一些。引入缓冲区(内存)先把要写入网卡的数据放到内存缓冲区中,等到攒一波之后,再统一进行发送(把多次IO合并成一次)
但是存在问题,如果发送的数据很少,由于缓冲区还没满,数据就呆在缓冲区里。
手动刷新缓冲区:flush()方法
步骤:
System.out.println("服务器启动!");
while (true){
//通过accept方法来“接听电话”,然后才能通信
Socket clientSocket = serverSocket.accept();
processConnection(clientSocket);
}
第一个客户端连上服务器之后,服务器就会从accept这里返回。(解除阻塞)进入到processConnection中。接下来就会在scanner.hasNext这里阻塞。等待客户端的请求。客户端请求到了之后,scanner.hasNext返回,继续执行,读取请求。根据请求计算相应,返回响应给客户端.....执行完上述一轮操作之后,循环回来再次继续在hasNext阻塞,等待下一个请求。
直到客户端退出之后,连接结束,此时循环才会退出。
while (true){
Scanner scanner = new Scanner(inputStream);
if(!scanner.hasNext()){
//读取完毕,客户端断开连接,就会产生读取完毕
System.out.printf("[%s:%d]客户端下线!",clientSocket.getInetAddress(),
clientSocket.getPort());
break;
}
服务器代码在执行里层循环的时候无法执行到第二次accept。
虽然第二个客户端和服务器在内核层面上建立了连接,是但是应用程序这里无法把连接拿到应用程序处理。
如果第一个客户端退出了,第二个客户端之前发的请求啥的就立即被处理,没被丢掉。
当前TCP在内核中,每个socket都是有缓冲区的。客户端发送的数据确实发了,服务器也收到了,只不过数据在服务器的接收缓冲区。一旦第一个客户端退出了,回到第一层循环,继续执行第二次accept,继续执行next就能把之前缓冲区的内容给读出来。
流程:
主循环:通过accept获取到连接,得到clientSocket。针对这个连接再进行一次循环,读取请求并解析,根据请求计算响应,把响应写回客户端。
长连接:每个客户端和服务器之间进行了多次的交互操作。
短连接:一个连接只进行一次请求响应交互。