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

Вниз

WinSock сервер, события   Найти похожие ветки 

 
Delpher_Gray ©   (2004-02-27 06:43) [0]

Как сделать, чтоб программа воспринемала сообщения от сокета ?
Вот код небольшой программки:
...

var
 Form1: TForm1;
 Data: WSAData;
 Sock: TSocket;
 Addr: TSockAddr;
 Msg: integer;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
 if WSAStartup($101, Data) <> 0 then
   Die;

 Sock := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

 if Sock = INVALID_SOCKET then
   Die;
 
 WSAAsyncSelect(Sock, Form1.Handle, Msg, FD_READ or FD_WRITE or FD_CLOSE);

 Addr.sin_family := AF_INET;
 Addr.sin_addr.S_addr := INADDR_ANY;
 Addr.sin_port := htons(1379);

 Bind(Sock, Addr, SizeOf(Addr));
 Listen(Sock, SOMAXCONN);
end;


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

Заранее благодарен !


 
Digitman ©   (2004-02-27 08:14) [1]

для начала скажи, это что, учебная задача ?
почему не используешь стандартные TClient/ServerSocket ?


 
Rouse_ ©   (2004-02-27 09:04) [2]

К примеру Msg у тебя обявлена как WM_ASYNC = WM_USER + 1;

тогда делай процедуру
procedure SockWND(var AMsg: TMessage); message WM_ASYNC;

в которой обрабатывай
WSAGetSelectEvent(Amsg.LParam) который равен FD_READ
в нем и читай...

А вообщето действительно лучше TClient/ServerSocket


 
Digitman ©   (2004-02-27 09:19) [3]

угу ... а то "из пушки по воробьям" получается - VCL все равно используется, размер модуля Forms будет определяющим в размере готового исп.файла, и попытки "сэкономить" на программиновании трансп.подсистемы непосредственно на WSAPI (если даже чем-то оправданы) выглядят в данном контексте по меньшей мере смешными


 
Delpher_Gray ©   (2004-02-27 20:55) [4]

Не понимаю :)

Вот почему тут вылазеет сообщение, только при соединении ? А при чтении данных нет ??

...

const
 WM_ASYNC = WM_USER + 1;

type
 TForm1 = class(TForm)
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   procedure SockWND(var AMsg: TMessage); message WM_ASYNC;
 public
   { Public declarations }
 end;

var
 Form1: TForm1;
 Data: WSAData;
 Sock: TSocket;
 Addr: TSockAddr;

...

procedure TForm1.SockWND(var AMsg: TMessage);
begin
 //WSAGetSelectEvent(AMsg.LParam);
 ShowMessage("Hi ! I""m socket ! It""s EVENT !");
 inherited;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 Msg: integer;
begin
 Msg := WM_ASYNC;

 if WSAStartup($101, Data) <> 0 then
   Die;

 Sock := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

 if Sock = INVALID_SOCKET then
   Die;
 
 WSAAsyncSelect(Sock, Form1.Handle, WM_ASYNC, FD_READ or FD_WRITE or FD_ACCEPT or FD_CONNECT or FD_CLOSE);

 Addr.sin_family := AF_INET;
 Addr.sin_addr.S_addr := INADDR_ANY;
 Addr.sin_port := htons(1379);

 Bind(Sock, Addr, SizeOf(Addr));
 Listen(Sock, SOMAXCONN);
end;


2Digitman:
>для начала скажи, это что, учебная задача ?
Да это я учусь :)


 
Verg ©   (2004-02-27 21:08) [5]

А чего ж ты хочешь?
Зачем listen сокету события  FD_READ, FD_WRITE, FD_CONNECT, FD_CLOSE?
Не надо ему этого всего.
Он же всего "ловец" соединений, и больше ничего.
Его задача - при FD_ACCEPT "клонировать" сокет вызовом newclientsocket:=accept(....), ну и отдать новый сокет на отдельное обслуживание, установив этому клону, возможно, WSAAsyncSelect ко всем выше перечисленным событиям. С тех пор newclientsocket начинает свою собственную, абсолютно самостоятельную жизнь.


 
Verg ©   (2004-02-27 21:15) [6]

Удалено модератором
Примечание: Оффтоп...


 
Rouse_ ©   (2004-02-28 16:43) [7]

> Verg ©   (27.02.04 21:08) [5]
Андрей, предлагаю указывать на ошибки а не подначивать...
В данном варианте былобы правильней обяснение почему нежелательно использовать данную комбинацию сообщений...
Как я представляю - если постоянно твердить человеку, что ты не правильно делаешь - он всеравно не научится и будет совершать теже ошибки, что и прежде...
И опять придется отвечать на эти вопросы снова и снова ;)

