Unix Socket sending/receiving 长消息
Unix Socket sending/receiving long messages
我正在使用tcp 编写一个简单的应用层协议,但遇到了问题。我想在消息发送中进行碎片化,因为消息太长了。但是我无法同步进程,客户端在服务器写入数据之前读取空缓冲区。消息大约为 4mb。我该如何编写这些方法?
对于客户
void send_message(string message);
string receive_message()
对于服务器
void send_message(int sock,string message)
string receive_message(int sock)
我的功能如下
void send_fragment(char* buffer,int length){
int n = write(sockfd, buffer, length);
if (n < 0)
{
perror("ERROR writing to socket");
exit(1);
}
}
string receive_fragment(){
char buffer[FRAGMENT_LENGTH];
bzero(buffer,FRAGMENT_LENGTH);
int n = read(sockfd, buffer, FRAGMENT_LENGTH-1);
if (n < 0)
{
perror("ERROR reading from socket");
exit(1);
}
return string(buffer);
}
void send_message(string message){
char buffer[FRAGMENT_LENGTH];
bzero(buffer,FRAGMENT_LENGTH);
int message_length = message.length();
//computes the number of fragment
int number_of_fragment = ceil((double)message_length / FRAGMENT_LENGTH);
sprintf(buffer,"%d",number_of_fragment);
//sends the number of fragment
send_fragment(buffer,strlen(buffer));
for(int i=0;i<number_of_fragment;++i){
bzero(buffer,FRAGMENT_LENGTH);
//fragment interval
int start = i*FRAGMENT_LENGTH;
int end = (i+1)*FRAGMENT_LENGTH;
if(i==number_of_fragment-1){
end = min(end,message_length);
}
//creates a fragment
const char* fragment = message.substr(start,end).c_str();
sprintf(buffer,"%s",fragment);
//sends the fragment
send_fragment(buffer,strlen(buffer));
}
}
string receive_message(){
//receive and computes the number of fragment
string number_of_fragment_string = receive_fragment();
int number_of_fragment = atoi(number_of_fragment_string.c_str());
string message ="";
for(int i=0;i<number_of_fragment;++i){
//concatenating fragments
message += receive_fragment();
}
return message;
}
1)使用send()
和recv()
创建send_message()
和receive_message()
。
2)Select recv()
中的适当标志阅读 recv()
标志手册页。 http://linux.die.net/man/2/recv.
3)在每次发送的报文的首尾使用一些定界符来标记首尾,以便接收端进行校验。
您必须在自己的代码中实现框架。 TCP 是一个 "stream" 意味着它只发送字节而没有任何类型的 start/end 指示。 (UDP 是基于数据包的,但不适合您这种大小的数据包。)
最简单的方法是将 4 字节长度写入套接字,让接收端读取这些字节,记住 endianess 是一个问题(使用 htonl()
和 ntohl()
将本地表示转换为 "network order").
然后继续读取该字节数。完成后,您就收到了消息。
如果您使用阻塞读取,这将相当简单——如果您读取的较少,则连接已断开。如果你使用非阻塞读取,你必须 assemble 你得到的片段(你甚至可以得到片段的长度,虽然不太可能)在每次读取调用时返回。
还有其他构建数据的方法,但这是最简单的方法。
您忽略了 recv()
返回的计数。不要用整个缓冲区构造一个字符串,而是只从缓冲区的那么多字节构造它。
我正在使用tcp 编写一个简单的应用层协议,但遇到了问题。我想在消息发送中进行碎片化,因为消息太长了。但是我无法同步进程,客户端在服务器写入数据之前读取空缓冲区。消息大约为 4mb。我该如何编写这些方法?
对于客户
void send_message(string message);
string receive_message()
对于服务器
void send_message(int sock,string message)
string receive_message(int sock)
我的功能如下
void send_fragment(char* buffer,int length){
int n = write(sockfd, buffer, length);
if (n < 0)
{
perror("ERROR writing to socket");
exit(1);
}
}
string receive_fragment(){
char buffer[FRAGMENT_LENGTH];
bzero(buffer,FRAGMENT_LENGTH);
int n = read(sockfd, buffer, FRAGMENT_LENGTH-1);
if (n < 0)
{
perror("ERROR reading from socket");
exit(1);
}
return string(buffer);
}
void send_message(string message){
char buffer[FRAGMENT_LENGTH];
bzero(buffer,FRAGMENT_LENGTH);
int message_length = message.length();
//computes the number of fragment
int number_of_fragment = ceil((double)message_length / FRAGMENT_LENGTH);
sprintf(buffer,"%d",number_of_fragment);
//sends the number of fragment
send_fragment(buffer,strlen(buffer));
for(int i=0;i<number_of_fragment;++i){
bzero(buffer,FRAGMENT_LENGTH);
//fragment interval
int start = i*FRAGMENT_LENGTH;
int end = (i+1)*FRAGMENT_LENGTH;
if(i==number_of_fragment-1){
end = min(end,message_length);
}
//creates a fragment
const char* fragment = message.substr(start,end).c_str();
sprintf(buffer,"%s",fragment);
//sends the fragment
send_fragment(buffer,strlen(buffer));
}
}
string receive_message(){
//receive and computes the number of fragment
string number_of_fragment_string = receive_fragment();
int number_of_fragment = atoi(number_of_fragment_string.c_str());
string message ="";
for(int i=0;i<number_of_fragment;++i){
//concatenating fragments
message += receive_fragment();
}
return message;
}
1)使用send()
和recv()
创建send_message()
和receive_message()
。
2)Select recv()
中的适当标志阅读 recv()
标志手册页。 http://linux.die.net/man/2/recv.
3)在每次发送的报文的首尾使用一些定界符来标记首尾,以便接收端进行校验。
您必须在自己的代码中实现框架。 TCP 是一个 "stream" 意味着它只发送字节而没有任何类型的 start/end 指示。 (UDP 是基于数据包的,但不适合您这种大小的数据包。)
最简单的方法是将 4 字节长度写入套接字,让接收端读取这些字节,记住 endianess 是一个问题(使用 htonl()
和 ntohl()
将本地表示转换为 "network order").
然后继续读取该字节数。完成后,您就收到了消息。
如果您使用阻塞读取,这将相当简单——如果您读取的较少,则连接已断开。如果你使用非阻塞读取,你必须 assemble 你得到的片段(你甚至可以得到片段的长度,虽然不太可能)在每次读取调用时返回。
还有其他构建数据的方法,但这是最简单的方法。
您忽略了 recv()
返回的计数。不要用整个缓冲区构造一个字符串,而是只从缓冲区的那么多字节构造它。