MSG_PEEK (数据窥探)

使用 MSG_PEEK 选项能够获取接收缓冲区数据的拷贝
  • recv() 专用选项,可用于数据预接收
  • 指定 MSG_PEEK 选项时,不会清空缓冲区
  • 可用于获取接收缓冲区种的数据量(字节数)

当接收缓冲区中没有数据时,MSG_PEEK 也会导致线程阻塞

下面的代码输出什么?为什么?

static char c_temp[1024 * 2] = {0};
char buf[32] = {0];

sleep(1);

r = recv(client, c_temp, sizeof(c_temp), MSG_PEEK);

c_temp[r] = 0;

printf("r = %d\n", r);
printf("c_temp = %s\n", c_temp);

r = recv(client, buf, sizeof(buf), 0);

buf[r] = 0;

printf("r = %d\n", r);
printf("buf = %s\n", buf);
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = {0};
    struct sockaddr_in addr = {0};
    int len = 0;
    char *test = "Delpin-Tang";

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    len = send(sock, test, strlen(test), 0);

    getchar();

    close(sock);

    return 0;
}
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    while (1) {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if (client == -1) {
            printf("client accept error");
            return -1;
        }

        printf("client: %d\n", client);

        do {
            r = recv(client, buf, sizeof(buf), MSG_PEEK);

            if (r > 0) {
                buf[r] = 0;
                printf("r = %d\n", r);
                printf("data: %s\n", buf);

                r = recv(client, buf, sizeof(buf), 0);
                buf[r] = 0;
                printf("r = %d\n", r);
                printf("data: %s\n", buf);
            } else {
                printf("no data in receive buf\n");  // 注意这里!(如果未打印表示阻塞)
            }

        }while (r > 0);

        close(client);
    }

    close(server);

    return 0;
}
输出:
server start success
client: 4
r = 11
data: Delpin-Tang
r = 11
data: Delpin-Tang

MSG_DONTWAIT (立即收发模式)

数据收发时不阻塞,立即返回
  • sned() : 如果无法将数据送入发送缓冲区,那么直接返回错误 (比如:发送缓冲器 1024 字节大小,欲想发送 2048 字节时)
  • recv() : 如果接收缓冲区中没有数据,那么直接返回错误
send() / recv() 返回值:
-1, 错误发生
0,  对端调用 close 关闭
n,   发送 / 接收 的数据量

下面的代码输出什么?为什么?

printf("connect success\n");
sleep(1);
test = "D.T.software";
send(sock, test, strlen(test), 0);
sleep(2);
test = "quit";
send(sock, test, strlen(test), 0);

👇

do {
    char buf[32] = {0};
    r = recv(client, buf, sizeof(buf), MSG_DONTWAIT);
    printf("r = %d\n", r);
    if (r > 0) {
        buf[r] = 0;
        printf("buf = %s\n", buf);
        if (strcmp(buf, "quit") == 0) {
            break;
        }
    }
    else {
        printf("no data receive\n");
        sleep(1);
    }
}while (1);
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    while (1) {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if (client == -1) {
            printf("client accept error");
            return -1;
        }

        printf("client: %d\n", client);

        do {
            char buf[32] = {0};
            r = recv(client, buf, sizeof(buf), MSG_DONTWAIT);
            printf("r = %d\n", r);
            if (r > 0) {
                buf[r] = 0;
                printf("buf = %s\n", buf);
                if (strcmp(buf, "quit") == 0) {
                    break;
                }
            }
            else {
                printf("no data receive\n");
                sleep(1);
            }
        }while (1);

        close(client);
    }

    close(server);

    return 0;
}
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = {0};
    struct sockaddr_in addr = {0};
    int len = 0;
    char *test;

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    sleep(1);

    test = "D.T.software";

    send(sock, test, strlen(test), 0);

    sleep(2);

    test = "quit";

    send(sock, test, strlen(test), 0);

    getchar();

    close(sock);

    return 0;
}
输出:
server start success
client: 4
r = -1
no data receive
r = -1
no data receive
r = 12
buf = D.T.software
r = -1
no data receive
r = -1
no data receive
r = 4
buf = quit

