基于Delphi的SocketI/O模型全接觸

字號:

這和Socket模型非常類似。下面我就以老陳接收信件為例講解Socket I/O模型。
    一:select模型
    老陳非常想看到女兒的信。以至于他每隔10分鐘就下樓檢查信箱,看是否有女兒的信,在這種情況下,“下樓檢查信箱”然后回到樓上耽誤了老陳太多的時間,以至于老陳無法做其他工作。
    select模型和老陳的這種情況非常相似:周而復始地去檢查......如果有數(shù)據(jù)......接收/發(fā)送.......
    使用線程來select應(yīng)該是通用的做法:
    procedure TListenThread.Execute;
    var
    addr : TSockAddrIn;
    fd_read : TFDSet;
    timeout : TTimeVal;
    ASock,
    MainSock : TSocket;
    len, i : Integer;
    begin
    MainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    addr.sin_family := AF_INET;
    addr.sin_port := htons(5678);
    addr.sin_addr.S_addr := htonl(INADDR_ANY);
    bind( MainSock, @addr, sizeof(addr) );
    listen( MainSock, 5 );
    while (not Terminated) do
    begin
    FD_ZERO( fd_read );
    FD_SET( MainSock, fd_read );
    timeout.tv_sec := 0;
    timeout.tv_usec := 500;
    if select( 0, @fd_read, nil, nil, @timeout ) > 0 then //至少有1個等待Accept的connection
    begin
    if FD_ISSET( MainSock, fd_read ) then
    begin
    for i:=0 to fd_read.fd_count-1 do //注意,fd_count <= 64,也就是說select只能同時管理最多64個連接
    begin
     len := sizeof(addr);
     ASock := accept( MainSock, addr, len );
     if ASock <> INVALID_SOCKET then
    ....//為ASock創(chuàng)建一個新的線程,在新的線程中再不停地select
     end;
    end;   
    end;
    end; //while (not self.Terminated)
    shutdown( MainSock, SD_BOTH );
    closesocket( MainSock );
    end;
    二:WSAAsyncSelect模型
    后來,老陳使用了微軟公司的新式信箱。這種信箱非常先進,一旦信箱里有新的信件,蓋茨就會給老陳打電話:喂,大爺,你有新的信件了!從此,老陳再也不必頻繁上下樓檢查信箱了,牙也不疼了,你瞅準了,藍天......不是,微軟......
    微軟提供的WSAAsyncSelect模型就是這個意思。
    WSAAsyncSelect模型是Windows下最簡單易用的一種Socket I/O模型。使用這種模型時,Windows會把網(wǎng)絡(luò)事件以消息的形勢通知應(yīng)用程序。
    首先定義一個消息標示常量:
    const WM_SOCKET = WM_USER + 55;
    再在主Form的private域添加一個處理此消息的函數(shù)聲明: private
    procedure WMSocket(var Msg: TMessage); message WM_SOCKET;
    然后就可以使用WSAAsyncSelect了: var
    addr : TSockAddr;
    sock : TSocket;
    sock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
    addr.sin_family := AF_INET;
    addr.sin_port := htons(5678);
    addr.sin_addr.S_addr := htonl(INADDR_ANY);
    bind( m_sock, @addr, sizeof(SOCKADDR) );
    WSAAsyncSelect( m_sock, Handle, WM_SOCKET, FD_ACCEPT or FD_CLOSE );
    listen( m_sock, 5 );
    ....
    應(yīng)用程序可以對收到WM_SOCKET消息進行分析,判斷是哪一個socket產(chǎn)生了網(wǎng)絡(luò)事件以及事件類型: procedure TfmMain.WMSocket(var Msg: TMessage);
    var
    sock : TSocket;
    addr : TSockAddrIn;
    addrlen : Integer;
    buf : Array [0..4095] of Char;
    begin
    //Msg的WParam是產(chǎn)生了網(wǎng)絡(luò)事件的socket句柄,LParam則包含了事件類型
    case WSAGetSelectEvent( Msg.LParam ) of
    FD_ACCEPT :
    begin
    addrlen := sizeof(addr);
    sock := accept( Msg.WParam, addr, addrlen );
    if sock <> INVALID_SOCKET then
     WSAAsyncSelect( sock, Handle, WM_SOCKET, FD_READ or FD_WRITE or FD_CLOSE );
    end;
    FD_CLOSE : closesocket( Msg.WParam );
    FD_READ : recv( Msg.WParam, buf[0], 4096, 0 );
    FD_WRITE : ;
    end;
    end;