小明思考

高性能服务器端计算
posts - 70, comments - 428, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

多进程服务端实现-共享socket

Posted on 2008-03-10 14:09 小明 阅读(11021) 评论(3)  编辑 收藏 引用 所属分类: Win32Network/ACE


众所周知,使用多进程的服务端模型有利于程序的健壮性。传统的做法是主进程负责收发数据,然后传给子进程来处理。这种做法的缺陷是需要大量的父子进程IPC,对效率来说是一种损失。

这里,我提出另外一种比较独特的做法,就是多个进程share socket,每次进程都可以accept,然后来自己处理。

几个关键点:
1) CreateProcess使用InheritHandle标记来share socket handle
2) 通过command line直接向子进程来传递父socket的值
3)使用Global Mutext来实现子进程互斥的accept

可以改进的地方
1) 使用动态进程池来程序具有更大的伸缩性
2)监控子进程的状态,处理僵死进程

下面是一个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;
        }
    }

    
const int 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 handle
       for(int i=0;i<CHILD_NUMBER;++i)
       {
           CloseHandle(hProcess[i]);
       }
    }

    
//clean
    CloseHandle(hSocketMutex);
    closesocket(s);
    WSACleanup( );
    
return 0;
}

 

子进程(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 cmdline
        if(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);
                }
                
else if(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);
    
return 0;
}

Feedback

# re: 多进程服务端实现-共享socket[未登录]  回复  更多评论   

2008-03-10 14:42 by cppexplore
“这里,我提出另外一种比较独特的做法,就是......”
呵呵,兄弟啊,我毕业答辩的时候,老师就反复的批评我们,“我提出.......”,“我发明......”之类的东西。
文中就是《unix网络编程》中的预派生进程阻塞在accept的方式嘛。
并且书中说明这种问题有惊群问题,可以前面加文件锁或者线程锁互斥,你文中加的是互斥锁。现在的linux从2.2.9版本起就不存在惊群问题而不需要加锁了,更好的是2.6内核的线程库中线程锁不陷入互斥状态的话就不会陷入内核态了,加不加性能一样。而win就没有这么好的线程锁。
多进程方式编程简单,程序健壮性相对比较好,但是切换开销比较大。现在的更倾向于预派生线程的方式。
另可以,起多个多线程的程序,bind不同port,前端部署lvs提供均衡负载一样可以达到更好的多进程效果。

# re: 多进程服务端实现-共享socket  回复  更多评论   

2008-07-06 02:12 by 放屁啊狗
windows的socket只能说是个小玩艺儿,跟unix下的socket简直无法比拟,可见windows只能做desktop系统

# re: 多进程服务端实现-共享socket[未登录]  回复  更多评论   

2009-05-03 16:43 by sun
hProcess[i] = piProcess[i].hProcess;
piProcess[i].hProcess状态改变,hProcess[i]状态也改变?
仍没弄明白

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理