LINUX.ORG.RU

Подскажите по обмену данными между сокетами

 ,


0

2

Есть сервер (cgate), написанный на java. К нему можно цепляться по TCP, и в telnet-стиле обмениваться текстовыми командами.

В попытках спарить этот сервер с node.js, заметил, что сообщения, приходящие с сервера, приходят всегда целыми (в спецификации последняя строка сообщения имеет вид навроде «200 OK\r\n»)

Я плохо представляю как сокеты работают на низком уровне. И возник такой вопрос:

как клиентский сокет узнаёт, что прием данных завершен, что пора перестать читать из сокета и отдать принятое сообщение мне в юзерспэйс?

Иными словами, если сервер отправил мне данные одним куском, могу ли я быть уверен, что мне придет этот кусок в целости, а не последовательностью из нескольких кусков? Ну или несколько сообщений придут одним куском

★★★★★

Иными словами, если сервер отправил мне данные одним куском, могу ли я быть уверен, что мне придет этот кусок в целости, а не последовательностью из нескольких кусков?

Нет. Для TCP и для любых STREAM сокетов деления данных на куски нету, воспринимай их как поток байтов. Они могут приходить любыми порциями, сохраняется только их порядок отправки.

Определить конец сообщения можно заранее зная его длину, либо по специальному символу или последовательности символов, обозначающих конец сообщения

Harald ★★★★★
()

как клиентский сокет узнаёт, что прием данных завершен, что пора перестать читать из сокета и отдать принятое сообщение мне в юзерспэйс?

Умные клиентские сокеты бывают только у хороших разработчиков.

baverman ★★★
()

Иными словами, если сервер отправил мне данные одним куском, могу ли я быть уверен, что мне придет этот кусок в целости, а не последовательностью из нескольких кусков?

Нет, не можешь.

Абстрагированно, сокет - это поток данных. Ты сам должен проверять, сколько у тебя доступно данных для чтения и сам читать, и сам склеивать из этих кусочков большую структуру.

Единственно, что тебе гарантирует TCP/IP - что данные придут и что они придут последовательно.

schizoid ★★★
()

В этих данных должна быть информация о длине. Если это HTTP, то клиент узнает о том, что весь message-head (который starting-line+headers) считан, встретив первую пустую строку(\r\n\r\n). Если у сообщения есть еще и message-body, то распарсив хедеры, он найдет Content-Length и по его значению определит размер дальнейших данных, относящихся к данному сообщению.

kravich ★★★★
()
Последнее исправление: kravich (всего исправлений: 1)

Все зависит от того, какие сокеты. Для datagram-сокетов (для протокола UDP) данные приходят ровно теми кусками, как их отправил peer. При этом, размер сообщения должен быть меньше MTU (Maximum Transmission Unit) - иначе сообщение просто не отправить. Для stream-сокетов (для протокола TCP) данные могут приходить произвольными кусками; TCP сам решает, когда и как бить поток данных на TCP-сегменты.

dmitry_vk ★★★
()

Спасибо всем ответившим. Вроде разобрался.

makoven ★★★★★
() автор топика
Ответ на: комментарий от Harald

Кстати, а write(socket, u_char_arr, sizeof(u_char_arr)); не затыкается случайно на нулевом символе? Вот уже полчаса бьюсь методом тыка, а в сокет картинку отослать не могу...

