一、TCP交互以及Socket API调用
三次握手建立TCP以后开始传输数据。
![](https://img2020.cnblogs.com/blog/737467/202011/737467-20201125000147213-287284156.png )
Accept后对于服务端来说整个socket创建完毕,直接进入read状态。read是一个阻塞调用,所谓阻塞是指服务器进入等待,直到read返回。
read其实是的主要时间是等待数据ready:
- 客户端发送后,有可能发送端的发送窗口已经满了,需要等待,记为T1。
- 进入发送窗口后,TCP传输本身需要时间,T2。
- 接收端数据后从内核拷贝到用户空间也需要时间,T3
所以read阻塞的时间至少需要T1+T2+T3,其中T1+T2的时间为等待数据的时间
二、进程读取数据的过程
![](https://img2020.cnblogs.com/blog/737467/202011/737467-20201125000200253-1745765790.png)
三、java演示
接下来用java源码来演示BIO的过程,为了看到效果特意在客户端加入了两次sleep来标识客户端IO的时间,而服务端的read却要一直等待阻塞在这个客户端的IO上。
服务端代码
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SocketBioServer {
static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress("localhost", 8080));
while (true) {
Socket socket = serverSocket.accept();
System.out.println(time() + "->accepted, begin to read......");
String result = readBytes(socket.getInputStream());
System.out.println(time() + "->" + result);
socket.close();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
static String readBytes(InputStream is) throws Exception {
long start = 0;
int total = 0;
long begin = System.currentTimeMillis();
int count = 0;
while ((count = is.read()) > -1) {//block,有数据写入的时候才会返回值。客户端关闭后才会返回-1
if (start == 0) {
start = System.currentTimeMillis();
}
total += count;
}
//读完数据的时间
long end = System.currentTimeMillis();
return "wait=" + (start - begin) + "ms,read=" + (end - start) + "ms,total=" + total + "bs";
}
static String time() {
return sdf.format(new Date());
}
}
客户端源码
import java.net.InetSocketAddress;
import java.net.Socket;
public class SocketBioClient {
public static void main(String[] args) {
try {
Socket s = new Socket();
s.connect(new InetSocketAddress("localhost", 8080));
Thread.sleep(5000);//模拟数据发送前的等待
s.getOutputStream().write(prepareBytes());
Thread.sleep(1000);//模拟写入数据需要的时间
s.close();//关闭后客户端才能返回-1
} catch (Exception ex) {
ex.printStackTrace();
}
}
static byte[] prepareBytes() {
byte[] bytes = new byte[1024 * 1024 * 1];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = 1;
}
return bytes;
}
}
执行结果
23:03:14->accepted, begin to read......
23:03:20->wait=5005ms,read=1002ms,total=1048576bs
通过上述可以看到read一直阻塞等待客户端的IO写入。