• TCP和UDP的发送缓冲区和接收缓冲区内存问题


    TCP协议是作用是用来进行端对端数据传送的,那么就会有发送端和接收端,在操作系统有两个空间即user space和kernal space。

    每个Tcp socket连接在内核中都有一个发送缓冲区和接收缓冲区,TCP的全双工的工作模式以及TCP的流量(拥塞)控制便是依赖于这两个独立的buffer以及buffer的填充状态。

    单工:只允许甲方向乙方传送信息,而乙方不能向甲方传送 ,如汽车单行道。

    半双工:半双工就是指一个时间段内只有一个动作发生,甲方可以向乙方传送数据,乙方也可以向甲方传送数据,但不能同时进行,如一条窄马路同一时间只能允许一个车通行。

    全双工:同时允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。

    一个socket的两端,都会有send和recv两个方法,如client发送数据到server,那么就是客户端进程调用send发送数据,而send的作用是将数据拷贝进入socket的内核发送缓冲区之中,然后send便会在上层返回。

    也就是说send()方法返回之时,数据不一定会

    发送到对端即服务器上去(和write写文件有点类似),send()仅仅是把应用层buffer的数据拷贝进socket的内核发送buffer中,发送是TCP的事情,和send其实没有太大关系。

    接收缓冲区把数据缓存入内核,等待recv()读取,recv()所做的工作,就是把内核缓冲区中的数据拷贝到应用层用户的buffer里面,并返回。若应用进程一直没有调用recv()进行读取的话,此数据会一直缓存在相应socket的接收缓冲区内。对于TCP,如果应用进程一直没有读取,接收缓冲区满了之后,发生的动作是:收端通知发端,接收窗口关闭(win=0)。这个便是滑动窗口的实现。保证TCP套接口接收缓冲区不会溢出,从而保证了TCP是可靠传输。因为对方不允许发出超过所通告窗口大小的数据。 这就是TCP的流量控制,如果对方无视窗口大小而发出了超过窗口大小的数据,则接收方TCP将丢弃它。

    查看socket发送缓冲区大小,cat /proc/sys/net/ipv4/tcp_wmem

    当接收端向发送端发送零窗口报文段后不久,接收端的接收缓存又有了一些存储空间,于是接收端向发送端发送了Windows size = 2的报文段,然而这个报文段在传输过程中丢失了。发送端一直等待收到接收端发送的非零窗口的通知,而接收端一直等待发送端发送数据,这样就死锁了。
    1
    (2)解决方法

    TCP为每个连接设有一个持续计时器。只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器,若持续计时器设置的时间到期,就发送一个零窗口探测报文段(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。
    1
    TCP的Window是一个16bit位字段,它代表的是窗口的字节容量,也就是TCP标准窗口最大为2^16 -1 = 65535个字节(grpc默认值)。
    另外在TCP的选项字段中还包括了一个TCP窗口扩大因子,option-kind 为 3 ,option-length为 3 个字节,option-data取值范围 0 -14.窗口扩大因子用来扩大TCP窗口,可把原来16bit的窗口,扩大为31bit,一般在带宽比较大的时候使用,比如光纤传输。
    /********************************

    一个UDP可读缓冲区不够导致丢包的现象

    看到一篇写UDP 丢包场景的文章,其中提到如果UDP 缓冲区填满导致丢包的问题,写了个小程序验证了下,确实之前没有细究过,描述如下:

    • 数据报分片重组丢失:UDP 协议本身规定的大小是 64kb,但是在数据链路层有 MTU 的限制,大小大概在 5kb,所以当你发送一个很大的 UDP 包的时候,这个包会在 IP 层进行分片,然后重组。这个过程就有可能导致分片的包丢失。UDP 本身有 CRC 检测机制,会抛弃掉丢失的 UDP 包;

    • UDP 缓冲区填满:当 UDP 的缓冲区已经被填满的时候,接收方还没有处理这部分的 UDP 数据报,这个时候再过来的数据报就没有地方可以存了,自然就都被丢弃了。

      client发送两次UDP数据,第一次 500字节,第二次300字节,server端阻塞模式下接包,第一次recvfrom( 1000 ),收到是 1000,还是500,还是300,还是其他?

      由于UDP通信的有界性,接收到只能是500或300,又由于UDP的无序性和非可靠性,接收到可能是300,也可能是500,也可能一直阻塞在recvfrom调用上,直到超时返回(也就是什么也收不到)。

      第二种情况:在假定数据包是不丢失并且是按照发送顺序按序到达的情况下,server端阻塞模式下接包,先后三次调用:recvfrom( 200),recvfrom( 1000),recvfrom( 1000),接收情况如何呢?
      由于UDP通信的有界性,第一次recvfrom( 200)将接收第一个500字节的数据包,但是因为用户空间buf只有200字节,于是只会返回前面200字节,剩下300字节将丢弃。第二次recvfrom( 1000)将返回300字节,第三次recvfrom( 1000)将会阻塞。

    如何解决:

    以libevent测试程序为例,在接收到缓冲区有数据的事件后,首先通过如下的方法,或者libevent封装的方法,获取到系统缓冲区中可读数据的大小,然后申请到对应大小的buffer去调用recvfrom方法,否则会出现如上UDP可读缓冲区小余可读数据的情况,导致出现UDP数据读不全的问题!

    1

    int ret = ioctl(fd, FIONREAD, &totallen);

    或者

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    static int

    get_n_bytes_readable_on_socket(evutil_socket_t fd)

    {

    #if defined(FIONREAD) && defined(_WIN32)

            unsigned long lng = EVBUFFER_MAX_READ;

            if (ioctlsocket(fd, FIONREAD, &lng) < 0)

                    return -1;

            /* Can overflow, but mostly harmlessly. XXXX */

            return (int)lng;

    #elif defined(FIONREAD)

            int n = EVBUFFER_MAX_READ;

            if (ioctl(fd, FIONREAD, &n) < 0)

                    return -1;

            return n;

    #else

            return EVBUFFER_MAX_READ;

    #endif

    }

    服务器收到包之后全部回发给客户端:

    [root@localhost sample]# ./test_udp_server

    bind() success – [0.0.0.0] [8888]

    Read: len [2] – content [bc]

    Read: len [4] – content [abcd]

    Read: len [4] – content [abcd]

    Read: len [17] – content [12345678901111111]

    Read: len [13] – content [aaaaaaaaab123]

    客户端发送字符给服务器,并输出服务器发回来的字符串:

    [root@localhost sample]# ./test_udp_client

    set sz:2097152, et recv_buff:0, len:0

    set sz:2097152, get snd_buff:0, len:0

    bind() success – [11.12.115.239] [8888]

    input 0: exit!

    input other: send to other msg!

    abc

    input message to others : 

    sendToServer: len [2] – content [bc]

    input message to others : Read:count:2 ,but read len [2] – content [bc]

    recvfrom() no pending data.: Success

    abcd

    sendToServer: len [4] – content [abcd]

    input message to others : Read:count:4 ,but read len [4] – content [abcd]

    recvfrom() no pending data.: Success

    abcd

    sendToServer: len [4] – content [abcd]

    input message to others : Read:count:4 ,but read len [4] – content [abcd]

    recvfrom() no pending data.: Success

    12345678901111111

    sendToServer: len [17] – content [12345678901111111]

    input message to others : Read:count:17 ,but read len [10] – content [1234567890

                                                                                     ­]

    recvfrom() no pending data.: Success

    aaaaaaaaab123

    sendToServer: len [13] – content [aaaaaaaaab123]

    input message to others : Read:count:13 ,but read len [10] – content [aaaaaaaaab

                                                                                     ­]

    recvfrom() no pending data.: Success

    服务器的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    121

    122

    123

    124

    125

    126

    127

    128

    129

    130

    131

    132

    133

    134

    135

    136

    137

    138

    139

    140

    141

    142

    143

    144

    145

    146

    147

    148

    149

    150

    151

    152

    153

    154

    155

    156

    157

    158

    159

    160

    161

    162

    163

    164

    165

    166

    167

    168

    169

    170

    171

    172

    173

    174

    175

    176

    177

    178

    //event test udp socket server

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include <sys/socket.h>

    #include <netinet/in.h>

    #include <arpa/inet.h>

    #include <event.h>

    #include <event2/listener.h>

    #include <fcntl.h>

    #include <pthread.h>

    #include <signal.h>

    #include <sys/types.h>

    #include <sys/time.h>

    #include <sys/stat.h>

    #include <sys/resource.h>

    /*

    [root@localhost sample]# gcc -o test_udp_server test_udp_server.c -levent -lpthread

    */

    #define SVR_IP "0.0.0.0"

    #define SVR_PORT 8888

    #define BUF_SIZE 1024

    #define UR_CLIENT_SOCK_BUF_SIZE (65536)

    #define UR_SERVER_SOCK_BUF_SIZE (UR_CLIENT_SOCK_BUF_SIZE * 32)

    void read_cb(int fd, short event, void *arg) {

        char buf[BUF_SIZE];

        int len;

        int size = sizeof(struct sockaddr);

        struct sockaddr_in client_addr;

        memset(buf, 0, sizeof(buf));

        len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &size);

        if (len == -1) {

            perror("recvfrom()");

        else if (len == 0) {

            printf("Connection Closed\n");

        else {

            printf("Read: len [%d] – content [%s]\n", len, buf);

            /* Echo */

            sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, size);

        }

    }

    int set_sock_buf_size(int fd, int sz0)

    {

        int sz;

        sz = sz0;

        while (sz > 0) {

            if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) {

                sz = sz / 2;

            else {

                break;

            }

        }

        if (sz < 1) {

            perror("Cannot set socket rcv size"); 

        }

        sz = sz0;

        while (sz > 0) {

            if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) {

                sz = sz / 2;

            else {

                break;

            }

        }

        if (sz < 1) {

            perror("Cannot set socket snd size"); 

        }

        return 0;

    }

    int bind_socket(struct event *ev) {

        int sock_fd;

        int flag = 1;

        struct sockaddr_in sin;

        /* Create endpoint */

        if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {

            perror("socket()");

            return -1;

        }

        /* Set socket option */

        if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {

            perror("setsockopt()");

            return 1;

        }

        /* Set IP, port */

        memset(&sin, 0, sizeof(sin));

        sin.sin_family = AF_INET;

        sin.sin_addr.s_addr = inet_addr(SVR_IP);

        sin.sin_port = htons(SVR_PORT);

    #ifdef IP_RECVERR

            if (sin.sin_family != AF_INET6) {

                int on = 0;

    #ifdef TURN_IP_RECVERR

                on = 1;

    #endif

                if(setsockopt(sock_fd, IPPROTO_IP, IP_RECVERR, (void *)&on, sizeof(on))<0)

                    perror("IP_RECVERR");

            }

    #endif

    #ifdef IPV6_RECVERR

            if (sin.sin_family == AF_INET6) {

                int on = 0;

    #ifdef TURN_IP_RECVERR

                on = 1;

    #endif

                if(setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVERR, (void *)&on, sizeof(on))<0)

                    perror("IPV6_RECVERR");

            }

    #endif

        if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) == -1) {

            perror("O_NONBLOCK");

            return -1;

        }

        set_sock_buf_size(sock_fd, UR_SERVER_SOCK_BUF_SIZE);

        /* Bind */

        if (bind(sock_fd, (struct sockaddr *)&sinsizeof(struct sockaddr)) < 0) {

            perror("bind()");

            return -1;

        else {

            printf("bind() success – [%s] [%u]\n", SVR_IP, SVR_PORT);

        }

        /* Init one event and add to active events */

        event_set(ev, sock_fd, EV_READ | EV_PERSIST, &read_cb, NULL);

        if (event_add(ev, NULL) == -1) {

            printf("event_add() failed\n");

        }

        return 0;

    }

    int

    main(int argc, char **argv)

    {

        struct event ev;

        /* Init. event */

        if (event_init() == NULL) {

            printf("event_init() failed\n");

            return -1;

        }

        /* Bind socket */

        if (bind_socket(&ev) != 0) {  

            printf("bind_socket() failed\n");

            return -1;

        }

        /* Enter event loop */

        event_dispatch();

        printf("done\n");

        return 0;

    }

    客户端的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    83

    84

    85

    86

    87

    88

    89

    90

    91

    92

    93

    94

    95

    96

    97

    98

    99

    100

    101

    102

    103

    104

    105

    106

    107

    108

    109

    110

    111

    112

    113

    114

    115

    116

    117

    118

    119

    120

    121

    122

    123

    124

    125

    126

    127

    128

    129

    130

    131

    132

    133

    134

    135

    136

    137

    138

    139

    140

    141

    142

    143

    144

    145

    146

    147

    148

    149

    150

    151

    152

    153

    154

    155

    156

    157

    158

    159

    160

    161

    162

    163

    164

    165

    166

    167

    168

    169

    170

    171

    172

    173

    174

    175

    176

    177

    178

    179

    180

    181

    182

    183

    184

    185

    186

    187

    188

    189

    190

    191

    192

    193

    194

    195

    196

    197

    198

    199

    200

    201

    202

    203

    204

    205

    206

    207

    208

    209

    210

    211

    212

    213

    214

    215

    216

    217

    218

    219

    220

    221

    222

    223

    224

    225

    226

    227

    228

    229

    230

    231

    232

    233

    234

    235

    236

    237

    238

    239

    240

    241

    242

    243

    244

    245

    246

    247

    248

    249

    250

    251

    252

    253

    254

    255

    256

    257

    258

    259

    260

    261

    262

    263

    264

    265

    266

    267

    268

    269

    270

    271

    272

    //event test udp socket server

    #include <stdio.h>

    #include <stdlib.h>

    #include <string.h>

    #include <sys/socket.h>

    #include <netinet/in.h>

    #include <arpa/inet.h>

    #include <event.h>

    #include <event2/listener.h>

    #include <fcntl.h>

    #include <pthread.h> 

    #include <signal.h>

    #include <sys/ioctl.h>

    #include <sys/types.h>

    #include <sys/time.h>

    #include <sys/stat.h>

    #include <sys/resource.h>

    /*

    [root@localhost sample]# gcc -o test_udp_client test_udp_client.c -levent -lpthread

    */

    #define LOCAL_IP "0.0.0.0"

    #define LOCAL_PORT 19393

    #define SVR_IP "11.12.115.239"

    #define SVR_PORT  8888

    #define BUF_SIZE 10 //1024

    #define UR_CLIENT_SOCK_BUF_SIZE (65536)

    #define UR_SERVER_SOCK_BUF_SIZE (UR_CLIENT_SOCK_BUF_SIZE * 32)

    void read_cb(int fd, short event, void *arg) {

        char buf[BUF_SIZE];

        int len = 0;

        int totallen = 0;

        int size = sizeof(struct sockaddr);

        struct sockaddr_in client_addr;

        while(1){

            int ret = ioctl(fd, FIONREAD, &totallen);

            if(ret < 0 || !totallen) {

                perror("recvfrom() no pending data.");

                break;

            }

            memset(buf, 0, sizeof(buf));

            len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &size);

            if (len == -1) {

                perror("recvfrom() error");

                break;

            else if (len == 0) {

                printf("Connection Closed\n");

            else {

                printf("Read:count:%d ,but read len [%d] – content [%s]\n",totallen, len, buf);

                /* Echo */

                //sendto(fd, buf, len, 0, (struct sockaddr *)&client_addr, size);

            }

        }

    }

      

    int set_sock_buf_size(int fd, int sz0)

    {

        int sz;

        sz = sz0;

        while (sz > 0) {

            if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) {

                sz = sz / 2;

            else {

                break;

            }

        }

        if (sz < 1) {

            perror("Cannot set socket rcv size"); 

        }

            int val=0, len = 0;

            int ret = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&val, &len);

        printf("set sz:%d, et recv_buff:%d, len:%d\r\n", sz, val, len);

        sz = sz0;

        while (sz > 0) {

            if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const void*) (&sz), (socklen_t) sizeof(sz)) < 0) {

                sz = sz / 2;

            else {

                break;

            }

        }

             

        val=0, len = 0;

        ret = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&val, &len);

        printf("set sz:%d, get snd_buff:%d, len:%d\r\n", sz, val, len);

        if (sz < 1) {

            perror("Cannot set socket snd size"); 

        }

        return 0;

    }

    void sendToServer(int fd, char* msg, int len, struct sockaddr_in *server){

        printf("sendToServer: len [%d] – content [%s]\n", len, msg);

        sendto(fd, msg, len, 0, (struct sockaddr *)server, sizeof(struct sockaddr_in));

    }

    typedef struct AAA{

        struct sockaddr_in *server;

        int fd;

    }ServerInfo;

    //recv user input value

    static void * recv_input_thread(void *arg)  {

        if (arg == NULL){

            return NULL;

        }

        ServerInfo *serverInfo = (ServerInfo *) arg;

        char  input;

        char msg[1024];

        printf("input 0: exit!\n"); 

        printf("input other: send to other msg!\n");

        scanf("%c", &input);

        fflush(stdin);

        if (input == '0'){

            return NULL;

        }

        do{

            printf("input message to others : "); 

            memset(msg, 0x00, 1024);

            //scanf("%s", &msg); 

            gets(msg);

            msg[1023] = '\0';

            printf("\n"); 

            sendToServer(serverInfo->fd, msg, strlen(msg), serverInfo->server);

      

        }while(input != '0'); 

        printf("Done!\n"); 

        free(serverInfo);

        return NULL;

    }

    int bind_socket(struct event *ev, int sock_fd, int local_port) { 

        int flag = 1;

        struct sockaddr_in sin;

       

        /* Set IP, port */

        memset(&sin, 0, sizeof(sin));

        sin.sin_family = AF_INET;

        sin.sin_addr.s_addr = inet_addr(LOCAL_IP);

        sin.sin_port = local_port;

    #ifdef IP_RECVERR

            if (sin.sin_family != AF_INET6) {

                int on = 0;

    #ifdef TURN_IP_RECVERR

                on = 1;

    #endif

                if(setsockopt(sock_fd, IPPROTO_IP, IP_RECVERR, (void *)&on, sizeof(on))<0)

                    perror("IP_RECVERR");

            }

    #endif

    #ifdef IPV6_RECVERR

            if (sin.sin_family == AF_INET6) {

                int on = 0;

    #ifdef TURN_IP_RECVERR

                on = 1;

    #endif

                if(setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVERR, (void *)&on, sizeof(on))<0)

                    perror("IPV6_RECVERR");

            }

    #endif

        if (fcntl(sock_fd, F_SETFL, O_NONBLOCK) == -1) {

            perror("O_NONBLOCK");

            return -1;

        }

        set_sock_buf_size(sock_fd, UR_SERVER_SOCK_BUF_SIZE);

        /* Bind */

        if (bind(sock_fd, (struct sockaddr *)&sinsizeof(struct sockaddr)) < 0) {

            perror("bind()");

            return -1;

        else {

            printf("bind() success – [%s] [%u]\n", SVR_IP, SVR_PORT);

        }

        /* Init one event and add to active events */

        event_set(ev, sock_fd, EV_READ | EV_PERSIST, &read_cb, NULL);

        if (event_add(ev, NULL) == -1) {

            printf("event_add() failed\n");

        }

        return 0;

    }

    int

    main(int argc, char **argv)

    {

        struct event udp_event;

        /* Init. event */

        if (event_init() == NULL) {

            printf("event_init() failed\n");

            return -1;

        

      

        int sock_fd;

        int flag = 1;

        struct sockaddr_in server;

        /* Create endpoint */

        if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {

            perror("socket()");

            return -1;

        }

        /* Set socket option */

        if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) < 0) {

            perror("setsockopt()");

            return 1;

        }

        /* Bind socket */

        if (bind_socket(&udp_event, sock_fd, LOCAL_PORT) != 0) {  

            printf("bind_socket() failed\n");

            return -1;

        }

        /* Set IP, port */

        memset(&server, 0, sizeof(server));

        server.sin_family = AF_INET;

        server.sin_addr.s_addr = inet_addr(SVR_IP);

        server.sin_port = htons(SVR_PORT);

        ServerInfo *serverInfo = (ServerInfo *)malloc(sizeof(ServerInfo));

        serverInfo->server = &server;

        serverInfo->fd = sock_fd;

        pthread_t tidp;

        if ((pthread_create(&tidp, NULL, recv_input_thread, (void*)serverInfo)) == -1){

            printf("create error!\n");

            return 1;

        }

      

        /* Enter the event loop; does not return. */

        event_dispatch();

        close(sock_fd);

        printf("done\n");

        return 0;

    }

  • 相关阅读:
    ui设计岗位招聘要求有哪些? 优漫动游
    Mybatis深入:整合框架
    Ansible最佳实践之委派任务和事实
    nvm切换node版本
    GoFrame:如何简单地搭建一个简单地微服务
    ESIM实战文本匹配
    PS抠图后有毛边怎么处理?
    Java使用BouncyCastle进行基于ECDSA算法的椭圆曲线secp256r1证书自签名
    关于面试--【namenode&fsimage&edits】
    基于深度神经网络的中药材识别
  • 原文地址:https://blog.csdn.net/u011555996/article/details/125354459