minakov ★★★★★
()
Ответ на: комментарий от sjinks
	unsigned char test_png[] = {
0X89, 0X50, 0X4E, 0X47, 0X0D, 0X0A, 0X1A, 0X0A, 0X00, 0X00, 0X00, 0X0D,
0X49, 0X48, 0X44, 0X52, 0X00, 0X00, 0X00, 0X05, 0X00, 0X00, 0X00, 0X05,
0X08, 0X02, 0X00, 0X00, 0X00, 0X02, 0X0D, 0XB1, 0XB2, 0X00, 0X00, 0X00,
0X09, 0X70, 0X48, 0X59, 0X73, 0X00, 0X00, 0X0B, 0X13, 0X00, 0X00, 0X0B,
0X13, 0X01, 0X00, 0X9A, 0X9C, 0X18, 0X00, 0X00, 0X00, 0X07, 0X74, 0X49,
0X4D, 0X45, 0X07, 0XDD, 0X06, 0X10, 0X10, 0X04, 0X38, 0XED, 0X65, 0XD9,
0X84, 0X00, 0X00, 0X00, 0X1D, 0X69, 0X54, 0X58, 0X74, 0X43, 0X6F, 0X6D,
0X6D, 0X65, 0X6E, 0X74, 0X00, 0X00, 0X00, 0X00, 0X00, 0X43, 0X72, 0X65,
0X61, 0X74, 0X65, 0X64, 0X20, 0X77, 0X69, 0X74, 0X68, 0X20, 0X47, 0X49,
0X4D, 0X50, 0X64, 0X2E, 0X65, 0X07, 0X00, 0X00, 0X00, 0X33, 0X49, 0X44,
0X41, 0X54, 0X08, 0XD7, 0X25, 0XC7, 0X21, 0X12, 0X80, 0X30, 0X10, 0XC0,
0XC0, 0XA4, 0X83, 0X3C, 0X3C, 0XFD, 0XFF, 0X13, 0XC1, 0X07, 0XD1, 0X75,
0X0B, 0X50, 0X4D, 0X54, 0X3C, 0XAD, 0X09, 0XF5, 0X25, 0X14, 0X58, 0XDF,
0XAE, 0X42, 0X29, 0XE0, 0X02, 0X4E, 0XD4, 0XE9, 0XBC, 0X6E, 0XAC, 0XD4,
0X1F, 0X7F, 0X5A, 0X1A, 0X4E, 0XF5, 0X90, 0XE0, 0X7B, 0X00, 0X00, 0X00,
0X00, 0X49, 0X45, 0X4E, 0X44, 0XAE, 0X42, 0X60, 0X82};

write(connection, &test_png, sizeof(test_png));

//strlen пишет 8, sizeof - 189, как положено

// и даже тупняк вида
for(temp=0;temp<=sizeof(test_png)-1;temp++){
    write(connection, &test_png[temp], sizeof(char));
}

// вообще не шлет туда. Возможно это и недосып, но я реально не вижу пути
minakov ★★★★★
()
Ответ на: комментарий от Harald

в онтопике можно и write

Это, да. Но так глупо привязываться к платформе? Фи, как некультурно.

К тому же мы не знаем на чем точно сидит этот школьник.

baverman ★★★
()
Ответ на: комментарий от minakov

а пишет 8 байт примерно

write(connection, &test_png, sizeof(test_png));

&test_png будет unsigned char** — те самые 8 байт на amd64.

Должно быть примерно так:

write(connection, test_png, sizeof(test_png));

И да, неплохо было бы проверять значение, возвращаемой write (количество записанных байт).

sjinks ★★★
()
Ответ на: комментарий от sjinks

Не выходит. Правильный хедер шлю, все окей, но из массива с нулевыми символами ни конкатенировать не выходит, ни поочередногоwrite. Все проверяю, количество байт возвращаемых write верно, curl хедер кажет и молчит на теле. Вручную мобрал хедер и тело в буфер, отправляю его. На выходе только хедер. Остается только спать как Менделеев. Амперсанд засунул нечаянно-проверял. Но все равно спасибо

minakov ★★★★★
()
Ответ на: комментарий от minakov

curl хедер кажет и молчит на теле

Так, уже что-то. Например, отсутствует или неправильный Content-Length. А есть минимальный пример кода, вопроизводящий ошибку, который можно потестить локально?

sjinks ★★★
()
Ответ на: комментарий от minakov

Вручную мобрал хедер и тело в буфер, отправляю его.

\r\n\r\n?

baverman ★★★
()
Ответ на: комментарий от sjinks

Вот рабочая копия: вчера она не работала.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

#define PORT 65000

