2009-11-14 4 views
2

У меня есть клиентская и серверная программа, где я хочу отправить целую структуру из клиента, а затем вывести идентификатор элемента структуры на сервере.Отправка структуры через TCP (C-программирование)

Я сделал все, подключая и т.д., и уже удался отправить строку через:

send(socket, string, string_size, 0); 

Итак, можно послать на структуру вместо строк через посыл()? Могу ли я просто заменить свой буфер на сервере пустой конструкцией того же типа и пойти?

+0

Спасибо всем за информативные ответы! – o01

ответ

1

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

Карл прав - вы можете отправить на структуру через сеть, говоря:

send(socket, (char*)my_struct, sizeof(my_struct), 0); 

Но вот вещь:

  • SizeOf (my_struct) может измениться между клиентом и сервером. Компиляторы часто выполняют некоторую отладку и выравнивание, поэтому, если вы не определяете выравнивание явно (возможно, используя #pragma pack()), этот размер может быть другим.
  • Другая проблема, как правило, байт-порядка. Некоторые машины являются большими, другие - малоподобными, поэтому расположение байтов может быть разным. В действительности, если ваш сервер или клиент не работает на не-Intel аппаратном обеспечении (что, вероятно, не так), то эта проблема существует более теоретически, чем на практике.

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

+0

Не то, чтобы это было очень важно, но вы должны, вероятно, применить структуру к void *, а не char *, так как это то, что send() прототипируется. –

+0

@rasher: Несмотря на то, что вы отмечаете как упаковку структуры, так и сериализацию, важно указать, что сериализация может убить производительность, если вы отправляете большой объем данных. Каждый вызов для отправки вызывает довольно дорогостоящий контекстный переход между пользовательским пространством и пространством ядра. Упаковка данных - действительно предпочтительный метод. –

0

Да, структуры одного типа имеют одинаковый размер. Если вы правильно набрали указатель, вам будет хорошо.

+0

Гуди! Не могли бы вы представить небольшой и простой пример того, как подойти к этому? – o01

+0

На самом деле не думаю, что ... структуры одного типа не имеют одинакового размера, потому что это зависит от дополнения, применяемого к членам структуры. Кроме того, у вас есть проблемы с архитектурой (32/64) и контентом. –

+0

Стефано и другие правы, конечно: это будет работать, только если вы работаете с совместимыми архитектурами C. Вы подтвердили, что это так, поэтому вы можете сделать свою отправку следующим образом (непроверенный, извините!) send (socket, ((char *) & yourStruct, sizeof (yourStruct), 0). Я замечаю, что это очень похоже на rasher's но я думаю, что немного правильнее принять адрес вашей структуры ('&'). –

4

Являются ли клиентские и серверные машины «одинаковыми»? То, что вы предлагаете, будет работать только в том случае, если компиляторы C на каждом конце выкладывают структуру в памяти точно так же. Есть много причин, почему это может быть не так. Например, клиентские и серверные макинтовы могут иметь разные архитектуры, а то, как они представляют числа в памяти (big-endian, little-endian), может отличаться. Даже если компьютеры-клиенты и серверные машины имеют одинаковую архитектуру, два разных компилятора C могут иметь разные политики для того, как они выкладывают структуры в памяти (например, заполнение полей для выравнивания ints на границах слов). Даже такой же консорциум с разными флагами может дать разные результаты.

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

+0

Да, клиент и серверная машина одинаковы, поэтому это не будет проблемой. Спасибо за подсказку tho :) – o01

2

Вы можете, если клиент и сервер выложили структуру точно так же, что означает, что поля имеют одинаковый размер с одинаковым заполнением. Например, если у вас есть long в вашей структуре, это может быть 32 бита на одном компьютере и 64 бита на другом, и в этом случае структура будет получена неправильно.

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

Для большинства клиент-серверных приложений это означает, что ответ заключается в том, что вы не можете делать это вообще. Фактически вы определяете сообщение с точки зрения количества отправленных байтов, какого порядка, что они означают и т. Д. Затем на каждом конце вы делаете что-то конкретное для платформы, чтобы убедиться, что структура, которую вы используете, имеет точно необходимый макет. Тем не менее, возможно, вам придется выполнить некоторую замену байтов, если вы отправляете целочисленные члены структуры little-endian, а затем вы хотите, чтобы ваш код работал на машине большого конца. Существуют так называемые форматы обмена данными, такие как XML, json и буферы протокола Google, так что вам не нужно делать это неудобно.

[Изменить: также помните, конечно, что некоторые элементы структуры никогда не могут быть отправлены по кабелю. Например, если ваша структура имеет указатель в ней, тогда адрес относится к памяти на отправляющей машине и бесполезен на принимающей стороне. Извините, если это уже очевидно для вас, но это, безусловно, не очевидно для всех, когда они только начинаются с C].

+0

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

+0

Правда. Я бы сказал, если вы на самом деле не читаете все байты в пространстве приложения (поскольку 'sizeof (thestruct)' меньше на читателе, чем писатель), то вы не получили сообщение. Кроме того, я думаю, что технически иногда неопределенное поведение заключается в том, чтобы скопировать произвольные байты над структурой - вы могли бы инициировать ловушки в элементах. Поэтому в этом случае сообщение не получено. Но вы правы, вы получите (некоторые из) байты сообщения, просто не понимаете самого сообщения. –

1

Вы можете, но вам нужно знать о двух важных вещах.

  1. Программы на обоих концах должны быть совместимы с ABI. Обычно они работают, если оба конца работают на одной и той же архитектуре процессора, той же ОС, скомпилированной с теми же флагами компилятора и компилятора.
  2. TCP - это поток. Вы должны убедиться, что вы отправляете всю структуру. Посмотрите документацию для вызова send() - он возвращает nr отправленных байтов - что может быть меньше, чем вы сказали ему. То же самое на стороне приемника. Просто потому, что вы отправили структуру с помощью 1-го вызова, не означает, что вы получите его с 1 вызовом recv. Чтобы получить все куски, сделайте несколько призывников.
0

В общем, это плохая идея, даже если ваш клиент и ваш сервер окажутся одинаково структурированы в памяти.

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

+0

Сериализация данных - самое худшее возможное решение, поскольку оно снижает производительность системы, вызывая дорогостоящие ненужные переключатели контекста между пространством пользователя и ядра для копирования данных в буферы ядра. Либо упорядочивайте элементы в 'struct', чтобы гарантировать, что нет отступов или использовать пакетную прагму. –

+0

Роберт С. Барнс: Я даже не знаю, как начать ответ на такое бессмысленное перераспределение. Давайте просто проигнорируем актуальность исходного вопроса на мгновение и подумаем о том, насколько велики будут объемы данных для копирования между пользовательским пространством и ядром, чтобы сделать измеримую разницу. С другой стороны, давайте не будем об этом думать. – hillu

+0

Возможно, мне нужно перефразировать: Сериализация должна быть вашим последним средством, поскольку каждый вызов для отправки вызывает чрезвычайно дорогостоящий контекстный переключатель. Например: http://www.linfo.org/context_switch.html «Контекстное переключение обычно интенсивно вычисляется, т. Е. Требует значительного времени процессора, которое может быть порядка наносекунд для каждого из десятков или сотен коммутаторов в секунду. Таким образом, переключение контекста представляет собой значительную стоимость системы с точки зрения процессорного времени и, по сути, может быть самой дорогостоящей операцией в операционной системе ». Посмотрите «стоимость переключения контекста» –

-3

Отправьте данные в виде текстового файла и затем декодируйте его после его получения. Это лучший способ, если вы хотите, чтобы ваши данные были отправлены!