Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > разные типы данных в сокет


Автор: knopka 17.11.2008, 01:27
сообщение передаваемое в сокет состоит из двух частей:
  • заголовка из  нескольких unsigned int полей 
  •  и текста
всё это должно передаваться в одном сообщении
Скажите пожалуйста как это лучше сделать 
Заранее спасибо

Автор: Andrey44 17.11.2008, 08:06
Создай структуру, и ее передавай.

Автор: knopka 17.11.2008, 09:16
Я так и делал, 
но со структурой приходит несколько символов, а должно быть явно  больше,
идеальным для меня было бы передача в формате вектор <char> 
но немогу сконвертировать структуру хотя бы в char

Автор: mrbrooks 17.11.2008, 09:27
knopka, ты бы код выложил. Достаточно твоего буфера и функций send(), recv(). 

Автор: REZiaMIX 17.11.2008, 09:59
Должно быть приблизительно так(в полупсевдокодеsmile ):
Код

struct packet{
char string[512];
}a;
recv(socket,(char*)&a,sizeof(a),NULL);
// отправка так
strcpy(a.string,"hello world!!!");
send(socket,(char*)&a,sizeof(a),NULL);

Или более гибкий вариант:
Код

struct packet{
uint len;
char*string;
}a;
recv(socket,(char*)&a,len,sizeof(a.len),NULL);
a.string = new char[a.len];
... обработка
delete [] a.string;
// отправка
char string[] = "Hello World!!!";
a.string = new char[strlen(string)];
strcpy(a.string,string);
a.len = strlen(string);
send(socket,(char*)&a,sizeof(a),NULL);

При передаче строки надо учитывать что она прийдет без \0.

Автор: knopka 17.11.2008, 12:06
to REZiaMIX 
Спасибо за помощь.
Цитата

... обработка

а как обработать ответное сообщение?
вид сообщения: �▒��趣��pW��������
первые четыре это len? а остальные string как их извлечь из буфера?

Добавлено через 6 минут и 2 секунды
а после отправки надо делать 
Цитата

delete [] a.string;

Автор: REZiaMIX 17.11.2008, 15:03
Цитата(knopka @ 17.11.2008,  12:06)
to REZiaMIX 
Спасибо за помощь.

а как обработать ответное сообщение?
вид сообщения: �▒��趣��pW��������
первые четыре это len? а остальные string как их извлечь из буфера?

Добавлено @ 12:12
а после отправки надо делать 
Цитата

delete [] a.string;

Цитата

первые четыре это len? а остальные string как их извлечь из буфера?

len - длина поля string , без нулевого символа.
Цитата

... обработка

Тут делаем с a.string что надо (но учесть что она без '\0')
Цитата

а после отправки надо делать 
Цитата

delete [] a.string;


Да , надо.

Объясню поподробней.
В гибком варианте идет расчет  , что при получении пакета мы сначала считаем len, len - длина поля string , без нулевого символа.
А потом считываем len байтов , в итоге получим все  ,что было отправлено.
То что я написал - просто для наглядности и понятия что надо делать. Приблизительный код будет таким , но тк я не тестировал возможны ошибки.
Просто надо понять что структура пакета:
len                     char
4 байта             len*char байтов
Поле len вноситься чтобы сервер знал , какой длины сообщение string.
При этом клиент должен соответственно заполнять поле len длиной string

Сам до конца не понял , но наверное надо использовать для структур правильное выравнивание , иначе возможны ошибки:
Код

#pragma pack(push, 1)

struct { /* ... */ };

#pragma pack(pop)


Автор: knopka 17.11.2008, 15:57
В том и загвоздка , что вопрос я упростил вопрос.
len у меня в задаче хранит всю длинну сообщения включая
несколько полей uint  и string

Автор: REZiaMIX 17.11.2008, 16:06
Цитата(knopka @ 17.11.2008,  15:57)
В том и загвоздка , что вопрос я упростил вопрос.
len у меня в задаче хранит всю длинну сообщения включая
несколько полей uint  и string

А зачем хранить длину полей фиксированной длины?
Код

struct packet
{
uint len;
char * string
uint val1;
uint val2;
}a;


