Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.11.25;
Скачать: CL | DM;

Вниз

Проблема с написанием чата...   Найти похожие ветки 

 
todeus ©   (2007-03-04 09:40) [0]

Я думаю все когда начинали изучение сокетов, писали свои(пусть даже примитивные) чаты. Вот и я решил этим занятся :)

Значит сервер я написал, клиент тоже.
Выглядит это довольно таки просто:
Сервер принимает сообщение от клиента и отсылает его потом всем клиентам которые подключены.
Клиент отсылает сообщение серверу и принимает от сервра ответ.

Запускаю я значит сервер, клиент. Все работает(С одним клиентом) то есть пишу в форме клиента сообщение, оно уходит на сервер и возвращается обратно. Запускаю воторй клиент. Пишу в нем, по идее должно сообщение уйти на сервер и прийти обоим клиентам, ан нет. На первый клиент сообщение приходит нормально, а вот на другой оно или приходит кракозябрами или вообще не приходит или приходит кусочек от сообщения, набор цифр и т.д. в общем очень походе что я неправильно принимаю число байт с сокета. А вот где я допустил ошибку найти не могу. Подскажите где я не прав

Код сервера:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, WinSock, StdCtrls;

const
 WM_SOCKET = WM_USER + 100;

type
 TSockMsg = record
   Msg: UINT;
   Sock: Integer;
   SelectEvent: WORD;
   SelectError: WORD;
   Result: LongInt;
 end;
 TForm1 = class(TForm)
   Button1: TButton;
   Label1: TLabel;
   Label2: TLabel;
   Label3: TLabel;
   Label4: TLabel;
   Label5: TLabel;
   procedure Button1Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
 private
   procedure SocketEvent(var Msg: TSockMsg); Message WM_SOCKET;
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 S: TSocket;
 AccS: array [1..100] of TSocket;
 Addr: TSockAddr;
 Data: TWSAData;
 Len: Integer;
 Arg:u_long;
 buff,str,buffer: string;
 i,k:integer;
 DataLen: LongInt;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
WSAStartup($101, Data);
S:=Socket(AF_Inet,Sock_Stream,0);
If S=Invalid_Socket then
 begin
 WSACleanup();
 label1.Caption:="Creating failed...";
 end
else
 begin
   Arg:=1;
   IOCtlSocket(S,FIONBIO,Arg);
   label1.Caption:="Socket create! OK!";
   Addr.sin_family:=PF_Inet;
   Addr.sin_port:=HToNS(5555);
   Addr.sin_addr.S_addr:=InAddr_Any;
   FillChar(Addr.sin_zero,SizeOf(Addr.sin_zero),0);
   if Bind(S,Addr,SizeOf(TSockAddr))=Socket_Error then
     begin
     Shutdown(S,SD_BOTH);
     WSACleanup();
     label2.Caption:="Bind failed...";
     end
   else
     begin
     label2.Caption:="Socket Bind! OK!";
     if listen(S,SoMaxConn)=Socket_Error then
       begin
       Shutdown(S,SD_BOTH);
       WSACleanup();
       label3.Caption:="Listening failed...";
       end
     else
       begin
       label3.Caption:="Listening! OK!";
       Len:=SizeOf(TSockAddr);
       WSAAsyncSelect(S, Handle, WM_SOCKET, FD_ACCEPT or FD_READ or FD_CLOSE );
       end;
     end;
 end;
end;

procedure TForm1.SocketEvent(var Msg: TSockMsg);
begin
case Msg.SelectEvent of
 FD_CLOSE:
 begin
   Shutdown(Msg.Sock,SD_BOTH);
   i:=i-1;
   label4.Caption:="Connected "+inttostr(i)+" client.";
 end;
 FD_ACCEPT:
 begin
   i:=i+1;
   AccS[i]:=Accept(S,@Addr,@Len);
   if AccS[i]=invalid_socket then
     label4.Caption:="Accept failed..."
   else
     label4.Caption:="Connected "+inttostr(i)+" client.";
 end;
 FD_READ:
 begin
  ioctlsocket(Msg.Sock, FIONREAD, DataLen);
  Fillchar(Buff,sizeof(buff),0);
  if Recv(Msg.Sock, Buff, sizeof(buff), 0)=Socket_Error then
   label5.Caption:="Recv failed..."
  else
   begin
   for k:=1 to i do
     if Send(AccS[k], Buff, SizeOf(Buff), 0)=Socket_Error then
       label5.Caption:="Send failed...";
     FillChar(Buff,sizeof(Buff),0);
   end;
 end;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
