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

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.024 c
14-1081509057
Феликс
2004-04-09 15:10
2004.05.02
Горе тебе народ побежденный...


3-1080986096
Хэтч
2004-04-03 13:54
2004.05.02
Можно ли "привязать" Image к строке в Table ?


4-1078926004
nuflin
2004-03-10 16:40
2004.05.02
Приведите пример с использовнием функции VirtualQueryEx() ///////


3-1081148071
Алексей
2004-04-05 10:54
2004.05.02
EHLib Grid


1-1082046790
Knight
2004-04-15 20:33
2004.05.02
Динамический массив в компоненте...