Здесь мы должны знать len, чтобы считать string, а:
адрес val1 = sizeof(len)+ len;
val2 = sizeof(len)+ len + sizeof(val1);

Простая арифметика)

Автор: knopka 17.11.2008, 16:09
Цитата

a.len = strlen(string);

а разве len это не длинна поле string?
мы ведь нигде не добавляем к длинне сообщения ещё и размер самого поля uint

Добавлено через 2 минуты и 58 секунд
А если структуру преобразоватьв vector это не упростит задачу?

Добавлено через 11 минут и 19 секунд
С адресами я понял, а как из буфера (char*)&a
прочитать значение для первого поля структуры? 

Автор: REZiaMIX 17.11.2008, 16:46
Цитата(knopka @ 17.11.2008,  16:09)
Цитата

a.len = strlen(string);

а разве len это не длинна поле string?
мы ведь нигде не добавляем к длинне сообщения ещё и размер самого поля uint

Добавлено @ 16:12
А если структуру преобразоватьв vector это не упростит задачу?

Добавлено @ 16:20
С адресами я понял, а как из буфера (char*)&a
прочитать значение для первого поля структуры?

len - длина поля string , уже писал.
передавать vector думаю не лучшая идея smile
a - обычная структура , с ней и работать надо как со структурой)

Код

struct packet
{
uint len;
char * string
uint val1;
uint val2;
}a;

Код

a.packet
a.string
a.val1
a.val2

Автор: knopka 17.11.2008, 17:11
то есть при приёме
Код

recv(socket,(char*)&a,len,sizeof(a.len),NULL);
 
структура будет заполнена автоматически?

Автор: REZiaMIX 17.11.2008, 19:17
Цитата(knopka @ 17.11.2008,  17:11)
то есть при приёме
Код

recv(socket,(char*)&a,len,sizeof(a.len),NULL);
 
структура будет заполнена автоматически?

Все поля , которые имеют неопределенный размер(в нашем случае string) надо както отдельно обрабатывать.
Опечатка , там точка:
Код

recv(socket,(char*)&a.len,sizeof(a.len),NULL);
 
Тут мы получим размер поля , для последующего считывания string.
далее считаем string
Код

recv(socket,(char*)&a.string,a.len,NULL);

Если в структуре все поля имеют фикс. размер , то:
Код

recv(socket,(char*)&a,sizeof(a),NULL);

Автоматически заполнит всю структуру.


Автор: knopka 18.11.2008, 15:47
Чё -то сообщение пустое

Автор: REZiaMIX 18.11.2008, 16:14
Цитата(knopka @ 18.11.2008,  15:47)
Чё -то сообщение пустое

Чье?

Автор: bsa 18.11.2008, 17:32
knopka, если пишешь на С++, то можно сделать примерно так:
Код
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

struct Header
{
    int field1;
    int field2;
    int field3;
//...
    std::string text;
    void send(int socket) const {
        send(socket, reinterpret_cast<const char*>(&field1), sizeof(field1), NULL);
        send(socket, reinterpret_cast<const char*>(&field2), sizeof(field2), NULL);
        send(socket, reinterpret_cast<const char*>(&field3), sizeof(field3), NULL);
//...
        unsigned textSize = text.size();
        send(socket, reinterpret_cast<const char*>(&textSize), sizeof(textSize), NULL);
        send(socket, text.data(), textSize, NULL);
    }
    void recv(int socket) const {
        recv(socket, reinterpret_cast<char*>(&field1), sizeof(field1), NULL);
        recv(socket, reinterpret_cast<char*>(&field2), sizeof(field2), NULL);
        recv(socket, reinterpret_cast<char*>(&field3), sizeof(field3), NULL);
//...
        unsigned textSize;
        recv(socket, reinterpret_cast<char*>(&textSize), sizeof(textSize), NULL);
        std::vector<char> tmp(textSize);
        recv(socket, &*tmp.begin(), textSize, NULL);
        text.clean();
        std::copy(tmp.begin(), tmp.end(), std::back_inserter(text));
    }
};

Автор: knopka 18.11.2008, 19:33
to REZiaMIX

Цитата

Чё -то сообщение пустое 


от сервера 

