• (转帖)组播实现


    组播使用在internet上面并不是一个好办法,很多的路由器是不支持组播的(以前华为的路由器也不支持,现在不知道),就算现在有路由器支持,你也没有办法保证你的包所经过的路由都是支持组播协议的.
    我觉得在WINDOWS下面还是使用TCP(或者UDP的PTOP)的IOCP模型的通用性好一些.至少不用担心你说的问题.
    下面的是一段组播程序:
    unit UdpSocket;

    interface

    uses
     Classes, SysUtils, WinSock, Windows;

    const
     DEFAULTBUFFERSIZE = 16384;
     MAXBUFFERSIZE = 63488;
     MULTICAST_TTL = 10;

    type
     PIP_mreq = ^TIP_mreq;
     TIP_mreq = record
        imr_multiaddr  : in_addr;
        imr_interface  : in_addr;
     end;

     ESocketError = class(Exception);

     TSockSytle = (MultCastSend, MultCastRecv);

     TUdpRecv = procedure(var Buf; Len: Integer;
       FromIP: string; FromPort: u_Short) of object;

     TUcpRecvThd = class(TThread)
     private
       FSocket     : TSocket;
       FBufSize    : Integer;
       FOnUdpRecv  : TUdpRecv;
     protected
       procedure Execute; override;
     end;

     TUcpSocket = class(TObject)
     private
       class procedure StartSocket();
       class procedure StopSocket();
     private
       FOnUdpRecv  : TUdpRecv;
       FLocalAddr  : String;
       FPort       : u_Short;
       FSocket     : TSocket;
       FAddrTo     : TSockAddr;
       FStyle      : TSockSytle;
       FBufSize    : Integer;
       FRemoteAddr : String;
       FMCReq      : TIP_mreq;
       FUcpRecvThd : TUcpRecvThd;
     private
       procedure SetLocalAddr(Value: String);
       procedure SetPort(Value: u_Short);
       procedure SetSytle(Value: TSockSytle);
       procedure SetBufSize(Value: Integer);
       procedure SetRemoteAddr(Value: String);
     public
       function Send(var Buf; Len: Integer): Boolean;
       procedure Busk();
     published
       property LocalAddr: String read FLocalAddr write SetLocalAddr;
       property Port: u_Short read FPort write SetPort;
       property Style: TSockSytle write SetSytle;
       property BufSize: Integer read FBufSize write SetBufSize;
       property RemoteAddr: String read FRemoteAddr write SetRemoteAddr;
       property OnUdpRecv: TUdpRecv read FOnUdpRecv write FOnUdpRecv;
     public
       constructor Create();
       destructor Destroy; override;
     end;

    implementation

    { TUcpSocket }

    procedure TUcpSocket.Busk;
    var
     pPE   : PProtoEnt;
     Sock  : TSocket;
     SockAddrLocal, SockAddrRemote : TSockAddr;
     nTTL, nReuseAddr : integer;
     MCReq : TIP_mreq;
    begin
     pPE := GetProtoByName('UDP');

     Sock := Socket(AF_INET, SOCK_DGRAM, pPE.p_proto);
     if Sock = INVALID_SOCKET then
       raise ESocketError.Create('创建Socket失败!');

     nReuseAddr := 1;
     if SetSockOpt(Sock, SOL_SOCKET, SO_REUSEADDR, @nReuseAddr, SizeOf(integer)) = SOCKET_ERROR then
     begin
       CloseSocket(Sock);
       Exit;
     end;

     FillChar(SockAddrLocal, SizeOf(SockAddrLocal), 0);
     SockAddrLocal.sin_family := AF_INET;
     if FStyle = MultCastSend then
       SockAddrLocal.sin_port := htons(0)
     else
       SockAddrLocal.sin_port := htons(Port);
     SockAddrLocal.sin_addr.S_addr := Inet_Addr(PChar(FLocalAddr));
     if Bind(Sock, SockAddrLocal, SizeOf(SockAddrLocal)) = SOCKET_ERROR then
     begin
       CloseSocket(Sock);
       Exit;
     end;

     if FStyle = MultCastSend then
     begin
       //设置发送缓冲大小
       if SetSockOpt(Sock, SOL_SOCKET, SO_SNDBUF,
         @FBufSize, SizeOf(Integer)) = SOCKET_ERROR then
       begin
         CloseSocket(Sock);
         Exit;
       end;

       //设置发送时的参数
       if SetSockOpt(Sock, IPPROTO_IP, IP_MULTICAST_IF, @(SockAddrLocal.sin_addr),
                     SizeOf(In_Addr)) = SOCKET_ERROR then
       begin
         CloseSocket(Sock);
         Exit;
       end;
       nTTL := MULTICAST_TTL;
       if SetSockOpt(Sock, IPPROTO_IP, IP_MULTICAST_TTL, @nTTL, SizeOf(integer)) = SOCKET_ERROR then
       begin
         CloseSocket(Sock);
         Exit;
       end;

       FillChar(SockAddrRemote, SizeOf(SockAddrRemote), 0);
       SockAddrRemote.sin_family := AF_INET;
       SockAddrRemote.sin_port := htons(Port);

       SockAddrRemote.sin_addr.S_addr := Inet_Addr(PChar(FRemoteAddr));

       FAddrTo := SockAddrRemote;
     end else //接收
     begin
       //设置接收缓冲大小
       if SetSockOpt(Sock, SOL_SOCKET, SO_RCVBUF, @fBufSize, SizeOf(integer)) = SOCKET_ERROR then
       begin
         CloseSocket(Sock);
         Exit;
       end;

       //加入组
       MCReq.imr_multiaddr.S_addr := Inet_Addr(PChar(FRemoteAddr));
       MCReq.imr_interface.S_addr := Inet_Addr(PChar(FLocalAddr));
       if SetSockOpt(Sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, @MCReq,
         SizeOf(TIP_mreq)) = SOCKET_ERROR then
       begin
         CloseSocket(Sock);
         Exit;
       end;

       fMCReq := MCReq;
     end;

     FSocket := Sock;

     if FStyle = MultCastRecv then
     begin
       FUcpRecvThd.FSocket := FSocket;
       FUcpRecvThd.FBufSize := FBufSize;
       FUcpRecvThd.FOnUdpRecv := FOnUdpRecv;
       FUcpRecvThd.Resume;
     end;
    end;

    constructor TUcpSocket.Create;
    begin
     FOnUdpRecv  := nil;
     FLocalAddr  := '127.0.0.1';
     FPort       := 0;
     FStyle      := MultCastRecv;
     FBufSize    := DEFAULTBUFFERSIZE;
     FUcpRecvThd := TUcpRecvThd.Create(true);
    end;

    destructor TUcpSocket.Destroy;
    begin
     CloseSocket(FSocket);
     FUcpRecvThd.Free;
     inherited;
    end;

    function TUcpSocket.Send(var Buf; Len: Integer): Boolean;
    begin
     Result := false;
     if SendTo(FSocket, Buf, Len, MSG_DONTROUTE, FAddrTo,
               SizeOf(FAddrTo)) <> SOCKET_ERROR then
       Result := true;
    end;

    procedure TUcpSocket.SetLocalAddr(Value: String);
    begin
     FLocalAddr := Value;
    end;

    procedure TUcpSocket.SetBufSize(Value: Integer);
    begin
     FBufSize := Value;
    end;

    procedure TUcpSocket.SetPort(Value: u_Short);
    begin
     FPort := Value;
    end;

    procedure TUcpSocket.SetRemoteAddr(Value: String);
    var
     nMCAddr : Cardinal;
    begin
     FRemoteAddr := Value;
     nMCAddr := ntohl(inet_addr(PChar(FRemoteAddr)));
     if not ((nMCAddr <= $efffffff) and (nMCAddr >= $e0000100)) then
       raise ESocketError.Create('无效的组播地址!');
    end;

    procedure TUcpSocket.SetSytle(Value: TSockSytle);
    begin
     FStyle := Value;
    end;

    class procedure TUcpSocket.StartSocket;
    var
     WsData:  TWSAData;
     err: Integer;
    begin
     err := WSAStartup(MAKEWORD(2, 2), WsData);
     if err <> 0 then
       raise ESocketError.Create('不能使用SOCKET服务!');

     if ( LOBYTE( WsData.wVersion ) <> 2 ) or
           ( HIBYTE( WsData.wVersion ) <> 2 ) then
       raise ESocketError.Create('没有找到所需要的SOCKET版本!');
    end;

    class procedure TUcpSocket.StopSocket;
    begin
     WSACleanup;
    end;

    { TUcpRecvThd }

    procedure TUcpRecvThd.Execute;
    var
     readFDs : TFDSet;
     nRecved, nAddrLen: integer;
     Buf : array [0..MAXBUFFERSIZE] of Byte;
     SockFrom : TSockAddr;
    begin
     Priority := tpHighest;
     while not Terminated do
     begin
       nAddrLen := SizeOf(SockFrom);
       FD_ZERO(readFDs);
       FD_SET(FSocket, readFDs);

       Select(0, @readFDs, nil, nil, nil);

       if FD_ISSET(FSocket, readFDs) then
       begin
         nRecved := RecvFrom(FSocket, buf, FBufSize, 0, SockFrom, nAddrLen);

         if Assigned(FOnUdpRecv) then
           FOnUdpRecv(Buf, nRecved, string(Inet_Ntoa(SockFrom.sin_addr)),
                   Cardinal(Ntohs(SockFrom.sin_port)));
       end;
     end;
    end;

    initialization
     TUcpSocket.StartSocket;
    finalization
     TUcpSocket.StopSocket;

    end.
    调用如下:

    unit Demo;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, StdCtrls, UdpSocket, WinSock;

    const
     MULTCASTADDR: String = '225.0.1.177';
     MULTCASTPORT: Integer = 10000;

    type
     TUdpSocketDemo = class(TForm)
       edtSendText: TEdit;
       meoRecvText: TMemo;
       cmdSend: TButton;
       cmdInit: TButton;
       cmdExit: TButton;
       procedure FormClose(Sender: TObject; var Action: TCloseAction);
       procedure cmdExitClick(Sender: TObject);
       procedure cmdSendClick(Sender: TObject);
       procedure cmdInitClick(Sender: TObject);
     private
       { Private declarations }
       FMultCastUdpSend: TUcpSocket;      //Send Socket
       FMultCastUdpRecv: TUcpSocket;      //Recv Socket
     public
       { Public declarations }

       procedure OnUdpRecv(var Buf; Len: Integer;
         FromIP: string; FromPort: u_Short);
     end;

    var
     UdpSocketDemo: TUdpSocketDemo;

    implementation

    {$R *.dfm}

    procedure TUdpSocketDemo.cmdInitClick(Sender: TObject);
    begin
     FMultCastUdpSend := TUcpSocket.Create;
     FMultCastUdpSend.LocalAddr := '172.18.2.111';
     FMultCastUdpSend.Port := MULTCASTPORT;
     FMultCastUdpSend.Style := MultCastSend;
     FMultCastUdpSend.RemoteAddr := MULTCASTADDR;
     FMultCastUdpSend.Busk;

     FMultCastUdpRecv := TUcpSocket.Create;
     FMultCastUdpRecv.LocalAddr := '172.18.2.111';
     FMultCastUdpRecv.Port := MULTCASTPORT;
     FMultCastUdpRecv.Style := MultCastRecv;
     FMultCastUdpRecv.RemoteAddr := MULTCASTADDR;
     FMultCastUdpRecv.OnUdpRecv := OnUdpRecv;
     FMultCastUdpRecv.Busk;
     
     cmdInit.Enabled := false;
    end;

    procedure TUdpSocketDemo.cmdSendClick(Sender: TObject);
    var
     Buf: array of Char;
     Len: Integer;
    begin
     Len := Length(edtSendText.Text);
     SetLength(Buf, Len);
     StrPCopy(@Buf[0], edtSendText.Text);

     FMultCastUdpSend.Send(Buf, Len);
    end;

    procedure TUdpSocketDemo.cmdExitClick(Sender: TObject);
    begin
     Close;
    end;

    procedure TUdpSocketDemo.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
     FMultCastUdpSend.Free;
    end;

    procedure TUdpSocketDemo.OnUdpRecv(var Buf; Len: Integer; FromIP: string;
     FromPort: u_Short);
    begin
     meoRecvText.Lines.Add(String(Buf));
    end;

    end.
  • 相关阅读:
    假期每日小结_2.2
    假期每日小结_2.1
    《新浪微博用户兴趣建模系统架构》阅读笔记
    《微博深度学习平台架构和实践》阅读笔记
    《亿级用户下的新浪微博平台架构》阅读笔记
    JavaScript中JSON的序列化和解析
    Servlet中@WebServlet("XXXX")注解无效,访问servlet报404错误
    数据卷(Data Volumes)
    Docker安装及基本命令
    springcloud服务配置中心
  • 原文地址:https://www.cnblogs.com/chengxin1982/p/1626139.html
Copyright © 2020-2023  润新知