13、数据收发的扩展用法(上)
① 问题
write()和send()都可以发送数据,有什么区别?
read()和recv()都可以接收数据,有什么区别?
② 数据收发选项
调用时flags参数的值为0,表示什么?默认收发网络数据
- send如果发送缓冲区中有数据,等到被清空,再送到发送缓冲区,send有可能等待。
- recv等到接收缓冲区有数据,把数据拷贝出来才返回,recv也等待。
- write ==> send (….,0)
- read ==> recv (….,0)
③ flags选项信息(常用)
注意:不同的操作系统对上述可选项的支持不同,实际工程开发时,需要事先对目标系统中支持的可选项进行调研。
④ MSG_OOB(带外数据,紧急数据)
- 原生定义
- 使用与普通数据不同的通道独立传输的数据
- 带外数据优先级比普通数据高(优先传输,对端优先接收)
- TCP中的带外数据
- 由于原生设计的限制,TCP无法提供真正意义上带外数据(TCP为流式传输,数据连绵不绝、按序传输)
- TCP中仅能通过传输协议消息头中的标记,传输紧急数据,且长度仅1字节
⑤ TCP带外数据实现原理
- TCP==>包形式传输==>报文
- URG指针(数据头中)指向紧急消息的下一个位置,即:URG指针
- 指向位置的前一个字节存储了紧急消息(后发送,先接受)
- 接收到先接收到0x03,放到一个特殊的缓冲区(1字节)
⑥ TCP带外数据处理策略
- 由于TCP设计为流式数据,因此,无法做到真正的带外数据
- 被标记的紧急数据可被提前接收,进入特殊缓冲区(仅1字节)
- 每个TCP包最多只有一个紧急数据(仅1字节)
- 特殊缓冲区仅存放最近的紧急数据(不及时接收将丢失)
- 用下面的方式收发数据会发生什么?
- 发送普通数据,普通方式接收 send (….,0) recv (….,0) 发什么收什么
- 发送普通数据,紧急方式接收 错误返回,紧急方式到特殊缓冲区接收,但是发送是普通数据,所以不会有数据接收
- 发送紧急数据,普通方式接收 紧急方式把数据发送到到特殊缓冲区接收,但是一直在普通缓冲区里收是收不到的,所以recv函数一直在等待….
- 发送紧急数据,紧急方式接收 正常正确收到数据
⑦ 编程实验:TCP紧急数据的发送与接收
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 = NULL;
int r = 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";
len = send(sock, test, strlen(test), MSG_OOB);
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 bind 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\n");
return -1;
}
printf("client: %d\n", client);
do
{
r = recv(client, buf, sizeof(buf), MSG_OOB);
if( r > 0 )
{
buf[r] = 0;
printf("OOB: %s\n", buf);
}
} while ( r > 0 );
close(client);
}
close(server);
return 0;
}
select-server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int server_handler(int server)
{
struct sockaddr_in addr = {0};
socklen_t asize = sizeof(addr);
return accept(server, (struct sockaddr*)&addr, &asize);
}
int client_handler(int client)
{
char buf[32] = {0};
int ret = recv(client, buf, sizeof(buf)-1, 0);
if( ret > 0 )
{
buf[ret] = 0;
printf("Receive: %s\n", buf);
}
return ret;
}
int main()
{
int server = 0;
struct sockaddr_in saddr = {0};
int max = 0;
int num = 0;
fd_set reads = {0};
fd_set temps = {0};
fd_set except = {0};
struct timeval timeout = {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");
FD_ZERO(&reads);
FD_SET(server, &reads);
max = server;
while( 1 )
{
temps = reads;
except = reads;
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
num = select(max+1, &temps, 0, &except, &timeout);
if( num > 0 )
{
int i = 0;
for(i=0; i<=max; i++)
{
if( FD_ISSET(i, &except) )
{
if( i != server )
{
char buf[2]= {0};
int r = recv(i, buf, sizeof(buf), MSG_OOB);
if( r > 0 )
{
buf[r] = 0;
printf("OOB: %s\n", buf);
}
}
}
if( FD_ISSET(i, &temps) )
{
if( i == server )
{
int client = server_handler(server);
if( client > -1 )
{
FD_SET(client, &reads);
max = (client > max) ? client : max;
printf("accept client: %d\n", client);
}
}
else
{
int r = client_handler(i);
if( r == -1 )
{
FD_CLR(i, &reads);
close(i);
}
}
}
}
}
}
close(server);
return 0;
}
⑧ 小问题:实际开发中,如何高效的接收TCP紧急数据?
⑨ 使用select接收紧急数据
- socket 上收到普通数据和紧急数据时都会使得select立即返回
- 普通数据:socket处于数据可读状态(可读取普通数据)
- 紧急数据:socket处理异常状态(可读取紧急数据)
⑩ 紧急数据接收示例
⑪ 编程实验:使用select接收紧急数据
⑫ 小结论
- read() / write()可用于收发普通数据(不具备扩展功能)
- send() / recv()可通过选项信息扩展更多功能
- TCP紧急数据可标识256种紧急事件(异常事件)
- 通过select能够及时处理紧急数据,并区分普通数据
14、数据收发的扩展用法(下)
① MSG_PEEK(数据窥探)
- 使用MSG_PEEK选项能够获取接收缓冲区数据的拷贝
- recv()专用选项,可用于数据预接收
- 指定MSG_PEEK选项时,不会清空缓冲区
- 可用于获取接收缓冲区中的数据量(字节数)
② 下面的代码输出什么?为什么?
两次打印的数据相同
③ MSG_DONTWAIT(立即收发模式)
- 数据收发时不阻塞,立即返回
- send() 如果无法将数据送入发送缓冲区,那么直接错误返回
- recv() 如果接收缓冲区中没有数据,那么直接错误返回
- send() / recv()返回值
- -1==>错误发生
- 0==>对端调用close()关闭连接
- n==>发送/接收的数据量
④ 下面的代码输出什么?为什么?
空闲时会打印-1和 no data received!
⑤ 再论阻塞发送模式(flags --> 0)
- send()
- 发送数据长度>发送缓冲区长度→返回错误
- 发送数据长度<=发送缓冲区剩余长度→复制数据到发送缓冲区
- 发送缓冲区剩余长度<发送数据长度<=发送缓冲区长度→等待发送缓冲区清空
⑥ 再论阻塞接收模式(flags --> 0)
- recv()
- 接收缓冲区中没有数据时→等待数据
- 接收缓冲区数据量<=接收区长度→数据全部拷贝到接收区
- 接收缓冲区数据量>接收区长度→拷贝部分数据到接收区
⑦ 通信框架的迭代增强
- 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 = NULL;
int r = 0;
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");
sleep(1);
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;
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 bind 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\n");
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);
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;
}
tcp_client.c
#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>
#include "tcp_client.h"
#include "msg_parser.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_GetData(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;
}
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_Connect(TcpClient* client, char* ip, int port);
int TcpClient_IsValid(TcpClient* client);
void TcpClient_Close(TcpClient* client);
void TcpClient_Del(TcpClient* client);
int TcpClient_Available(TcpClient* client);
void TcpClient_SetData(TcpClient* client, void* data);
void* TcpClient_GetData(TcpClient* client);
#endif
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)