на сервере обработка эхом
Код

a mess;

new_sock >> mess;//получили
new_sock << mess;//отослали 


стандартное построение классов Socket - ClientSocket - SimpleClientMain 

 а в клиенте  после приема ответ все поля у структуры а пустые

Автор: knopka 18.11.2008, 19:56
to bsa

не нашёл описания 
Цитата

reinterpret_cast

я так понимаю преобразует тип поля в <char*>?

а нельзя как то посимвольно перечитать структуру в вектор? (в смысле конвертировать)

буду пробовать и этот вариант

Автор: REZiaMIX 18.11.2008, 20:08
Цитата(bsa @ 18.11.2008,  17:32)
knopka, если пишешь на С++, то можно сделать примерно так:

А зачем посылать 3 инта отдельно?)

Автор: REZiaMIX 18.11.2008, 21:08
Дабы закончить эту тему:
Сервер:
Код

#include <windows.h>
#include <stdio.h>
#include <conio.h>

#pragma comment(lib,"ws2_32.lib")

SOCKET my_sock;
#pragma pack(push, 1)
struct packet
{
    int val1;
    int val2;
    int val3;
    unsigned int len;
    char *string;
    void pcsend(SOCKET to)
    {
        printf("Send:\nval1=%i\nval2=%i\nval3=%i\nlen=%i\nstring=%s\n",val1,val2,val3,len,string);
        send(to,(char*)this,sizeof(int)*3 + sizeof(unsigned int),NULL);
        send(to,string,len,NULL);
        delete [] string;
    }
    void pcrecv(SOCKET from)
    {
        recv(from,(char*)this,sizeof(int)*3 + sizeof(unsigned int),NULL);
        string = new char[len+1];
        recv(from,string,len,NULL);
        string[len] = '\0';
        printf("Recv:\nval1=%i\nval2=%i\nval3=%i\nlen=%i\nstring=%s\n",val1,val2,val3,len,string);
        val1++;
        val2++;
        val3++;
        delete [] string;
    }
};

#pragma pack(pop)
#pragma pack(pop)

#define SERVERADDR "127.0.0.1"

int main(int argc,char*argv[])
{
    unsigned int port = 5432;
    SOCKADDR_IN serverSockAddr;
    SOCKADDR_IN clientSockAddr;
    SOCKET serverSocket;
    SOCKET clientSocket;
    int addrLen=sizeof(SOCKADDR_IN);
    WSAData wd;
    WSAStartup(0x202,&wd);
    serverSocket=socket(AF_INET, SOCK_STREAM, 0);
    memset(&serverSockAddr, 0, sizeof(serverSockAddr));
    serverSockAddr.sin_port=htons(port);
    serverSockAddr.sin_family=AF_INET;
    serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    bind(serverSocket,
        (LPSOCKADDR) &serverSockAddr,
        sizeof(serverSockAddr));

    listen(serverSocket, 1);

    clientSocket=accept(serverSocket,
        (LPSOCKADDR) &clientSockAddr,
        &addrLen);
    char string[] = "Hello client!!!";
    packet tosend;
    tosend.pcrecv(clientSocket);
    tosend.len = strlen(string);
    tosend.string = new char[tosend.len+1];
    strcpy(tosend.string,string);
    tosend.pcsend(clientSocket); 
    _getch();
}


Клиент:
Код

#include <windows.h>
#include <stdio.h>
#include <conio.h>

#pragma comment(lib,"ws2_32.lib")
SOCKET my_sock;
#pragma pack(push, 1)
struct packet
{
    int val1;
    int val2;
    int val3;
    unsigned int len;
    char *string;
    void pcsend(SOCKET to)
    {
        printf("Send:\nval1=%i\nval2=%i\nval3=%i\nlen=%i\nstring=%s\n",val1,val2,val3,len,string);
        send(to,(char*)this,sizeof(int)*3 + sizeof(unsigned int),NULL);
        send(to,string,len,NULL);
        delete [] string;
    }
    void pcrecv(SOCKET from)
    {
        recv(from,(char*)this,sizeof(int)*3 + sizeof(unsigned int),NULL);
        string = new char[len+1];
        recv(from,string,len,NULL);
        string[len] = '\0';
        printf("Recv:\nval1=%i\nval2=%i\nval3=%i\nlen=%i\nstring=%s\n",val1,val2,val3,len,string);
        val1++;
        val2++;
        val3++;
        delete [] string;
    }
};