PS: Извини если задел


 
Delpher_Gray ©   (2004-02-29 21:12) [8]

Rouse прав !
Если я не знаю как это делать, зачем мне рассказывать теорию без исходного кода, если я сам всё равно ничего не напишу ?

Напишите плз конкретный пример.

Заранее благодарен !!


 
Verg ©   (2004-02-29 21:22) [9]


> Delpher_Gray ©   (29.02.04 21:12) [8]


А понял -  типа "заткнись и пиши мне программу. И нечего тут базарить. Я жду"

Жди. :)


> если я сам всё равно ничего не напишу


В десятку попал.


 
Verg ©   (2004-02-29 21:37) [10]


> Rouse_ ©   (28.02.04 16:43) [7]


По-моему я ясно написал, что просто бесполезно ждать этих событий от данного типа сокета. Это не ошибка, это просто бессмысленность.

Что нужно делать на единственное полезное событие я тоже написал

> при FD_ACCEPT "клонировать" сокет вызовом newclientsocket:=accept(....),
> ну и отдать новый сокет на отдельное обслуживание, установив
> этому клону, возможно, WSAAsyncSelect ко всем выше перечисленным
> событиям.


Вроде 99% моего поста - это разъяснения как надо поступать, а вовсе не то, о чем ты пишешь.


 
Delpher_Gray ©   (2004-03-01 19:41) [11]

Прочитал ещё немножко статей вроде-бы разобрался теперь, но не могу причитать данные...

Вот что у меня получилось:
const
 WM_ASYNC = WM_USER + 1;

...

procedure TForm1.SockWND(var AMsg: TMessage);
var
 MS: TMemoryStream;
 Str: string;
begin
 if WSAGetSelectEvent(AMsg.LParam) = FD_ACCEPT then
 begin
   NewSock := accept(Sock, nil, nil);
   NewSock := WSAAsyncSelect(NewSock, Form1.Handle, WM_ASYNC, FD_READ);
 end else
 if WSAGetSelectEvent(AMsg.LParam) = FD_READ then
 begin
   Stat.Lines.Add("FD_READ");
   recv(NewSock, Str, 10, 0);
   // Тут Str забивается пробеламИ, а должен быть принятый текст
 end;
end;

...

WSAAsyncSelect(Sock, Form1.Handle, WM_ASYNC, FD_ACCEPT);


 
Delpher_Gray ©   (2004-03-01 19:42) [12]

Забыл :)
Sock, NewSock: TSocket;


 
Verg ©   (2004-03-01 20:23) [13]


> [11] Delpher_Gray ©   (01.03.04 19:41)


Ну вот. Теперь уже ближе к "телу".

1. WSAGetSelectEvent(AMsg.LParam) - это необязательно, можно просто MSG.LParamLo
2. Сокет, к которому относится событие находится в MSG.WParam. т.е.
если применительно к Sock твоя вольность была "дозволительна", то для клиентских сокетов, образованных accept это уже не проканает (как ты их будешь отличать в обрабтчике сообщений, когда их будет много?)
3. String - это hugestring, т.е. неявный указатель на динамически распределяемую область памяти. Т.о., перед тем как использовать его в качестве буфера, ему надо сделать SetLength(str, сколько надо); А уже затем - received := recv(Msg.WParam, Str[1], length(Str), 0); Заметь, что received может оказаться меньше length(Str) - это надо учесть.
4. По ходу работы с клиентским сокетом необходимо будет обрабатывать FD_WRITE (асинхронная запись) и FD_CLOSE (закрытие сокета-соединения клиентом(собеседником)).


 
Delpher_Gray ©   (2004-03-02 11:26) [14]

>Сокет, к которому относится событие находится в MSG.WParam. т.е.если применительно к Sock твоя вольность была "дозволительна", то для клиентских сокетов, образованных accept это уже не проканает (как ты их будешь отличать в обрабтчике сообщений, когда их будет много?)

Подскажи как решить эту проблему ??

И ещё вопрос: как быть в консольном приложении ? Сообщения не обрабатываются, по моим догадкам потому-что THandle не зарегистрирован в Windows.
Как это сделать, и в этом ли дело ??


 
Verg ©   (2004-03-02 11:36) [15]


> Подскажи как решить эту проблему ??


Я ж говорю:
не    ....:= recv(NewSock, ....);
а     ....:= recv(Msg.WParam, ....);

