多程序服務端實現-共享socket
這裡,我提出另外一種比較獨特的做法,就是多個程序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 <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( 2, 2 );
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 <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( 2, 2 );
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;
}
多程序方式程式設計簡單,程式健壯性相對比較好,但是切換開銷比較大。