#pragma pack(pop)

#define SERVERADDR "127.0.0.1"

int main(int argc,char*argv[])
{
    unsigned int port = 5432;

    WSAData wd;
    WSAStartup(0x202,&wd);
    my_sock=socket(AF_INET,SOCK_STREAM,0);
    sockaddr_in dest_addr;
    dest_addr.sin_family=AF_INET;
    dest_addr.sin_port=htons(port);
    HOSTENT *hst;


    if (inet_addr(SERVERADDR)!=INADDR_NONE)
      dest_addr.sin_addr.s_addr=inet_addr(SERVERADDR);
    else
      if (hst=gethostbyname(SERVERADDR))
          ((unsigned long *)&dest_addr.sin_addr)[0]=
            ((unsigned long **)hst->h_addr_list)[0][0];

    connect(my_sock,(sockaddr *)&dest_addr,
                    sizeof(dest_addr));

    char string [] = "Hello Server!!!";        
    packet tosend;
    tosend.val1 = 666;
    tosend.val2 = 1;
    tosend.val3 = -1;
    tosend.len = strlen(string);
    tosend.string = new char[tosend.len+1];
    strcpy(tosend.string,string);
    tosend.string[tosend.len] = '\0';
    
    tosend.pcsend(my_sock);
    tosend.pcrecv(my_sock); 
    _getch();
}



Писал на коленках , но компилируется и работает 100%. Клиент посылает 3 числа и строчку. Сервер увеличивает числа на 1 , меняет строчку и отвечает.

Автор: knopka 19.11.2008, 00:11
to REZiaMIX

спасибо только не пойму где компилить 

судя по 
Цитата

#include <conio.h>

 в винде, попробовал в CodeBlocks и в VisualStudio ошибки

и ещё вопрос
Цитата

Код

send(to,(char*)this,sizeof(int)*3 + sizeof(unsigned int),NULL);
        send(to,string,len,NULL);


почему два раза посылаем, сначала цифры а потом строку?

Добавлено через 14 минут и 17 секунд
to REZiaMIX

Я Вас наверно уже достал ну ещё вопрос

А в Linux есть альтернатива
Код

WSAData 


Автор: REZiaMIX 19.11.2008, 12:06
Цитата

судя по 
Цитата

#include <conio.h>

 в винде, попробовал в CodeBlocks и в VisualStudio ошибки

conio.h используется для _getch();
можно заменить чемнить другим и избавиться от заголовка.

Цитата

и ещё вопрос
Цитата

Код

send(to,(char*)this,sizeof(int)*3 + sizeof(unsigned int),NULL);
        send(to,string,len,NULL);


почему два раза посылаем, сначала цифры а потом строку?

Т.к. string в данном случае в структуре - указатель на 0 символ строки.
Если послать структуры целиком , то вместо строки пошлем указатель на нее , что не верно.
Сначала посылаем 3 инта структуры(val1,val2,val3) + uint(len)
Код

sizeof(int)*3 + sizeof(unsigned int)

потом посылаем строку.
Цитата


А в Linux есть альтернатива
Код

WSAData 



Наверное не правильный выход , но прокатит это:
Код


    char buff[1024];
    WSAStartup(0x202,(WSADATA *) &buff[0]);

Вообще в никсах посмотри описание WSAStartup и подгони тип данных.

Автор: bsa 20.11.2008, 11:54
Цитата(knopka @ 18.11.2008,  19:56)
to bsa

не нашёл описания 
Цитата

reinterpret_cast

я так понимаю преобразует тип поля в <char*>?
reinterpret_cast это преобразование одного типа в другой. Используется, когда не работают static_cast и dynamic_cast. Это стандартные "преобразователи" C++. В принципе, можно написать и (const char*)&field1, но это устаревшая (для С++) форма записи.
Цитата(knopka @ 18.11.2008,  19:56)
а нельзя как то посимвольно перечитать структуру в вектор? (в смысле конвертировать)
Можно и структуру "конвертировать" в вектор, а нужно ли? Но мне показалось это лишним в данном месте...

