안녕하세요..한델 회원 여러분...
게시판에 글 올리기는 정말 첨 이네여...
오랫동안 델파이를 안 쓰다가 오랜만에 쓸려구 하니 여러가지 어려움이 있네여..
다름이 아니라 제가 UNIX 에서 UDP 통신용 프로그램을 만들었는데 이것을
Delphi 로 다시 만들라구 그랬더랍니다. 근데 너무 오랜만(전 델1.0부터 3.0)
까정만 써 봐서리)이라 문법두 아리아리 하구 버전도 6로 업 되있더군여..
어렵사리 후배한넘 닥달해서 Delphi 5 를 구해서 설치 하구선 이런 저런 기능을 살펴보던중 놀랍게도 fastNet 이란 탭에서 UDP 용 컴포넌트를 발견 했죠..
근데 이거 어떻게 사용하는 지 영 몰겠더군여..
UNIX 에서 코딩할땐 흔히 하는 SOCKET 만들고 BIND 하구 옵션주구 어쩌구
저쩌구 하는데..특히 멀티 캐스팅(제가필요로하는건 멀티캐스팅용 UDP라) 옵션및 기타등등을 설정하는데
이 컴포넌튼 단순히 IP 와 PORT NO만 수정하게 되 있더군요그래서 소스를 볼까했는데 .pas 로 등록도 안된것 같구..winapi를 쓸까두 생각했는데
좋은 생각 같지가 않군여..혹 이 컴포넌트로 멀티캐스팅 하게 쓰는 방법아시는
분은 꼭 좀 갈켜 주세여...
아래는 참고 소스 코드 임다...
-----------------------------------------------------------------
unit ListenThrd;
interface
uses
Classes, ComCtrls, StdCtrls, Windows, Winsock2;
const
BufferSize = MAXGETHOSTSTRUCT;
type
TMCOptions = record
Port : Integer;
Address : String;
UseWS2 : Boolean;
end;
TConnData = record
NumEvents : DWORD;
EventArray : array[1..WSA_MAXIMUM_WAIT_EVENTS] of WSAEvent;
end;
TMulticast = record
imr_multiaddr : TInAddr; // IP multicast address of group */
imr_interface : TInAddr; // local IP address of interface */
end;
TListenThrd = class(TThread)
private
{ Private declarations }
FRunning : Boolean;
protected
MsgNo : Integer;
EventMsg : WSAEVENT;
lpNetworkEvents : PWSANETWORKEVENTS;
ConnData : TConnData;
Buffers : array[0..BufferSize - 1] of char;
Msg,
StatusMsg : String;
Done : Boolean;
ReUseFlag : BOOL;
sktBlast : TSocket;
Multicast : TMulticast;
MCOptions : TMCOptions;
LocalAddr,
RemoteAddr : TSockAddr;
Started : Boolean;
wsaData : TWSADATA;
function Start : Boolean;
procedure DisplayMsg;
procedure DisplayStatusMsg;
procedure ChangeWS;
procedure ChangeBtnState;
procedure CleanUp(Sender : TObject);
procedure HandleSocketEvent;
procedure GetMsg;
function JoinSession : Boolean;
procedure LeaveSession;
procedure Execute; override;
public
property Running : Boolean read FRunning default FALSE;
procedure CloseSession;
constructor Create(Options : TMCOptions);
end;
var
thrdListen : TListenThrd;
Options : TMCOptions;
implementation
{ TListenThrd }
Uses
Dialogs, Forms, Main, SysUtils;
var
AddrStrSize : Integer;
function TListenThrd.Start : Boolean;
var
VerReqd : WordRec;
begin
with VerReqd do
begin
Hi := 2;
Lo := 2;
end;
Result := WSAStartUp(Word(VerReqd), wsaData) = 0;
end;
procedure TListenThrd.DisplayMsg;
begin
frmMain.memMsg.Lines.Add(Msg);
end;
procedure TListenThrd.DisplayStatusMsg;
begin
frmMain.memStatusMsg.Lines.Add(StatusMsg);
end;
procedure TListenThrd.ChangeWS;
begin
MessageBeep(0);
with frmMain do
WindowState := wsNormal;
end;
procedure TListenThrd.ChangeBtnState;
begin
frmMain.bbtnStop.Enabled := not frmMain.bbtnStop.Enabled;
frmMain.bbtnStart.Enabled := not frmMain.bbtnStart.Enabled;
end;
procedure TListenThrd.CleanUp(Sender : TObject);
begin
if Started then
begin
closesocket(sktBlast);
WSACleanUp;
end;
end;
function TListenThrd.JoinSession : Boolean;
var
sktRes : Integer;
sktTemp : TSocket;
begin
Result := TRUE;
case MCOptions.UseWS2 of
TRUE : begin
sktTemp := WSAJoinLeaf(sktBlast, @RemoteAddr, SizeOf(RemoteAddr), NIL, NIL, NIL, NIL, JL_RECEIVER_ONLY);
if sktTemp = INVALID_SOCKET then
begin
StatusMsg := Concat('Call to WSAJoinLeaf failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Result := FALSE;
Synchronize(ChangeBtnState);
Exit;
end;
end;
FALSE : begin
with Multicast do
begin
imr_multiaddr.s_addr := inet_addr(PChar(MCOptions.Address));
imr_interface.s_addr := INADDR_ANY;
end;
sktRes := setsockopt(sktBlast,IPPROTO_IP, IP_ADD_MEMBERSHIP, pchar(@Multicast), SizeOf(Multicast));
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to setsockopt failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Result := FALSE;
end;
end;
end;// case
end;
procedure TListenThrd.LeaveSession;
var
sktRes : Integer;
begin
if not UseWS2 then
with Multicast do
begin
imr_multiaddr.s_addr := inet_addr(MultiCastAddr);
imr_interface.s_addr := INADDR_ANY;
end;
sktRes := setsockopt(sktBlast, IPPROTO_IP, IP_DROP_MEMBERSHIP, pchar(@Multicast), SizeOf(Multicast));
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to setsockopt failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
end;
end;
procedure TListenThrd.CloseSession;
begin
// Leave session ...
LeaveSession;
// Terminate the thread by setting the global Done and call
Done := TRUE;
// Call WSASetEvent to signal HandleSocketEvent
WSASetEvent(EventMsg);
end;
constructor TListenThrd.Create(Options : TMCOptions);
var
sktRes : Integer;
begin
inherited Create(TRUE);
MCOptions := Options;
FreeOnTerminate := TRUE;
Started := Start;
if not Started then
begin
StatusMsg := 'Cannot load Winsock 2.0!';
Synchronize(DisplayStatusMsg);
Exit;
end;
OnTerminate := CleanUp;
MsgNo := 0;
with MCOptions do
begin
// get a UDP socket
if UseWS2 then
begin
// Convert address string to a value
RemoteAddr.sin_family := AF_INET;
AddrStrSize := 16;
sktRes := WSAStringToAddress(Pchar(MultiCastAddr),AF_INET, NIL, @RemoteAddr, @AddrStrSize);
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to WSAStringToAddress failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Exit;
end;
sktBlast := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NIL, 0, WSA_FLAG_OVERLAPPED or
WSA_FLAG_MULTIPOINT_C_LEAF or
WSA_FLAG_MULTIPOINT_D_LEAF);
end
else
sktBlast := socket(AF_INET, SOCK_DGRAM, 0);
if sktBlast = SOCKET_ERROR then
begin
StatusMsg := Concat('Failed to create UDP socket! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Exit;
end;
// Create an event ...
EventMsg := CreateEvent(NIL, FALSE, FALSE, NIL);
if EventMsg = WSA_INVALID_EVENT then
begin
StatusMsg := Concat('Failed to create Message Event. Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Done := TRUE;
Exit;
end;
// Now set up notification ...
sktRes := WSAEventSelect(sktBlast, EventMsg, FD_READ);
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to WSAEventSelect failed for socket ', IntToStr(sktBlast),'. Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
closesocket(sktBlast);
Done := TRUE;
Exit;
end;
// Set up socket options for multicast ...
// Always reuse socket
ReUseFlag := TRUE;
sktRes := setsockopt(sktBlast, SOL_SOCKET, SO_REUSEADDR, @ReUseFlag,
SizeOf(ReUseFlag));
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to setsockopt failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
closesocket(sktBlast);
Synchronize(ChangeWS);
Exit;
end;
// Name the socket and assign the local port number to receive on
with LocalAddr do
begin
sin_family := AF_INET;
sin_addr.s_addr := htonl(INADDR_ANY);
sin_port := htons(MCOptions.Port);
end;
// bind it!
sktRes := bind(sktBlast, LocalAddr, SizeOf(TSockAddrIn));
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to bind failed ! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
closesocket(sktBlast);
Exit;
end;
if UseWS2 then
begin
// swap host to network order ...
with RemoteAddr do
begin
sin_family := PF_INET;
sktRes := WSAHtons(sktBlast, MCOptions.Port, @sin_port);
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to WSAHtons failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
closesocket(sktBlast);
Exit;
end;
end;
end;
// Join the multicast group
if not JoinSession then
begin
closesocket(sktBlast);
Exit;
end;
StatusMsg := 'Started listening thread ...';
Synchronize(DisplayStatusMsg);
end;// with MCOptions
Resume;
end;
procedure TListenThrd.HandleSocketEvent;
var
sktRes : Integer;
begin
sktRes := WSAEnumNetworkEvents(sktBlast, EventMsg, @Buffers[0]);
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to WSAEnumNetworkEvents failed. Error ',IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Done := TRUE;
Exit;
end;
lpNetworkEvents := PWSANETWORKEVENTS(@Buffers[0]);
// Decipher network events ...
with lpNetworkevents^ do
begin
// Is this a FD_READ event?
if (lNetworkEvents and FD_READ) = FD_READ then
begin
if iErrorCode[1] = WSAENETDOWN then
begin
StatusMsg := Concat('Network down ... Error ',IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
end else
begin
GetMsg;
end;
end;
end;
end;
procedure TListenThrd.GetMsg;
var
Error: Integer;
Buff : PWSABUF;
Flags, NoBytes,
Size,
sktRes,
AddrStrSize : Integer;
AddrStr : array[0..MAXGETHOSTSTRUCT - 1] of char;
PortNo : shortint;
begin
// Get the message ...
Buff := NIL;
Flags := 0;
AddrStrSize := MAXGETHOSTSTRUCT;
try
Buff := AllocMem(SizeOf(Buffers));
Buff.Buf := Buffers;
Buff.len := SizeOf(Buffers);
Size := SizeOf(RemoteAddr);
sktRes := WSARecvFrom(sktBlast, Buff, 1, @NoBytes, @Flags, @RemoteAddr, @Size, NIL, NIL);
if sktRes = SOCKET_ERROR then
begin
Error := WSAGetLastError;
if Error <> WSAEWOULDBLOCK then
begin
StatusMsg := Concat('Call to WSARecvFrom failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Done := TRUE;
WSASetEvent(EventMsg);
end;
end else
begin
Synchronize(ChangeWS);
Msg := String(Buff.buf);
Synchronize(DisplayMsg);
// Convert address to a readable string ...
PortNo := 0;
sktRes := WSAAddressToString(@RemoteAddr, SizeOf(RemoteAddr), NIL, AddrStr, @AddrStrSize);
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to WSAAddressToString failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
end;
// Convert from network to host byte order
sktRes := WSANtohs(sktBlast,RemoteAddr.sin_port, @PortNo);
if sktRes = SOCKET_ERROR then
begin
StatusMsg := Concat('Call to WSANtohs failed! Error ', IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
end else
begin
StatusMsg := Concat('Message #', IntToStr(MsgNo), ' received from ', String(AddrStr));
Synchronize(DisplayStatusMsg);
end;
end;
finally
FreeMem(Buff);
end;
end;
procedure TListenThrd.Execute;
var
WaitStatus : DWORD;
begin
with ConnData do
begin
NumEvents := 1;
EventArray[1] := EventMsg;
Done := FALSE;
repeat
WaitStatus := WSAWaitForMultipleEvents(NumEvents, @EventArray, FALSE, WSA_INFINITE, FALSE);
case WaitStatus of
WSA_WAIT_FAILED : begin
StatusMsg := Concat('WSA_WAIT_FAILED ... Error ',IntToStr(WSAGetLastError));
Synchronize(DisplayStatusMsg);
Done := TRUE;
end;
WAIT_IO_COMPLETION : begin
StatusMsg := 'WAIT_IO_COMPLETION ...';
Synchronize(DisplayStatusMsg);
end;
WSA_WAIT_EVENT_0 : begin
StatusMsg := 'WSA_WAIT_EVENT_0 ...';
Synchronize(DisplayStatusMsg);
if Done then
begin// then we are done ...
StatusMsg := 'Listening thread terminated ...';
Synchronize(DisplayStatusMsg);
end else
begin
Inc(MsgNo);
HandleSocketEvent;
end;
end;
end;// case
until Done;
end;
end;
end.