Форум: "Сети";
Текущий архив: 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