再论阻塞发送模式 (flags → 0)

send()
  • 发送数据长度 > 发送缓冲区长度 👉 返回错误
  • 发送数据长度 <= 发送缓冲区剩余长度 👉 复制数据到发送缓冲区 👉 返回发送的字节数
  • 发送缓冲区剩余长度 < 发送数据长度 <= 发送缓冲区长度 👉 等待发送缓冲器清空 👉 复制数据到发送缓冲区 👉 返回发送的字节数
resv()
  • 接收缓冲区中没有数据时 👉 等待数据
  • 接收缓冲区数据量 <= 接收区长度 👉 数据全部拷贝到接收区
  • 接收缓冲区数据量 > 接收区长度 👉 拷贝部分数据到接收区 👉 返回接收的字节数

通讯框架的迭代增强

int TcpClient_Available(TcpClient *client)
{
    static char c_temp[1024 * 2] = {0};
    int ret = -1;
    Client *c = (Client*)client;
    
    if (c) {
        ret = recv(c->fd, c_temp, sizeof(c_temp), MSG_PEEK | MSG_DONTWAIT);
    }
    
    return -1;
}
tcp_client.h
#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H

#include "message.h"

typedef void TcpClient;

TcpClient *TcpClient_New();
TcpClient *TcpClient_From(int fd);

int TcpClient_SendMsg(TcpClient *client, Message *msg);
int TcpClient_SendRaw(TcpClient *client, char *buf, int length);
Message *TcpClient_RecvMsg(TcpClient *client);
int TcpClient_RecvRaw(TcpClient *client, char *buf, int length);
int TcpClient_Available(TcpClient *client);

int TcpClient_Connect(TcpClient *client, char *ip, int port);
int TcpClient_IsValid(TcpClient *client);
void TcpClient_Close(TcpClient *client);
void TcpClient_Del(TcpClient *client);

void TcpClient_SetData(TcpClient *client, void *data);
void *TcpClient_GetDate(TcpClient *client);

#endif
tcp_client.c
#include "tcp_client.h"

#include "msg_parser.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h> 
#include <arpa/inet.h>
#include <unistd.h>
#include <malloc.h>

typedef struct tcp_client {
    int fd;
    MParser *parser;
    void *data;
}Client;

TcpClient *TcpClient_New()
{
    return TcpClient_From(-1);
}

TcpClient *TcpClient_From(int fd)
{
    Client *ret = malloc(sizeof(Client));

    if (ret) {
        ret->fd     = fd;
        ret->parser = MParser_New();
        ret->data   = NULL;
    }

    return (ret && ret->parser) ? ret : (free(ret), NULL);
}

int TcpClient_SendMsg(TcpClient *client, Message *msg)
{
    int ret = 0;
    Client *c = (Client*)client;

    if (c && msg) {
        int len = Message_Size(msg);
        char *data = (char*)Message_H2N(msg);

        ret = (send(c->fd, data, len, 0) != -1);

        Message_N2H(msg);
    }

    return ret;
}

int TcpClient_SendRaw(TcpClient *client, char *buf, int length)
{
    int ret = 0;
    Client *c = (Client*)client;

    if (c && buf)  {
        ret = send(c->fd, buf, length, 0);
    }

    return ret;
}

Message *TcpClient_RecvMsg(TcpClient *client)
{
    Message *ret = NULL;
    Client *c = (Client*)client;

    if (c) {
        ret = MParser_ReadFd(c->parser, c->fd);
    }

    return ret;
}

int TcpClient_RecvRaw(TcpClient *client, char *buf, int length)
{
    int ret = 0;
    Client *c = (Client*)client;

    if (c && buf) {
        ret = recv(c->fd, buf, length, 0);
    }

    return ret;
}

