tcp/ip bio 部分内容

447 阅读5分钟

弱鸡开发一个,有错误麻烦指出

前置需要

需要到的命令

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

  1. 按之前的步骤启动server
  2. 启动第一个client
  3. 启动第二个client
  4. ...
  5. 查看网络状态
netstat -natp

出现

这时候就会有SYN_RECV状态的连接,表示超出了

SendBufferSize TcpNoDelay

现在的client 设置的SendBufferSize是20,但是当设置TcpNoDelay为false会进行优化,将数据统一发送;
当设置TcpNoDelay为true时,则没有优化

为true时

为false时

OOBInline TcpNoDelay

OOBInline 是先发一个用来嗅探 和TcpNoDelay的优先级同上

keepalive

和web的keepalive不一样,这里是心跳,检测长时间没有通讯的情况下,是否断连,抓包就能看得到,不上截图了