Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2007.11.25;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.52 MB
Время: 0.047 c
15-1188359243
georgius
2007-08-29 07:47
2007.11.25
Новая версия urlmon


15-1192992887
@!!ex
2007-10-21 22:54
2007.11.25
Восстановление аккаунта The Bat


3-1184003913
IMHO
2007-07-09 21:58
2007.11.25
SQLite 3


6-1161104009
BFG9k
2006-10-17 20:53
2007.11.25
обработка PHP сценариев с помощью HTTP сервера Indy


15-1193108125
Slider007
2007-10-23 06:55
2007.11.25
С днем рождения ! 23 октября 2007 вторник





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский