弱鸡开发一个,有错误麻烦指出
前置需要
需要到的命令
lsof -p
netstat -natp
tcpdump
jps
strace
代码(java)
server
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
public class SocketBioDemo {
// 参数
private static final int RECEIVE_BUFFER = 10;
private static final int SO_TIMEOUT = 0;
private static final boolean REUSE_ADDR = false;
// 服务启动之后 太多的链接 资源不够 等待的数量 超过拒绝
private static final int BACK_LOG = 2;
// 通讯中的参数
// 长链接
private static final boolean _KEEPALIVE = false;
// 是否优先发一个字符做嗅探
private static final boolean _OOB = false;
// netstat -natp 查看receive q 相关
private static final int _REC_BUF = 20;
// 是否重定向地址
private static final boolean _REUSE_ADDR = false;
// send buffer
private static final int _SEND_BUF = 20;
// 断开连接的速度
private static final boolean _LINGER = true;
//???
private static final int _LINGER_N = 0;
// 读取的时候 超时时间 等client多久
private static final int _TIMEOUT = 0;
// tcp优化算法 发送数据比较少可以缓冲
private static final boolean _NO_DELAY = false;
public static void main(String[] args) {
System.out.println("server");
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(11111), BACK_LOG);
serverSocket.setReceiveBufferSize(RECEIVE_BUFFER);
serverSocket.setReuseAddress(REUSE_ADDR);
serverSocket.setSoTimeout(SO_TIMEOUT);
while (true){
try {
System.in.read();
Socket client = serverSocket.accept();
System.out.println("client:" + client.getPort());
client.setKeepAlive(_KEEPALIVE);
client.setOOBInline(_OOB);
client.setReceiveBufferSize(_REC_BUF);
client.setReuseAddress(_REUSE_ADDR);
client.setSendBufferSize(_SEND_BUF);
client.setSoLinger(_LINGER, _LINGER_N);
client.setSoTimeout(_TIMEOUT);
client.setTcpNoDelay(_NO_DELAY);
new Thread(() -> {
while (true) {
InputStream in = null;
try {
in = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
char[] data = new char[1024];
int num = reader.read(data);
if (num > 0) {
System.out.println("data:" + num + "," + new String(data, 0, num));
}else if(num==0){
}else{
System.out.println("close");
client.close();
break;
}
} catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
} finally {
try {
serverSocket.close();
} catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
}catch (IOException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
client
import java.io.*;
import java.net.Socket;
public class SocketBioClient {
public static void main(String[] args) {
System.out.println("start");
try {
Socket client = new Socket("192.168.31.151",11111);
client.setSendBufferSize(20);
client.setTcpNoDelay(false);
client.setOOBInline(false);
OutputStream out = client.getOutputStream();
InputStream in = System.in;
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true){
String line = reader.readLine();
System.out.println("read:"+line);
if(line!=null){
byte[] bb = line.getBytes();
for (byte b :bb){
out.write(b);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
基本传输部分
启动抓包
//根据网卡自己设置抓包
tcpdump -nn -i ens3 port 11111
server启动阶段
启动server
java SocketBioDemo
查看网络
netstat -natp
我们会看到11111端口被监听了 出现了一个listen
这时候抓包部分是没任何消息的
查看java
jps
这时候会获取pid 大概长这样
18796 Jps
14990 SocketBioDemo
根据pid 获取文件标识符状态
lsof -p xxxx
发现目前是listen状态
client启动阶段
启动client
java SocketBioClient
抓包窗口 抓到三次握手
14:33:19.067641 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [S], seq 764802480, win 29200, options [mss 1460,sackOK,TS val 516017944 ecr 0,nop,wscale 7], length 0
14:33:19.067681 IP 192.168.31.151.11111 > 192.168.31.152.47490: Flags [S.], seq 3299156094, ack 764802481, win 1152, options [mss 1460,sackOK,TS val 516021010 ecr 516017944,nop,wscale 0], length 0
14:33:19.067983 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 516017945 ecr 516021010], length 0
三次握手的 seq ack 自行查询文档
查看网络
netstat - natp
发现不只是一个listen,现在还出现了一个 established,前面的recv-q 和send-q 还都是0,后面的pid/program name还是-,这代表内核态已经有了这个,但是没有分配给任何程序来处理,也没有任何堆积数据
client发送数据
//在client 的启动窗口下 输入任意字符 例如
abcd
查看抓包
14:33:19.067641 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [S], seq 764802480, win 29200, options [mss 1460,sackOK,TS val 516017944 ecr 0,nop,wscale 7], length 0
14:33:19.067681 IP 192.168.31.151.11111 > 192.168.31.152.47490: Flags [S.], seq 3299156094, ack 764802481, win 1152, options [mss 1460,sackOK,TS val 516021010 ecr 516017944,nop,wscale 0], length 0
14:33:19.067983 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 516017945 ecr 516021010], length 0
14:40:35.278692 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [P.], seq 1:2, ack 1, win 229, options [nop,nop,TS val 516454155 ecr 516021010], length 1
14:40:35.278744 IP 192.168.31.151.11111 > 192.168.31.152.47490: Flags [.], ack 2, win 1151, options [nop,nop,TS val 516457221 ecr 516454155], length 0
14:40:35.278998 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [P.], seq 2:5, ack 1, win 229, options [nop,nop,TS val 516454155 ecr 516457221], length 3
14:40:35.319045 IP 192.168.31.151.11111 > 192.168.31.152.47490: Flags [.], ack 5, win 1148, options [nop,nop,TS val 516457262 ecr 516454155], length 0
抓包内容由原来的三行变成了七行,因为开启OOBInline,所以发送了两次
查看网络
netstat -natp
recv-q 已经变成了4,这已经代表有堆积数据了,虽然还是没有程序来处理
server 接受数据
server 取消阻塞 这时会看到之前client发来的数据
client:47490
data:4,abcd
这时候看网络状态
netstat -natp
再用lsof查看
lsof -p xxxx
总结一下
拥塞机制
14:40:35.278692 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [P.], seq 1:2, ack 1, win 229, options [nop,nop,TS val 516454155 ecr 516021010], length 1
14:40:35.278744 IP 192.168.31.151.11111 > 192.168.31.152.47490: Flags [.], ack 2, win 1151, options [nop,nop,TS val 516457221 ecr 516454155], length 0
14:40:35.278998 IP 192.168.31.152.47490 > 192.168.31.151.11111: Flags [P.], seq 2:5, ack 1, win 229, options [nop,nop,TS val 516454155 ecr 516457221], length 3
14:40:35.319045 IP 192.168.31.151.11111 > 192.168.31.152.47490: Flags [.], ack 5, win 1148, options [nop,nop,TS val 516457262 ecr 516454155], length 0
win 是互相通知的win大小,如果server的win过小,客户端会自己阻塞,
测试
1.启动server 保持阻塞
2.启动 client 不停的输入
3.查看抓包
可以看出server侧发送的时候 ,win是一直在变小的
参数详解
BACK_LOG
- 按之前的步骤启动server
- 启动第一个client
- 启动第二个client
- ...
- 查看网络状态
netstat -natp
出现
这时候就会有SYN_RECV状态的连接,表示超出了
SendBufferSize TcpNoDelay
现在的client 设置的SendBufferSize是20,但是当设置TcpNoDelay为false会进行优化,将数据统一发送;
当设置TcpNoDelay为true时,则没有优化
为true时
为false时
OOBInline TcpNoDelay
OOBInline 是先发一个用来嗅探 和TcpNoDelay的优先级同上
keepalive
和web的keepalive不一样,这里是心跳,检测长时间没有通讯的情况下,是否断连,抓包就能看得到,不上截图了