int TcpClient_Connect(TcpClient *client, char *ip, int port)
{
    int ret = TcpClient_IsValid(client);
    Client *c = (Client*)client;

    if (!ret && ip && c && ((c->fd = socket(PF_INET, SOCK_STREAM, 0)) != -1)) {
        struct sockaddr_in addr = {0};

        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_port = htons(port);

        ret = (connect(c->fd, (struct sockaddr*)&addr, sizeof(addr)) != -1);
    }

    return ret;
}

int TcpClient_IsValid(TcpClient *client)
{       
    int ret = 0;
    Client *c = (Client*)client;

    if (c) {
        struct tcp_info info = {0};
        int l = sizeof(info);

        getsockopt(c->fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t*)&l);

        ret = (info.tcpi_state == TCP_ESTABLISHED);
    }

    return ret;
}

void TcpClient_Close(TcpClient *client)
{
    Client *c = (Client*)client;

    if (c) {
        close(c->fd);

        c->fd = -1;

        MParser_Reset(c->parser);
    }
}

void TcpClient_Del(TcpClient *client)
{
    Client *c = (Client*)client;

    if (c) {
        TcpClient_Close(c);
        MParser_Del(c->parser);
        free(c);
    }   
}

void TcpClient_SetData(TcpClient *client, void *data)
{
    Client *c = (Client*)client;

    if (c) {
        c->data = data;
    }
}

void *TcpClient_GetDate(TcpClient *client)
{
    void *ret = NULL;
    Client *c = (Client*)client;

    if (c) {
        ret = c->data;
    }

    return ret;
}

int TcpClient_Available(TcpClient *client)
{
    static char c_temp[1024 * 2] = {0};
    int ret = -1;
    Client *c = (Client*)client;
    
    if (c) {
        ret = recv(c->fd, c_temp, sizeof(c_temp), MSG_PEEK | MSG_DONTWAIT);
    }
    
    return ret;
}

MSG_WAITALL(等待数据)

  • 接收专用,等待需要的数据完全满足时,recv() 才返回

MSG_MORE(更多数据)

  • 发送专用,指示内核不着急将发送缓冲区中的数据进行传输

下面的代码输出什么?为什么?

client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int sock = {0};
    struct sockaddr_in addr = {0};
    int len = 0;
    char *test;
    int i = 0;

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if (sock == -1) {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    /***************************************/
    test = "Delphi-Tang";
    for (i=0; i<strlen(test); ++i) {
        send(sock, test+i, 1, 0);
        if (i % 2) {
            sleep(1);
        }
    }

    test = "quit";
    for (i=0; i<(strlen(test)-1); ++i) {
        send(sock, test + i, 1, MSG_MORE);
        sleep(1);
    }

    send(sock, test + i, 1, 0);
    /***************************************/
    
    getchar();

    close(sock);

    return 0;
}
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main()
{
    int server = 0;
    struct sockaddr_in saddr = {0};
    int client = 0;
    struct sockaddr_in caddr = {0};
    socklen_t asize = 0;
    char buf[32] = {0};
    int r = 0;
    

    server = socket(PF_INET, SOCK_STREAM, 0);

    if (server == -1) {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if (bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
        printf("server bind error\n");
        return -1;
    }

    if (listen(server, 1) == -1) {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    while (1) {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if (client == -1) {
            printf("client accept error");
            return -1;
        }

        printf("client: %d\n", client);

        do {
            /***************************************/
            int len[2] = {11, 4};
            int i = 0;

            for (i=0; i<2; ++i) {
                r = recv(client, buf, len[i], MSG_WAITALL);
                printf("r = %d\n", r);
                if (r > 0) {
                    buf[r] = 0;
                    printf("data = %s\n", buf);

                    if (strcmp(buf, "quit") == 0) {
                        break;
                    }
                }
            }
            /***************************************/
        }while (0);

        close(client);
    }

    close(server);

    return 0;
}
输出:
client: 4
r = 11
data = Delphi-Tang
r = 4
data = quit

思考:如何使用 UDP 进行数据收发?

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