Unix Socket отправка / получение длинных сообщений
Я пишу простой протокол прикладного уровня, используя tcp, и я столкнулся с проблемой. Я хочу сделать фрагментацию при отправке сообщений, потому что сообщения очень длинные. Но я не могу синхронизировать процесс, и клиент читает пустой буфер, прежде чем сервер записывает данные. Сообщения примерно 4 МБ. Как я могу написать эти методы?
Для клиента
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;
}
3 ответа
Вы должны реализовать кадрирование в своем собственном коде. TCP - это "поток", то есть он просто отправляет байты без указания начала / конца. (UDP основан на пакетах, но не подходит для пакетов вашего размера.)
Простейшим способом было бы записать 4-байтовую длину в сокет и получить принимающую сторону для чтения этих байтов, помня, что проблема с порядком байтов является проблемой (используйте htonl()
а также ntohl()
преобразовать локальные представления в "сетевой порядок").
Затем перейдите к чтению этого количества байтов. Когда это будет сделано, вы получите свое сообщение.
Если вы используете блокировку чтения, это будет довольно просто - если вы получите меньше, то соединение разорвано. Если вы используете неблокирующее чтение, вы должны собирать части, которые вы получаете (вы могли бы даже получить длину в частях, хотя вряд ли), обратно с каждым вызовом чтения.
Есть и другие способы создания ваших данных, но это самый простой способ.
Вы игнорируете счет, возвращенный recv()
, Вместо того чтобы создавать строку со всем буфером, создавайте ее только из такого количества байтов буфера.
1) Создать send_message()
а также receive_message()
с помощью send()
а также recv()
,
2) Выберите соответствующие флаги в recv()
Читать recv()
справочная страница для флагов. http://linux.die.net/man/2/recv.
3) Используйте некоторый разделитель в начале и конце сообщения, передаваемого каждый раз, чтобы отметить начало и конец, чтобы можно было проверить на стороне получателя.