Про консольное и не только приложение.
Посмотри внимательно как реализованы компоненты TServerSocket в асинхронном режиме в модуле ScktComp.pas

Там ты найдешь ответы почти на все свои вопросы.


 
Digitman ©   (2004-03-02 11:43) [16]


> Delpher_Gray


а что за блажь - реализовывать серверную часть в конс.процессе, когда на то есть сервис-процессы при полной поддержке любой ОС на NT-платформе ? В частности ВинХрю, которая заявлена тобой в вопросе ?


 
Polevi ©   (2004-03-02 11:47) [17]

>Digitman ©   (02.03.04 11:43) [16]
думаю сервисы ему сюда никчему, пусть сначала с winsock разберктся


 
Delpher_Gray ©   (2004-03-02 12:25) [18]

Всем пасиба, заработало !
Я юзаю функцию AllocateHWnd(Method: TWndMethod): HWND; модуля Classes.

Если подключать этот самый модуль, но программа очень сильно "разбухает" :(

Подскажите как это можно сделать без модуля Classes.


 
Reindeer Moss Eater ©   (2004-03-02 12:33) [19]

uses Windows;

CreateWindow


 
Digitman ©   (2004-03-02 12:34) [20]

а взглянуть в модуль и посмотреть, как это реализовано у Борланда, - не судьба ?


 
Digitman ©   (2004-03-02 12:35) [21]


> программа очень сильно "разбухает"


у тебя же учебные цели ?!!


 
Polevi ©   (2004-03-02 12:41) [22]

>Delpher_Gray ©   (02.03.04 12:25) [18]
имей совесть, сколько можно одно и тоже тебе объяснять

http://delphimaster.net/view/6-1073556999/


 
Delpher_Gray ©   (2004-03-02 12:42) [23]

2Digitman:
Учебные цели - это сначала...
Чем дальше, тем сложнее ! Пытаюсь рнаписать мини-сервер...

>а взглянуть в модуль и посмотреть, как это реализовано у Борланда, - не судьба ?

Уже взглянул, вот что получилось:
program Server;

uses
 KOL, Windows, Messages, WinSock;

const
 WM_SOCK = WM_USER + 1;

type
 TServer = class
 private
   procedure SockWND(var AMsg: TMessage); message WM_SOCK;
 end;

var
 wc:WNDCLASS;
 Wnd: THandle;
 Msg: TMsg;
 Cancel: boolean;
 Stream: PStream;
 Data: WSAData;
 Sock, ASock: TSocket;
 Addr: TSockAddr;
 OffSet, Received: integer;
 Buff: array[1..1024] of Byte;
 Serv: TServer;

procedure TServer.SockWND(var AMsg: TMessage);
begin
 case AMsg.LParam of
   FD_ACCEPT:
   begin
     MsgOk("FD_ACCEPT");
     ASock := accept(Sock, nil, nil);
     ASock := WSAAsyncSelect(ASock, Wnd, WM_SOCK, FD_READ);
   end;
   FD_READ:
   begin
     MsgOk("FD_READ");
     ioctlsocket(AMsg.WParam, FIONREAD, Longint(OffSet));
     Received := recv(AMsg.WParam, Buff[1], SizeOf(Buff), 0);
     MsgOk(Int2Str(Received)+" &#232;&#231; "+Int2Str(OffSet));
   end;
 end;
end;

begin
 Wnd := CreateMutex(nil, false, "Server");
 if WaitForSingleObject(Wnd, 0) <> wait_TimeOut then
 begin
   Cancel := false;

   with Wc do
   begin
    Style:= 0;
    lpfnWndProc:= @DefWindowProc;
    cbClsExtra:= 0;
    cbWndExtra:= 0;
    hInstance:= 0;
    hIcon:= 0;
    hCursor:= 0;
    hbrBackground:= 0;
    lpszMenuName:= nil;
    lpszClassName:= "Server";
    hInstance := HInstance;
   end;

   Windows.RegisterClass(Wc);
   Wnd := CreateWindowEx(WS_EX_TOOLWINDOW, wc.lpszClassName,
     "", WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, nil);

   if WSAStartup($101, Data) <> 0 then
     Cancel := true;

   Sock := Socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

   if Sock = INVALID_SOCKET then
     Cancel := true;

   WSAAsyncSelect(Sock, Wnd, WM_SOCK, FD_ACCEPT);

   Addr.sin_family := AF_INET;
   Addr.sin_addr.S_addr := INADDR_ANY;
   Addr.sin_port := htons(1379);

   Bind(Sock, Addr, SizeOf(Addr));
   Listen(Sock, SOMAXCONN);

   Stream := NewMemoryStream;

   while not Cancel do
   begin          
     while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
     begin
       TranslateMessage(Msg);
       DispatchMessage(Msg);
     end;
     Sleep(5);
   end;

   Stream.Free;

   WSACleanUp;
 end;
end.


Однако не работает... А почему ?


 
Polevi ©   (2004-03-02 12:46) [24]

>Delpher_Gray ©   (02.03.04 12:42) [23]
Polevi ©   (02.03.04 12:41) [22]


 
Digitman ©   (2004-03-02 12:52) [25]


>      ASock := accept(Sock, nil, nil);
>      ASock := WSAAsyncSelect(ASock, Wnd, WM_SOCK, FD_READ);


это что за бред ?

в ASock записывается хэндл нового гнезда и тут же затирается !!


 
Digitman ©   (2004-03-02 12:57) [26]


> Wnd := CreateMutex(nil, false, "Server");
...
> Wnd := CreateWindowEx(WS_EX_TOOLWINDOW, wc.lpszClassName,
>      "", WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, nil);


а это что за бред ?

в Wnd сначала пишется хэндл мьютекса (на кой шут он здесь - тоже непонятно), и тут же затирается хэндлом окна ...

а Stream зачем ?
а Sleep ? зачем ?

ты когда откуда-то некий код сдираешь, ты хоть думай немного, зачем этот код нужен. что он делает, в какой момент времени и почему !


 
Digitman ©   (2004-03-02 12:59) [27]

и еще .. подумай, каким образом твой конс.процесс м.б. корректно завершен юзером, если while-цикл крутится бесконечно


 
Digitman ©   (2004-03-02 13:01) [28]

пардон ..

да там Peekmessage !!

тело цикла даже в одной итерации не выполнится, и цикл будет прерван, если на момент PeekMessage сообщений в очереди нет


 
Delpher_Gray ©   (2004-03-02 13:05) [29]

>если while-цикл крутится бесконечно
Цикл крутится, пока Cancel = false

Mutex нужен чтоб избежать повторного запуска программы.

>Polevi ©   (02.03.04 12:41) [22]
Хе, а про это я уже забыл :) Давно было...
Но тут проблема...
Почему-то когда я сам создаю окно (CreateWindowEx), то процедура приёма сообщений procedure TServer.SockWND(var AMsg: TMessage); не работает !
А если обрабатывать сообщения в цикле, то всё работает !? Как сделать чтоб заработала procedure TServer.SockWND(var AMsg: TMessage); ?


 
Digitman ©   (2004-03-02 13:10) [30]


