BIO
客户端
package org.example.net.bio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;
public class BioClient {
public static void main(String[] args) throws IOException {
int port = 8888;
String ip = "127.0.0.1";
Socket socket = new Socket(ip, port);
// socket.setSoTimeout(10 * 1000);
BufferedReader in = null;
PrintWriter out = null;
System.out.println("客户端启动!");
try {
Scanner scanner = new Scanner(System.in);
while (true) {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String input = scanner.nextLine();
out.println(input);
if (input.equals("exit")) break;
System.out.println("输入完毕,等待回应");
String data = in.readLine();
System.out.println("输入完毕,有回应了");
System.out.println(new Date() + "客户端收到服务端响应内容:" + data);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
服务端
package org.example.net.bio;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.Scanner;
public class BioServer {
public static void main(String[] args) throws IOException {
int port = 8888;
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器启动!");
while (true) {
/*
try {
TimeUnit.MINUTES.sleep(1);
System.out.println("服务端从sleep中唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
Socket socket = serverSocket.accept();
System.out.println("服务器收到连接了");
new Thread(new SocketHandle(socket)).start();
}
}
}
class SocketHandle implements Runnable {
Socket socket;
SocketHandle(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader in = null;
PrintWriter out = null;
Scanner scanner = new Scanner(System.in);
try {
while (true) {
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String data = in.readLine();
if (data.equals("exit")) {
break;
}
System.out.println(new Date() + "服务端收到客户端的消息:" + data);
System.out.println("服务端接收完毕,准备发送消息");
out.println(scanner.nextLine());
System.out.println("服务端接收完毕,发送消息完毕");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("当前线程结束了");
}
}
测试场景
-
正常执行效果 客户端发送两次数据,服务端一次返回,最后客户端发送exit,服务端读取后,close连接,然后客户端close连接。
-
如果服务端accept之前一直sleep呢,在accept之前,连接状态是什么样的? 在accept之前,连接是建立状态。在accept之前,这个"连接"会放在服务端操作系统的全连接队列中。当sleep之后,服务端可以消费该连接,然后正常处理该连接上的读写操作。
-
如果"连接"在服务端的全连接队列中,此时客户端已经可以发送数据,那么服务端会返回ACK吗?
服务端可以返回ACK,这个ACK是操作系统的TCP/IP协议栈返回的。
- 如果这个数据发送成功了,由于服务端还没有accept连接,服务端无法处理这个请求,客户端等待服务端响应超时,客户端主动关闭了连接,服务端还能正常的消费全连接队列中的这个连接吗?
不影响服务端消费,因为这个连接已经在全连接队列中了。操作系统收到客户端的FIN之后,没有从全连接队列中删除。
- 如果消费成功了,那么服务端还能读取那个已经响应了ACK的请求数据吗?如果能读取成功,处理完这个请求数据之后,要返回结果的话,能写数据成功吗?
服务端可以正常读取这个数据,但是写响应时,客户端的操作系统收到这个响应数据后,会被RST掉。
上图中,虽然服务端在accept之前处于sleep状态,但是客户端依然可以三次握手成功,依然可以发送数据成功。客户端读取服务端数据超时之后,关闭连接,发送FIN。服务端accept连接之后,可以读取数据,并发送响应,但是被客户端RST。