Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Сети > Переслать структуру


Автор: MaXL 28.1.2008, 15:41
Всем привет.
Есть структура:
Код

  TPacket = record
    u_Magic:  WORD;   //магическое поле, должно быть равно в $0D
    u_Type:   WORD;   //тип пакета, константы начинаются с PT_***
    u_Hor:    WORD;   //если ходит, то это вертикальный номер ячейки
    u_Vert:   WORD;   //а это её горизонтальный
    u_Answer: WORD;   //ответ на сделанный ход, константы на AW_***
    c_Text:   array[0..245] of char; //если чат, то тут сообщение
  end; //общий размер структуры $100 байт.

Её нужно переслать использую средства WinSock, ну и потом ессно получить, пользуясь теме же средствами.
Пытался делать так:
Код

  ZeroMemory(@sendPacket, SizeOf(sendPacket));
  sendPacket.u_Magic:= $0D;
  sendPacket.u_Type:= PT_HELO;
  send(clientSocket, sendPacket, SizeOf(sendPacket), 0);

Функция send возвращает _не_ INVALID_SOCKET. Т.е. данные ушли, но потом я пытаюсь читать так:
Код

recv(serverSocket, inPacket, PACKET_SIZE, 0);

Ну и там приходят всякая муть.
Почитал форум, вроде бы как из слов понял, что так делать нельзя.
Был совет пересылать частично, так вот частично это как ? кто может посоветовать алгоритм ?
Просто у  меня программа может быть либо сервером либо клиентом, и код для них(именно код обработки входящих пакетов) один.
И ещё, пишу используя не блокирующий режим сокетом, т.е. сообщения посылаются моему приложению, а он уже обрабатывает.
Кто что посоветует ?

Автор: MaXL 23.2.2008, 03:23
Вопрос всё ещё актуален.

Автор: SneG0K 23.2.2008, 06:09
Код

send(Sock, TPacket.u_Magic, sizeof(TPacket.u_Magic));


И так далее... береш элемент из структуры и отсылаеш его... Береш следующий и отсылаеш следующий и так далее...

Автор: MetalFan 23.2.2008, 14:31
SneG0K, а нафиг так? ошибка явно не в этом.

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

Автор: vovksextra 23.2.2008, 15:35
Должно все работать

не видно в каком режиме сокет работает. нужен полный код .

Автор: MaXL 23.2.2008, 16:44
Всё, что касается сервера.
Вот функция, которая запускает сервер:
Код

//почти все переменные глобальные
function TPlayForm.StartServer: boolean;
var
  errId: DWORD;
begin
  Result:= false;
  serverSocket:= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
  if serverSocket = INVALID_SOCKET then
    begin
      MessageBoxExA(Handle, 'Couldn`t create socket', 'Error', MB_ICONERROR, 0);
      exit;
    end;

  ZeroMemory(@servAddr, SizeOf(servAddr));
  servAddr.sin_port:= htons(serverPort);
  servAddr.sin_family:= AF_INET;
  servAddr.sin_addr.S_addr:= INADDR_ANY;

  if bind(serverSocket, @servAddr, SizeOf(servAddr)) = SOCKET_ERROR then
    begin
      MessageBoxExA(Handle, PChar('Couldn`t bind port ' + IntToStr(serverPort)),
        'Error', MB_ICONERROR, 0);
      exit;
    end;

  if listen(serverSocket, 1) = SOCKET_ERROR then
    begin
      MessageBoxExA(Handle, PChar('Couldn`t listen on port ' + IntToStr(serverPort)),
        'Error', MB_ICONERROR, 0);
      exit;
    end;

  WSAAsyncSelect(serverSocket, PlayForm.Handle, WM_NETWORK,
    FD_ACCEPT or FD_WRITE or FD_READ or FD_CLOSE);
  Result:= true;
end;

Вот процедура, которая выполняется, когда окно приходит сообщение WM_NETWORK:
Код

procedure TPlayForm.DispatchSocketEvent(var Msg: TMessage);
var
  inPacket: TPacket;
  addrLen: integer;
begin
  ZeroMemory(@inPacket, PACKET_SIZE);
  case WSAGetSelectEvent(Msg.LParam) of
    FD_ACCEPT:
      begin
        clientSocket:= TSocket(Msg.WParam);
        addrLen:= SizeOf(clientAddr);
        clientSocket:= Accept(clientSocket, clientAddr, addrLen);
      end;
    FD_WRITE:
      begin
      end;
    FD_READ:
      begin
        recv(serverSocket, inPacket, PACKET_SIZE, 0);
        DispatchPacket(d);
      end;
    FD_CLOSE:
      begin
      end;      
  end;
end;

Вот код процедуры DispatchPacket:
Код

procedure TPlayForm.DispatchPacket(Packet: TPacket);
var
  newPacket: TPacket;