int main(int argc, char **argv){
int listenfd = 0, connection = 0, temp;
struct sockaddr_in serv_addr; 

char header[] = "HTTP/1.1 200 OK\r\n"
"Server: MyOwnServer/0.1 (GNU/Linux x86_64) (Arch Linux Bla-Bla)\r\n"
"Content-Type: image/png\r\n"
"Content-Length: 189\r\n\r\n";

unsigned char test_png[] = {
0X89, 0X50, 0X4E, 0X47, 0X0D, 0X0A, 0X1A, 0X0A, 0X00, 0X00, 0X00, 0X0D,
0X49, 0X48, 0X44, 0X52, 0X00, 0X00, 0X00, 0X05, 0X00, 0X00, 0X00, 0X05,
0X08, 0X02, 0X00, 0X00, 0X00, 0X02, 0X0D, 0XB1, 0XB2, 0X00, 0X00, 0X00,
0X09, 0X70, 0X48, 0X59, 0X73, 0X00, 0X00, 0X0B, 0X13, 0X00, 0X00, 0X0B,
0X13, 0X01, 0X00, 0X9A, 0X9C, 0X18, 0X00, 0X00, 0X00, 0X07, 0X74, 0X49,
0X4D, 0X45, 0X07, 0XDD, 0X06, 0X10, 0X10, 0X04, 0X38, 0XED, 0X65, 0XD9,
0X84, 0X00, 0X00, 0X00, 0X1D, 0X69, 0X54, 0X58, 0X74, 0X43, 0X6F, 0X6D,
0X6D, 0X65, 0X6E, 0X74, 0X00, 0X00, 0X00, 0X00, 0X00, 0X43, 0X72, 0X65,
0X61, 0X74, 0X65, 0X64, 0X20, 0X77, 0X69, 0X74, 0X68, 0X20, 0X47, 0X49,
0X4D, 0X50, 0X64, 0X2E, 0X65, 0X07, 0X00, 0X00, 0X00, 0X33, 0X49, 0X44,
0X41, 0X54, 0X08, 0XD7, 0X25, 0XC7, 0X21, 0X12, 0X80, 0X30, 0X10, 0XC0,
0XC0, 0XA4, 0X83, 0X3C, 0X3C, 0XFD, 0XFF, 0X13, 0XC1, 0X07, 0XD1, 0X75,
0X0B, 0X50, 0X4D, 0X54, 0X3C, 0XAD, 0X09, 0XF5, 0X25, 0X14, 0X58, 0XDF,
0XAE, 0X42, 0X29, 0XE0, 0X02, 0X4E, 0XD4, 0XE9, 0XBC, 0X6E, 0XAC, 0XD4,
0X1F, 0X7F, 0X5A, 0X1A, 0X4E, 0XF5, 0X90, 0XE0, 0X7B, 0X00, 0X00, 0X00,
0X00, 0X49, 0X45, 0X4E, 0X44, 0XAE, 0X42, 0X60, 0X82};

listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));

serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT); 

if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) <0){
	perror("unable to bind!");
}
if(listen(listenfd, 10)<0){
	perror("unable to listen");
}
while(1){
	connection = accept(listenfd, (struct sockaddr*)NULL, NULL); 
	temp = write(connection, header, strlen(header));
	fprintf(stderr, "%d bytes sent (header data)\n", temp);
	temp = write(connection, test_png, sizeof(test_png));
	fprintf(stderr, "%d bytes sent (message, page body)\n", temp);
	close(connection);
}
}
Но там еще потоки были и всякая всячина. Спасибо, кто не остался равнодушен и пытался помочь

minakov ★★★★★
()
Ответ на: комментарий от minakov

У меня этот код работает корректно, curl получает и хедеры, и тело. Возможно, для отдачи буфера нужно использовать что-то вроде такой функции (опустил проверку значения, возвращаемого send):

void send_all(int sock,char* buff,size_t buff_size)
{
  size_t total = 0;
  while(total!=buff_size)
  {
    int bytes_sent = send(sock,buff+total,buff_size-total,0);
    total += bytes_sent;
  }
}

kravich ★★★★
()
Последнее исправление: kravich (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.