1. 程式人生 > >多程序服務端實現-共享socket

多程序服務端實現-共享socket

眾所周知,使用多程序的服務端模型有利於程式的健壯性。傳統的做法是主程序負責收發資料,然後傳給子程序來處理。這種做法的缺陷是需要大量的父子程序IPC,對效率來說是一種損失。

這裡,我提出另外一種比較獨特的做法,就是多個程序share socket,每次程序都可以accept,然後來自己處理。

幾個關鍵點:
1) CreateProcess使用InheritHandle標記來share socket handle
2) 通過command line直接向子程序來傳遞父socket的值
3)使用Global Mutext來實現子程序互斥的accept

可以改進的地方
1) 使用動態程序池來程式具有更大的伸縮性
2)監控子程序的狀態,處理僵死程序

多程序共享一個socket,急啊!
在網路遊戲程式設計中,假如客戶端提供房間讓玩家登入遊戲,比如說四國軍旗,進入遊戲後會產生一個新的程序,我想問:客戶端程序和四國軍旗遊戲程序通過wsaduplicatesocket共享一個socket和伺服器端進行通訊,某個時間只能是一個程序利用這個socket和服務端通訊,怎麼來協調這個socket,使客戶端程序和四國軍旗遊戲程序很好的交替和伺服器很好的通訊?想了很久,查了一些資料,但不知道怎麼來做?急啊!如果可以提供一部分程式碼,那非常感激!這是我所有的分了,各位幫幫忙!

------解決方案--------------------------------------------------------


共享原來socket是unix上邊的概念,unix下邊的早期網路伺服器一般都是由父程序
bind
listen
accept

然後子程序read,write,這兩個程序共享socket,但是隻有一個程序讀寫。包括unix下邊現在的inetd程序也是這樣的。

另外有很多大型伺服器也是這樣的架構,有點有很多了

不可以簡單的將socket的值利用。應該通過DuplicateHandle進行。

程序1:
  WSAStartup();
  s1 = socket();
  connect();

程序2:
  WSAStartup();
  hh = OpenProcess(p1Id);
  DuplicateHandle(hh, s1, -1, &s2, PROCESS_ALL_ACCESS, FALSE, DUPLICATE_SAME_ACCESS);
  connect(s2);
  ...
注意:
  程序2中的s1和p1Id為程序1中的s1值和程序1的程序ID.


修改,程序2中可以直接利用程序1的socket進行send或recv。不用重新connect。
程序2:
  WSAStartup();
  hh = OpenProcess(p1Id);
  DuplicateHandle(hh, s1, -1, &s2, PROCESS_ALL_ACCESS, FALSE, DUPLICATE_SAME_ACCESS);
  send(s2);

下面是一個echo server 的例子來展示這項技術, FYI

父程序(SSParent.cpp)