end;

end.



Код клиента:

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, WinSock, ExtCtrls;

const
 WM_SOCKET = WM_USER + 100;

type
 TSockMsg = record
   Msg: UINT;
   Sock: Integer;
   SelectEvent: WORD;
   SelectError: WORD;
   Result: LongInt;
 end;
 TForm1 = class(TForm)
   Button1: TButton;
   Edit1: TEdit;
   Label1: TLabel;
   Edit2: TEdit;
   Button2: TButton;
   Label2: TLabel;
   Timer1: TTimer;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
   procedure Timer1Timer(Sender: TObject);
 private
   procedure SocketEvent(var Msg: TSockMsg); Message WM_SOCKET;
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 S:TSocket;
 Addr:TSockAddr;
 Data:TWSAData;
 buff, Buffer:string;
 ip: PAnsiChar;
 DataLen: LongInt;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
ip:=PAnsiChar(Edit1.Text);
WSAStartup($101,Data);
S:=Socket(AF_Inet,Sock_Stream,0);
If S=invalid_socket then
 begin
 WSACleanup();
 label1.Caption:="Creating failed...";
 end
else
 begin
 Addr.sin_family:=PF_Inet;
 Addr.sin_port:=HToNS(5555);
 Addr.sin_addr.S_addr:=Inet_Addr(ip);
 FillChar(Addr.sin_zero,SizeOf(Addr.sin_zero),0);
 if connect(S,Addr,SizeOf(TSockAddr))=socket_error then
   begin
   Shutdown(S,SD_BOTH);
   WSACleanup();
   label1.Caption:="Connect failed...";
   end
 else
   begin
   label1.Caption:="Connected to "+ip;
   Button1.Enabled:=false;
   Edit1.Enabled:=false;
   Button2.Enabled:=true;
   Edit2.Enabled:=true;
   end;
 end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
buff:=Edit2.Text;
if send(s,buff,SizeOf(buff),0)=Socket_error then
 label2.Caption:="Send failed..."
else
 fillchar(buff,sizeof(buff),0);

end;

procedure TForm1.SocketEvent(var Msg: TSockMsg);
begin
case Msg.SelectEvent of
 FD_CLOSE:
 begin
   Shutdown(S,SD_BOTH);
   WSACleanup();
   label2.Caption:="Server down...";
 end;
 FD_READ:
 begin
  ioctlsocket(S, FIONREAD, DataLen);
  fillchar(Buffer,sizeof(DataLen),0);
  if Recv(S, Buffer, DataLen, 0)=Socket_Error then
   label2.Caption:="Recv failed..."
  else
    label2.Caption:=Buffer;
 end;
end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 WSAAsyncSelect(S, Handle, WM_SOCKET, FD_ACCEPT or FD_READ or FD_CLOSE );
end;

end.



И еще вот исходники на всякий случай:
http://todeus.hoha.ru/Chat.rar

Заранее спасибо :)


 
Dmitrij_K   (2007-03-04 11:57) [1]

Под переменную Buffer надо выделять память!

ioctlsocket(S, FIONREAD, DataLen);
SetLength(Buffer, DataLen);// fillchar(Buffer,sizeof(DataLen),0);


 
todeus ©   (2007-03-04 13:20) [2]

Не помогло...

Сделал в клиете:

  ioctlsocket(S, FIONREAD, DataLen);
  SetLength(Buffer,DataLen);
  fillchar(Buffer,sizeof(DataLen),0);
  if Recv(S, Buffer, sizeof(buffer), 0)=Socket_Error then
  ...


В сервере:

 ioctlsocket(Msg.Sock, FIONREAD, DataLen);
 SetLength(Buff, DataLen);
 Fillchar(Buff,sizeof(DataLen),0);
 if Recv(Msg.Sock, Buff, sizeof(buff), 0)=Socket_Error then
  ...


