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 successclient: 4r = 11data: Delpin-Tangr = 11data: 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 successclient: 4r = -1no data receiver = -1no data receiver = 12buf = D.T.softwarer = -1no data receiver = -1no data receiver = 4buf = 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: 4r = 11data = Delphi-Tangr = 4data = quit

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