《Linux网络开发必学教程》14_数据收发的扩展用法 (下)

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 #include #include #include #include #include #include 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 #include #include #include #include #include #include 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 #include #include #include #include #include #include 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 #include #include #include #include #include #include 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 #include #include #include #include #include #include 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= https://www.it610.com/article/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 = https://www.it610.com/article/(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 = https://www.it610.com/article/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(更多数据)
  • 发送专用,指示内核不着急将发送缓冲区中的数据进行传输
下面的代码输出什么?为什么?
【《Linux网络开发必学教程》14_数据收发的扩展用法 (下)】client.c
#include #include #include #include #include #include #include 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

server.c
#include #include #include #include #include #include #include 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 = https://www.it610.com/article/%s/n", buf); if (strcmp(buf, "quit") == 0) { break; } } } /***************************************/ }while (0); close(client); }close(server); return 0; }

输出:
client: 4 r = 11 data = https://www.it610.com/article/Delphi-Tang r = 4 data = quit

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

    推荐阅读