Ничего не изменилось :(


 
Сергей М. ©   (2007-03-05 08:16) [3]

Результат := Recv(S, PChar(Buffer)^, Length(buffer), 0)


 
todeus ©   (2007-03-05 10:27) [4]

Не помогло...

Клиент исправил на:

 ioctlsocket(S, FIONREAD, DataLen);
 SetLength(Buffer,DataLen);
 fillchar(Buffer,sizeof(DataLen),0);
 if Recv(S, Pchar(Buffer)^, Length(buffer), 0)=Socket_Error then
 ...


Сервер:
ioctlsocket(Msg.Sock, FIONREAD, DataLen);
SetLength(Buff, DataLen);
Fillchar(Buff,sizeof(DataLen),0);
if Recv(Msg.Sock, PChar(Buff)^, Length(buff), 0)=Socket_Error then
 ...


Стало еще хуже :(
Щас когда запускаю сервер и один клиент, клиентом посылаю что нибудь то назад ничего не приходит. А сервер пишет "Recv failed..."(Эт я ставил проверку если Recv=SOCKET_ERROR)... В общем щас сервер данные даже не принимает :(


 
todeus ©   (2007-03-05 10:29) [5]

И зачем использовать ссылку? По идее и без нее должно работать(хотя может я и ошибаюсь)...
И во всех примерах которые я видел, третим параметром recv() был именно sizeof() а не length()...


 
G_M_S   (2007-03-05 10:41) [6]


> sizeof(DataLen)

SizeOf - размер памяти, выделенной под переменную. В данном случае - замер типа LongInt. Возникает вопрос: если размер типа LongInt фиксирован (а он таки фиксирован, т.к. это один из стандартных базовых типов) - СКОЛЬКО именно байт за раз ты будешь читать?

fillchar(Buffer,sizeof(DataLen),0); - ИМХО глупо выглядит: сначала читаешь в DataLen количество байт, а потом передаешь размер самой переменной.


 
Сергей М. ©   (2007-03-05 11:30) [7]


> todeus


Учи Паскаль.


 
Сергей М. ©   (2007-03-05 11:54) [8]


> todeus


Кр.того, твоя логика манипуляций с массивом AccS не выдерживает никакой критики


 
todeus ©   (2007-03-05 14:58) [9]

Насчет AccS - первое что пришло в голову :)
to Сергей М.
И обьясни тогда почему с AccS работать не будет? Я там ошибки не вижу... Может много памяти жрет, может не красиво... Но по моему в этом месте оно работать должно...

to G_M_S
Насчет sizeof(DataLen) - сначала там было sizeof(buffer) потом, после нескольких изменений там было DataLen, потом уже после множественных copy/paste туда появилось sizeof(DataLen)
Проще говоря запарился :)

Сейчас это выглядит как(Сервер):

FD_READ:
 begin
 ioctlsocket(Msg.Sock, FIONREAD, DataLen);
 SetLength(Buff, DataLen);
 Fillchar(Buff,DataLen,0);
 if Recv(Msg.Sock, Buff, DataLen, 0)=Socket_Error then
   label5.Caption:="Recv failed..."
  else
   begin
   for k:=1 to i do
     if Send(AccS[k], Buff, DataLen, 0)=Socket_Error then
       label5.Caption:="Send failed...";
     FillChar(Buff, DataLen,0);
   end;
 end;


Клиент:

FD_READ:
 begin
  ioctlsocket(S, FIONREAD, DataLen);
  SetLength(Buffer,DataLen);
  fillchar(Buffer,DataLen,0);
  if Recv(S, Buffer, DataLen, 0)=Socket_Error then
   label2.Caption:="Recv failed..."
  else
    label2.Caption:=Buffer;
 end;


И по прежнему кракозябры вылазиют :)

to Сергей М.
Паскаль я учил.


 
G_M_S ©   (2007-03-05 15:05) [10]

Совет №1:
кончай маятся дурью, лезь на закладку Internet за TTCPServer/Client или на закладку Indy.