> Delpher_Gray ©   (02.03.04 13:05) [29]
> Цикл крутится, пока Cancel = false


ну и в каком месте ты намерен взводить этот флаг ?


> Mutex нужен чтоб избежать повторного запуска программы.


на кой ляд тебе это сейчас нужно ? не меси ты кислое с пресным ! научись корректно работать с Winsock, потом уже будешь накручивать в программе иную функц-ть , ну никак не связанную с транспортом !


 
Digitman ©   (2004-03-02 13:12) [31]

а уж если тебе нужен мьютекс, то почему затираешь значение его хэндла ? закрывать-то потом мьютекс как будешь в ДАННОМ процессе, если ты хэндл его потерял ? понадеешься на ОС что ли ?


 
Delpher_Gray ©   (2004-03-02 13:18) [32]

Cancel := true; Можно писать например если принято какое-нить сообщение от сокета.

С мьютексом да будет так:
 if WaitForSingleObject(CreateMutex(nil, false, "Server"), 0) <> wait_TimeOut then
 begin
   ...
 end;


Всё работает нормально...

Ты лучше подскажи почему у меня SockWND(var AMsg: TMessage); не работает !??


 
Polevi ©   (2004-03-02 13:24) [33]

>Ты лучше подскажи почему у меня SockWND(var AMsg: TMessage); не работает !??
а где она у тебя вызыввется ?


 
Digitman ©   (2004-03-02 13:29) [34]


> почему у меня SockWND(var AMsg: TMessage); не работает !??


а почему это должно работать ?

этот метод будет вызван ТОЛЬКО в случае вызова метода Dispatch() твоего объекта TServer()

TServer = class
private
  procedure SockWND(var AMsg: TMessage); message WM_SOCK;
protected
  procedure Run;
end;
...
procedure TServer.Run;
var
 Msg: TMsg;