begin
  if Packet.u_Magic <> $0D then exit;
  ZeroMemory(@newPacket, SizeOf(newPacket));
  newPacket.u_Magic:= $0D;
  //обработка пакета
  case Packet.u_Type of
    PT_HELO:
      begin
        playing:= true;
        if serverStarted then
          begin
            newPacket.u_Type:= PT_HELO;
            send(clientSocket, newPacket, SizeOf(newPacket), 0);
          end;  
      end;
    PT_CHAT:
      begin
        ChatMemo.Lines.Add(Packet.c_Text);
      end;
    PT_PING:
      begin
        newPacket.u_Type:= PT_PONG;
        send(clientSocket, newPacket, SizeOf(newPacket), 0);
      end;
    PT_PONG:
      begin
      end;
   //ну и тут обработчик для этих констант, пока всё пусто 
   //...
  end;
end;     

Теперь всё, что касается по поводу клиента.
Подключение к серверу:
Код

function TPlayForm.ConnectToServ: boolean;
begin
  Result:= false;
  ZeroMemory(@servAddr, sizeof(servAddr));
  clientSocket:= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  servAddr.sin_family:= AF_INET;
  servAddr.sin_port:= htons(serverPort);
  servAddr.sin_addr.S_addr:= inet_addr(PChar(serverIP));

  WSAAsyncSelect(clientSocket, Handle, WM_NETWORK,
    FD_ACCEPT or FD_WRITE or FD_READ or FD_CLOSE);//т.е. используем одну процедуру и для сервера и для клиента.

  connect(clientSocket, @servAddr, sizeof(servAddr));
  Result:= true;
end;

А вот код, с помощью которого я посылаю:
Код

var
  sendPacket: TPacket;
  msg: String;
begin
  //...
  ZeroMemory(@sendpacket, SizeOf(sendPacket));
  sendPacket.u_Magic:= $0D;
  sendPacket.u_Type:= PT_CHAT;
  msg:= MessageEdit.Text;
  if Length(msg) = 0 then exit;
  lstrcpy(sendPacket.c_Text, PChar(msg));
  if send(clientSocket, sendPacket, SizeOf(sendPacket), 0) = SOCKET_ERROR then
    //...
  //...
end;

Ну вот так..=)

Автор: MetalFan 23.2.2008, 20:02
Цитата(MaXL @  28.1.2008,  15:41 Найти цитируемый пост)
  TPacket = record
    u_Magic:  WORD;   //магическое поле, должно быть равно в $0D
    u_Type:   WORD;   //тип пакета, константы начинаются с PT_***
    u_Hor:    WORD;   //если ходит, то это вертикальный номер ячейки
    u_Vert:   WORD;   //а это её горизонтальный
    u_Answer: WORD;   //ответ на сделанный ход, константы на AW_***
    c_Text:   array[0..245] of char; //если чат, то тут сообщение
  end; //общий размер структуры $100 байт.

забавно...
последний элемент записи 246 байт... то каким образом вся структура 100 байт??

Добавлено через 1 минуту и 6 секунд
и еще, автор, если ты не можешь выложить пример проекта, то кури его сам...
те куски кода, что ты привел, не воссоздают полную картину.

Автор: MaXL 24.2.2008, 04:46
MetalFan, да, правильно последний массив 246, потом ещё пять вордов, и получается 256, а в шестнадцатиричной 0x100(или $100).
Вроде бы именно эти куски кода за всё и отвечают, ну да ладно вот выкладываю полностью этот проджект, пока там всё криво, но это только пока, но надеюсь, что разобраться будет не сложно.

Автор: MetalFan 24.2.2008, 11:31
MaXL, а, сорь, не заметил знак бакса %)
гляну на досуге проект)

Автор: dumb 24.2.2008, 13:49
особо не всматривался, но налицо самая распространенная ошибка - "пакетный tcp".
tcp - поточный протокол. для простоты, представь, что recv вычитывает данные порциями от 1 до 3 байт(и неважно, что send послал сразу всю структуру). будет твоя программа работать?

Автор: MetalFan 24.2.2008, 14:01
ага, ну так ясно...
ты в момент получения события FD_READ пытаешься читать данные из сокета сервера...
замени
Код

procedure TPlayForm.DispatchSocketEvent(var Msg: TMessage);
var
  inPacket: TPacket;
  addrLen: integer;
  lSz: Integer;
begin
...
    FD_READ:
      begin
        lSz := recv(TSocket(Msg.wParam), inPacket, PACKET_SIZE, 0); //было ServerSocket. можно или так или ClientSocket
        DispatchPacket(inPacket);
      end;
...
end;

и все, сообщения приходят норм

Добавлено через 42 секунды
ах да, lSz ввел для контроля за размером принятых данных

Автор: MaXL 24.2.2008, 15:02
dumb, спасибо.
MetalFan, спасибо. +1  smile 

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