Совет №2: на моей памяти если сказано в доках "buffer", то надо брать как минимум динамический массив byte или Char, а уж никак не строку :))) Если требуется указатель на массив - то бери PChar.

Сорри, только сейчас заметил, что ты в String все ресейвишь :))))))))


 
Сергей М. ©   (2007-03-05 15:08) [11]


> Насчет AccS - первое что пришло в голову


И что, ты всегда пишешь программы методом "от балды" ?)


> обьясни тогда почему с AccS работать не будет?


Сначала объясни, чем тебе не угодили готовые компоненты ?


> Паскаль я учил.


Значит недоучил.


 
todeus ©   (2007-03-06 06:06) [12]


> Совет №1:кончай маятся дурью, лезь на закладку Internet
> за TTCPServer/Client или на закладку Indy


> Сначала объясни, чем тебе не угодили готовые компоненты
> ?

Блин, я не хочу учиться программировать методом: "Нажми на эту кнопочку, перетащи эту иконку, нажми запустить оно будет работать".
Хочу разобраться как и почему оно работает, а чтобы так разобраться надо писать код своими руками.


> Совет №2: на моей памяти если сказано в доках "buffer",
> то надо брать как минимум динамический массив byte или Char,
>  а уж никак не строку :))) Если требуется указатель на массив
> - то бери PChar.

Ща буду переделывать все :) Посмотрим че получиться :)


> И что, ты всегда пишешь программы методом "от балды" ?)

Да :)

Я же блин не пишу мегазверский чат который будет продаваться за офигенные бабки. Это просто я пытаюсь разобраться с WinSock - что это такое и счем их едят. Именно по этому я не хочу копать в сторону TTCPServer/Client и Indy.


 
Сергей М. ©   (2007-03-06 08:16) [13]


> todeus ©   (06.03.07 06:06) [12]


> Хочу разобраться как и почему оно работает


Понятно.

Тогда задумайся для начала, что произойдет с твоим сервером при подключении 101-го клиента


 
todeus ©   (2007-03-06 08:39) [14]

будет жопа. Это я знаю. :)
Мне щас хотябы для двух...

Я щас хочу разобраться как отсылать и прнимать данные...


 
Сергей М. ©   (2007-03-06 09:03) [15]


> todeus ©   (06.03.07 08:39) [14]


Далее.

Предположим, в некий момент времени подключены 2 клиента (i = 2), соответственно 1-й элемент массива содержих хэндл гнезда для первого активного соединения, а 2-й элемент - хэндл гнезда для второго.

Теперь, предположим, первое соединение корректно соединение разрывается по инициативе клиента, на сервере происходит событие FD_CLOSE, при этом ты декрементируешь значение переменной i (i = 1). 1-й элемент массива при этом содержит хэндл уже несуществующего гнезда ("мусор"), а 2-й элемент - хэндл гнезда по-прежнему существующего соединения.

Далее подключается некий клиент, происходит событие FD_ACCEPT, ты инкрементируешь i (i = 2) и тут же затираешь 2-й элемент массива, тем самым теряя валидный хэндл гнезда активного соединения.

А теперь думай, к чему это приводит ...


 
Сергей М. ©   (2007-03-06 09:18) [16]


> todeus


Важная деталь - событие FD_CLOSE не происходит при физическом разрыве канала соединения, к примеру, компьютер партнера неожиданно обесточен или физически отключен от информационной сети.


 
lilirw   (2007-03-25 09:14) [17]

ITs Like ME
[url=http://myurl.com.tw/jcos]casino bonus[/url]



Страницы: 1 вся ветка

Текущий архив: 2007.11.25;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.014 c
8-1170069211
T54
2007-01-29 14:13
2007.11.25
Видео


2-1194094219
Kolan
2007-11-03 15:50
2007.11.25
Как проеделить координаты для DropDownMenu у ToolButton?


11-1163342104
Ned
2006-11-12 17:35
2007.11.25
Отцентровать изображение


2-1194007977
BATTLE_MAX
2007-11-02 15:52
2007.11.25
Сохранение TImageList


15-1192827366
Nic
2007-10-20 00:56
2007.11.25
Какой регистратор лучше для...