Цитата(REZiaMIX)
А зачем посылать 3 инта отдельно?)
А ты уверен, что выравнивание у структуры будет на обеих сторонах (клиент-сервер) одинаковым?  smile 

Автор: knopka 20.11.2008, 18:19
Большое спасибо to REZiaMIX и bsa очень помогли вопрос закрыт

Автор: REZiaMIX 20.11.2008, 19:17
Цитата(bsa @ 20.11.2008,  11:54)
ты уверен, что выравнивание у структуры будет на обеих сторонах (клиент-сервер) одинаковым?  smile

Хм , ну если здесь пишем клиент  , то выравнивание задать можем , значит можем быть уверенны)
Вообще , нужно знать что нужно знать протокол клиент<->сервер , чтобы таких вопросов не возникало)
Думаю в данном случае оба решения подойдут)

Автор: knopka 20.11.2008, 19:34
ДА оба подходят

Автор: J0ker 20.11.2008, 20:26
все вышенаписанное для TCP соединения на прием работать НЕ БУДЕТ
т.к. фрагментация

Автор: REZiaMIX 21.11.2008, 08:25
Цитата(J0ker @ 20.11.2008,  20:26)
все вышенаписанное для TCP соединения на прием работать НЕ БУДЕТ
т.к. фрагментация

Хм , поясни , у меня все работает как ни крути

Автор: bsa 21.11.2008, 10:23
Кстати, мой вариант работать точно не должен - в описании recv написано, что в случае если полученные данные не помещаются в буфер - излишек теряется. Таким образом действительно надо делать все через вектор...

Автор: J0ker 21.11.2008, 18:49
Цитата(REZiaMIX @ 21.11.2008,  08:25)
Цитата(J0ker @ 20.11.2008,  20:26)
все вышенаписанное для TCP соединения на прием работать НЕ БУДЕТ
т.к. фрагментация

Хм , поясни , у меня все работает как ни крути

ну просто пока везет  smile 
данные, отправленные через TCP могут приходить как угодно фрагментированные, даже если были отправлены через send одним куском
recv заполняет буфер только тем, что пришло на данный момент

Добавлено через 1 минуту и 33 секунды
Цитата(bsa @  21.11.2008,  10:23 Найти цитируемый пост)
Кстати, мой вариант работать точно не должен - в описании recv написано, что в случае если полученные данные не помещаются в буфер - излишек теряется.

это верно только для UDP пакетов
для TCP ничего не теряется

Автор: REZiaMIX 22.11.2008, 12:26
Цитата(J0ker @ 21.11.2008,  18:49)
данные, отправленные через TCP могут приходить как угодно фрагментированные, даже если были отправлены через send одним куском
recv заполняет буфер только тем, что пришло на данный момент

В данном случае просто рассматривается логика протокола(т.е. все в идеальном состоянии) , а фрагментирование это уже другой разговор.

Автор: J0ker 22.11.2008, 17:26
Цитата(REZiaMIX @ 22.11.2008,  12:26)
Цитата(J0ker @ 21.11.2008,  18:49)
данные, отправленные через TCP могут приходить как угодно фрагментированные, даже если были отправлены через send одним куском
recv заполняет буфер только тем, что пришло на данный момент

В данном случае просто рассматривается логика протокола(т.е. все в идеальном состоянии) , а фрагментирование это уже другой разговор.

фрагментация относится как раз к логике протокола TCP
идеального состояния тут не существует - фрагментация может быть даже на локальной машине

Автор: REZiaMIX 23.11.2008, 11:54
Цитата(J0ker @ 22.11.2008,  17:26)
фрагментация относится как раз к логике протокола TCP
идеального состояния тут не существует - фрагментация может быть даже на локальной машине

Идеально состояние - нет потери пакетов , отсутствие фрагментации , но тут суть в том. Если вместо вызова recv подставить функцию , обеспечивающую склейку пакетов и тд , то этот подход будет работать как надо в любых предусмотренных случаях
P.S. Сотый пост)

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)