begin
 while GetMessage(Msg, 0, 0, 0) do
  if Msg.hWnd = Wnd then
    Dispatch(Msg.Message)
  else
    DispatchMessage(Msg);
end;


 
Delpher_Gray ©   (2004-01-23 13:21) [35]

Не понял :))

В модуле Classes обработка сообщений сделана так:
SetWindowLong(Wnd, GWL_WNDPROC, ???);

Вместо ??? идёт процедура, которая будет вызываться, но как всё это написать !?

SetWindowLong(Wnd, GWL_WNDPROC, Serv.SockWND); - ошибка

Где Serv - TServer;


 
Digitman ©   (2004-03-03 17:48) [36]


> Delpher_Gray ©   (23.01.04 13:21) [35]


бросай "сети"

у тебя не хватает базовых знаний по Delphi Objectpascal


 
Delpher_Gray ©   (2004-03-03 18:24) [37]

>бросай "сети"

У меня нет знаний в работе с ситемой, а именно с сообщениями, окнами и т.п...
Поэтому так трудно даётся.
На TServerSocket и TClientSocket я писал довольно сложные программки, работающие 100% ;))

Подскажи плиз.......
SetWindowLong(Wnd, GWL_WNDPROC, ???); - ошибка - это так объяснил... На самом деле знаю почему ошибка, потому что переменная ???, где у меня Serv.SockWND - должна быть типа Pointer а у меня процедура !
Как её преобразовать для SetWindowLong ?

В Classes это делается так:
SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));

Где Method - процедура, типа TWndMethod:
type
 TWndMethod = procedure(var Message: TMessage) of object;


 
Digitman ©   (2004-03-03 18:46) [38]


> Как её преобразовать для SetWindowLong ?
>
> В Classes это делается так:
> SetWindowLong(Result, GWL_WNDPROC, Longint(MakeObjectInstance(Method)));


ну та к посмотри, как это делается в Classes, и сделай по образу и подобию !!


 
Verg ©   (2004-03-03 19:11) [39]

1. С Classes не так уж и "распухает"
2. Тебе не обойтись без этого модуля для сервера, либо по-сути тебе его придется "воспроизвести" врукопашную.

TList, TStringList, TMemory и прочий Stream, Thread и т.д. - вещи функционально необходимые для серъезного серверного приложения.
Писать "самопальные"? - в конце концов у тебя получится тот же Classes....., тот же велосипед.

Я так считаю (не люблю писать ИМХО :)))


 
Delpher_Gray ©   (2004-03-03 19:40) [40]

>С Classes не так уж и "распухает"
Ага, как-же :)) Так прога весит 15.5 кб, и пашет, а с модулем: 150

>Тебе не обойтись без этого модуля для сервера, либо по-сути тебе его придется "воспроизвести" врукопашную.
Юзаю KOL и WinAPI :)

2Digitman:
Я нашёл решение, очени, правильно ли...
...

procedure SockMSG(HWnd, Msg, WParam, LParam: longint); stdcall;
begin
 if Msg = WM_SOCK then
   case LParam of
     FD_ACCEPT:
     begin
       MsgOk("FD_ACCEPT");
       ASock := accept(Sock, nil, nil);
       WSAAsyncSelect(ASock, Wnd, WM_SOCK, FD_READ);
       SendString("FD_ACCEPT", ASock);
     end;
     FD_READ:
     begin
       MsgOk("FD_READ");
       ioctlsocket(WParam, FIONREAD, Longint(OffSet));
       Received := recv(WParam, Buff[1], SizeOf(Buff), 0);
       MsgOk(Int2Str(Received)+" &#232;&#231; "+Int2Str(OffSet));
     end;
   end;
end;

...

SetWindowLong(Wnd, GWL_WNDPROC, Longint(@SockMSG));


Так вот :) Всё работает, но привильно ли всё сделано ??



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

Форум: "Сети";
Текущий архив: 2004.05.02;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.59 MB
Время: 0.033 c
14-1081401628
Dark Man
2004-04-08 09:20
2004.05.02
Продать софтину или просто показать


3-1081325950
Term
2004-04-07 12:19
2004.05.02
Трех звенные приожения, что я сделал не так.....???


3-1081258516
Julia_
2004-04-06 17:35
2004.05.02
Сообщение об ошибке


1-1082206330
Viktor
2004-04-17 16:52
2004.05.02
Проверить объект на существование


3-1081421582
Zapekank0
2004-04-08 14:53
2004.05.02
Подключение к MySQL в Delphi





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский