Sockets 如何在C中使用套接字读取流的TCP数据包?
让我先告诉你我想做什么。 我正在尝试编写一个非常简单的代理服务器。 我使用socket API创建了一个套接字。Sockets 如何在C中使用套接字读取流的TCP数据包?,sockets,proxy,tcp,stream,Sockets,Proxy,Tcp,Stream,让我先告诉你我想做什么。 我正在尝试编写一个非常简单的代理服务器。 我使用socket API创建了一个套接字。 socket=socket(AF_INET,SOCK_STREAM,0)) 我的代理服务器工作得很好,直到我尝试使用它来传输数据。 因此,我所做的是我的服务器套接字侦听请求并解析它们,然后将它们转发到实际的服务器,然后使用read()调用读取数据包&我盲目地将其转发回客户端 对于所有html页面和图像,它都可以正常工作。但当我尝试转发流媒体视频时,我无法做到这一点 我的套接字总是返回
socket=socket(AF_INET,SOCK_STREAM,0))代码>
我的代理服务器工作得很好,直到我尝试使用它来传输数据。
因此,我所做的是我的服务器套接字侦听请求并解析它们,然后将它们转发到实际的服务器,然后使用read()调用读取数据包&我盲目地将其转发回客户端
对于所有html页面和图像,它都可以正常工作。但当我尝试转发流媒体视频时,我无法做到这一点
我的套接字总是返回应用层数据(HTTP数据包),但在流式视频中,只有第一个数据包是HTTP,其余的都是TCP数据包。因此,我只能转发第一个HTTP数据包。当我尝试读取包含数据的其他数据包(都是TCP)时,我在应用层没有得到任何信息(这很明显,因为这些数据包中的应用层没有任何信息)。所以我被卡住了,我不知道如何从TCP层读取这些数据包(我不想使用原始套接字)并完成我的工作
提前感谢如果您使用socket API,那么您就在HTTP下面的层上,也就是说,对您来说,一切都是“TCP”。如果连接卡在某个地方,则很可能是其他东西断了。注意:不能保证HTTP请求或应答报头甚至可以放入单个数据包中;他们通常就是这样
兼容HTTP 1.1的流媒体服务器将使用“内容编码:分块”并报告每个分块的长度,而不是整个文件的长度,代理时应记住这一点。如果您使用套接字API,则您位于HTTP下面的层,也就是说,对您来说,一切都是“仅TCP”。如果连接卡在某个地方,则很可能是其他东西断了。注意:不能保证HTTP请求或应答报头甚至可以放入单个数据包中;他们通常就是这样
兼容HTTP 1.1的流媒体服务器将使用“Content Encoding:chunked”并报告每个块的长度,而不是整个文件的长度,代理时应记住这一点
所以我做的是我的服务器套接字
倾听请求并解析
他们
为什么??HTTP代理不必解析除了请求的第一行以外的任何内容,就可以知道在哪里进行上游连接。其他一切都只是在两个方向上复制字节
所以我做的是我的服务器套接字
倾听请求并解析
他们
为什么??HTTP代理不必解析除了请求的第一行以外的任何内容,就可以知道在哪里进行上游连接。其他一切都只是在两个方向上复制字节。您必须解析数据包头才能知道从套接字读取多少数据。首先,使用一个环形缓冲区(一个循环缓冲区!),例如BSDsys/queue.h
,对从流接收的数据进行排序
下面的代码显示了如何提取第3层中IPv4数据包的头\u长度
、总长度
、源地址和目标地址。请参阅以了解偏移:
typedef struct {
unsigned char version;
unsigned char header_length;
unsigned short total_length;
struct in_addr src;
struct in_addr dst;
} Packet;
int rb_packet_write_out(RingBuffer *b, int fd, int count) {
int i;
for (i = 0; i < count; i++) {
if (b->level < 20) {
return i;
}
Packet p;
unsigned char *start = b->blob + b->read_cursor;
unsigned char b1 = start[0];
p.version = b1 >> 4;
p.header_length = b1 & 0xf;
p.total_length = bigendian_deserialize_uint16(start + 2);
if (b->level < p.total_length) {
return i;
}
memcpy(&(p.src), start + 12, 4);
memcpy(&(p.dst), start + 16, 4);
char s[5], d[5];
inet_ntop(AF_INET, &(p.src), s, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(p.dst), d, INET_ADDRSTRLEN);
L_DEBUG("Packet: v%u %s -> %s (%u)", p.version, s, d, p.total_length);
}
return i;
}
类型定义结构{
无符号字符版本;
无符号字符头长度;
无符号短总长度;
地址src中的结构;
结构地址dst;
}包;
int rb_数据包_写入_(环形缓冲区*b、int fd、int计数){
int i;
对于(i=0;i20级){
返回i;
}
包p;
无符号字符*开始=b->blob+b->读取光标;
无符号字符b1=开始[0];
p、 版本=b1>>4;
p、 标题长度=b1&0xf;
p、 总长度=bigendian反序列化uint16(开始+2);
如果(b->level%s(%u)”,p.version,s,d,p.total_长度);
}
返回i;
}
您必须解析数据包头才能知道从套接字读取多少数据。首先,使用一个环形缓冲区(一个循环缓冲区!),例如BSDsys/queue.h
,对从流接收的数据进行排序
下面的代码显示了如何提取第3层中IPv4数据包的头\u长度
、总长度
、源地址和目标地址。请参阅以了解偏移:
typedef struct {
unsigned char version;
unsigned char header_length;
unsigned short total_length;
struct in_addr src;
struct in_addr dst;
} Packet;
int rb_packet_write_out(RingBuffer *b, int fd, int count) {
int i;
for (i = 0; i < count; i++) {
if (b->level < 20) {
return i;
}
Packet p;
unsigned char *start = b->blob + b->read_cursor;
unsigned char b1 = start[0];
p.version = b1 >> 4;
p.header_length = b1 & 0xf;
p.total_length = bigendian_deserialize_uint16(start + 2);
if (b->level < p.total_length) {
return i;
}
memcpy(&(p.src), start + 12, 4);
memcpy(&(p.dst), start + 16, 4);
char s[5], d[5];
inet_ntop(AF_INET, &(p.src), s, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(p.dst), d, INET_ADDRSTRLEN);
L_DEBUG("Packet: v%u %s -> %s (%u)", p.version, s, d, p.total_length);
}
return i;
}
类型定义结构{
无符号字符版本;
无符号字符头长度;
无符号短总长度;
地址src中的结构;
结构地址dst;
}包;
int rb_数据包_写入_(环形缓冲区*b、int fd、int计数){
int i;
对于(i=0;i20级){
返回i;
}
包p;
无符号字符*开始=b->blob+b->读取光标;
无符号字符b1=开始[0];
p、 版本=b1>>4;
p、 标题长度=b1&0xf;
p、 总长度=bigendian反序列化uint16(开始+2);
如果(b->level%s(%u)”,p.version,s,d,p.total_长度);
}
返回i;
}
谢谢您的回复。我同意你的看法,但你不认为我应该能够读取流的TCP数据包中的数据,因为我能够读取html页面或流的所有其他数据包。绝对可以。我的怀疑是,为了读取所有我需要的数据,您只调用了read()函数一次