отправка форматированных сообщений через TCP-соединение

У меня есть существующая программа C, которая печатает ряд сообщений со стандартной ошибкой, используя:

fprintf(stderr, ...

Я хотел бы модифицировать эту программу, чтобы эти сообщения также рассылались по TCP-соединению через Интернет. (Который я уже создал как сокет SOCK_STREAM.) Каков наилучший способ отформатировать сообщения, как если бы они были с помощью fprintf, а затем отправить их через Интернет?

Конечно, прежде чем я смогу отправить сообщение, мне сначала нужно узнать его длину, поэтому я могу сначала отправить длину клиенту, чтобы клиент знал, сколько байтов нужно прочитать...

Любые идеи будут высоко оценены!


person Charles    schedule 06.12.2009    source источник


Ответы (4)


После подключения сокета вы можете открыть его как поток, используя fdopen() в дескрипторе файла сокета. Это даст вам FILE *, который вы можете передать fprintf() вместо stderr.

person caf    schedule 06.12.2009
comment
Спасибо, но я думал об этом, и, насколько я могу судить, чтобы клиент знал, сколько байтов нужно прочитать для каждого сообщения, мне сначала нужно будет отправить длину сообщения заранее. Я не думаю, что это можно сделать, просто передав FILE * вместо stderr... - person Charles; 06.12.2009
comment
Что ж, если все ваши сообщения заканчиваются на \n, клиент может просто читать, пока не увидит его. - person caf; 06.12.2009
comment
... что легко сделать, используя fgets(), fscanf() и т. д. на стороне чтения, когда у вас есть FILE * с использованием fdopen(). - person mark4o; 06.12.2009

Я бы посоветовал вам обернуть выходные вызовы в функцию с переменным аргументом (так работает семейство функций printf) и делать все оттуда. Например, это может выглядеть так:

int multi_log(FILE * stream, int fd, const char * fmt, ...) {
   char buff[BUFF_MAX] = {0};
   int len = 0;
   va_list args;
   va_start (args, fmt);
   len = vsnprintf (buff, BUFF_MAX, fmt, args);
   va_end (args);

   fputs (buff, stream);
   write (fd, buff, len); 
}

Таким образом, вы можете добавлять и/или удалять функциональные возможности по мере необходимости.

При использовании размера, возвращенного из vsnprintf, применяются предостережения, внимательно прочитайте справочные страницы

person ezpz    schedule 06.12.2009

Я думаю, вам нужно посмотреть на sprintf или (лучше) вариант, который требует от вас явного указания размера выходного буфера.

person Stephen C    schedule 06.12.2009

Вы делаете это сложнее, чем это должно быть. Если вы отправляете двоичные данные, а клиенту необходимо распознать границы сообщения, тогда да, вам нужно сначала отправить длину сообщения или использовать какое-то «кадрирование» для определения границ сообщения. Но в данном случае вам это не нужно. Вы можете просто записать текст сообщения в сокет, используя подход, подобный предложенному выше.

С другой стороны, одноранговый узел может просто сидеть в цикле чтения текста, пока сокет не будет закрыт. Если вы хотите, чтобы читатель обрабатывал текст построчно, вы можете пометить конец каждой строки символом новой строки. Затем читатель может просто просмотреть полученный текст в поисках этих символов новой строки.

Единственное, что может быть сложно, это то, что, поскольку TCP ориентирован на байты, а не на сообщения, читатель не всегда будет получать текст фрагментами того же размера, что и отправитель. (Обычно вы будете, но это не гарантируется.) Таким образом, ваш читатель должен иметь возможность обрабатывать случай, когда одно чтение из сокета может получить часть строки или, возможно, несколько строк, объединенных вместе. Это не большая проблема, пока вы кодируете вероятность того, что это может произойти.

person Duncan    schedule 31.07.2015