#include <stdio.h>
#include 
<winsock2.h
>
#include 
<windows.h>
#include 
<process.h>#define MUTEX_NAME "sschild"int main(int argc, char* argv[])
{
    { 
//init        WORD wVersionRequested;
        WSADATA wsaData;
        wVersionRequested 
= MAKEWORD( 22 );
        WSAStartup( wVersionRequested, 
&wsaData );
    }

    SOCKET s 
= socket(AF_INET,SOCK_STREAM,0);
    
if(s==INVALID_SOCKET)
    {
        printf(
"create socket failed!\n");
        
return-1;
    }

    { 
//bind&listen        sockaddr_in sa;
        sa.sin_family 
= AF_INET;
        sa.sin_port 
= htons( 1500 );
        sa.sin_addr.s_addr 
=0 ; 
        
int rc = bind(s,(sockaddr *)&sa,sizeof(sa));
        
if(rc == SOCKET_ERROR)
        {
            printf(
"bind failed:%d\n",::WSAGetLastError());
            
return-1;
        }
        listen(s,SOMAXCONN);
    }

    HANDLE hSocketMutex;
    { 
//create mutex        hSocketMutex = ::CreateMutex(NULL,FALSE,MUTEX_NAME);
        
if(hSocketMutex==NULL)
        {
            printf(
"fail CreateMutex:%d\n",::GetLastError());
            
return-1;
        }
    }

    
constint CHILD_NUMBER =5;
    HANDLE hProcess[CHILD_NUMBER];
    { 
//create child process       STARTUPINFO si = { sizeof(si) };
       PROCESS_INFORMATION piProcess[CHILD_NUMBER];
       
char pCmdLine[256];
       sprintf(pCmdLine,
"SSChild %d",s);
       
for(int i=0;i<CHILD_NUMBER;++i)
       {
           
if(!CreateProcess(NULL,pCmdLine,NULL,NULL,TRUE,0, NULL, NULL, &si, &piProcess[i]))
           {
               printf(
"fail CreateProcess:%d\n",::GetLastError());
               
return-1;
           }
           hProcess[i] 
= piProcess[i].hProcess;
           CloseHandle(piProcess[i].hThread);
       }
    }

    ::WaitForMultipleObjects(CHILD_NUMBER,hProcess,TRUE,INFINITE);

    {
//close all child handlefor(int i=0;i<CHILD_NUMBER;++i)
       {
           CloseHandle(hProcess[i]);
       }
    }

    
//clean    CloseHandle(hSocketMutex);
    closesocket(s);
    WSACleanup( );
    
return0;
}

子程序(SSChild.cpp)

#include <stdio.h>
#include 
<winsock2.h>
#include 
<windows.h>
#include 
<process.h>#define MUTEX_NAME "sschild"int main(int argc, char* argv[])
{
    printf(
"sschild startup!\n");

    { 
//init        WORD wVersionRequested;
        WSADATA wsaData;
        wVersionRequested 
= MAKEWORD( 22 );
        WSAStartup( wVersionRequested, 
&wsaData );
    }

    DWORD pid 
= ::GetCurrentProcessId();

    HANDLE hSocketMutex;
    { 
//open mutex        hSocketMutex = ::OpenMutex(MUTEX_ALL_ACCESS,FALSE,MUTEX_NAME);
        
if(hSocketMutex==NULL)
        {
            printf(
"fail OpenMutex:%d\n",::GetLastError());
            
return-1;
        }
    }

    SOCKET s;
    {  
//get socket handle from cmdlineif(argc<=1)
        {
            printf(
"usage: sschild socket_handle\n");
            
return-1;
        }
        s 
= (SOCKET) atoi(argv[1]);
    }

    
while(1)
    {
        WaitForSingleObject(hSocketMutex,INFINITE);
        sockaddr_in sa;
        
int add_len =sizeof(sa);
        SOCKET c 
= accept(s,(sockaddr*)&sa,&add_len);
        ReleaseMutex(hSocketMutex);
        
if(c!=INVALID_SOCKET)
        {
            printf(
"[%d],client:%s port:%d connected!\n",pid,inet_ntoa(sa.sin_addr),sa.sin_port);
            
while(1)
            {
                
char buffer[256]={0};
                
int rc= recv(c,buffer,255,0);
                
if(rc>0)
                {
                    printf(
"[%d]recv msg:%s\n",pid,buffer);
                    send(c,buffer,strlen(buffer)
+1,0);
                }
                
elseif(rc == SOCKET_ERROR)
                {
                    printf(
"[%d]recv msg failed:%d\n",pid,::WSAGetLastError());
                    closesocket(c);
                    
break;
                }
                
else
                {
                    printf(
"[%d]connection close\n",pid);
                    closesocket(c);
                    
break;
                }
            }
        }
        
else
        {
            printf(
"[%d]fail accept:%d\n",pid,::WSAGetLastError());
        }
    }

    CloseHandle(hSocketMutex);
    
return0;
}

多程序方式程式設計簡單,程式健壯性相對比較好,但是切換開銷比較大。