C winapi中服务器的实现
Posted
技术标签:
【中文标题】C winapi中服务器的实现【英文标题】:Implementing of server in C winapi 【发布时间】:2016-01-05 15:08:42 【问题描述】:我是多线程新手,想设计服务器
这是我当前的代码:
typedef struct Session
HANDLE handlers[2];
//HANDLE h_Send;
//main session handler
HANDLE h_MainHandler;
HANDLE sema_MessageQ;
char* UserName;
SOCKET Socket;
PList MessageQ;
Session,*pSession;
#define NUM_OF_WORKER_THREADS 5
#define MAX_LOOPS 5
#define SEND_STR_SIZE 256
static Session ClientSessions[NUM_OF_WORKER_THREADS];
void MainServer()
int Ind;
int Loop;
int bindRes;
int ListenRes;
unsigned long Address;
char SendStr[SEND_STR_SIZE];
SOCKET MainSocket = INVALID_SOCKET;
SOCKADDR_IN service;
XPacket Packet;
TransferResult_t RecvRes;
TransferResult_t SendRes;
//doing all the WSAStartup / bind / listen
//and error checkinh
// Initialize all thread handles to NULL, to mark that they have not been initialized
for ( Ind = 0; Ind < NUM_OF_WORKER_THREADS; Ind++ )
ClientSessions[Ind].h_MainHandler = NULL;
printf( "Waiting for a client to connect...\n" );
for ( Loop = 0; Loop < MAX_LOOPS; Loop++ )
SOCKET AcceptSocket = accept( MainSocket, NULL, NULL );
if ( AcceptSocket == INVALID_SOCKET )
printf( "Accepting connection with client failed, error %ld\n", WSAGetLastError());
goto server_cleanup_3;
printf( "Client Connected.\n" );
Ind = FindFirstUnusedThreadSlot();
if ( Ind == NUM_OF_WORKER_THREADS ) //no slot is available
printf( "No slots available for client, dropping the connection.\n" );
closesocket( AcceptSocket ); //Closing the socket, dropping the connection.
continue;
else
/*check that user name is not in use*/
char *AcceptedStr = NULL;
//get UserName
RecvRes = ReceivePacket(&Packet, AcceptSocket);
if ( RecvRes == TRNS_FAILED || RecvRes == TRNS_DISCONNECTED )
printf( "Service socket error while reading, closing socket.\n" );
closesocket( AcceptSocket );
continue;
if (FALSE == CheckForUsedName((char*)Packet.Data))
strcpy(SendStr ,(char*)Packet.Data);
strcpy( SendStr + strlen((char*)Packet.Data), " already taken!" );
SendRes = (TransferResult_t)PROTOCOL_SendCommand(
SendStr,
strlen(SendStr) + 1,
PROT_SERVER_RECEIVE_SYSTEM_MSG,
AcceptSocket);
closesocket(AcceptSocket);
continue;
printf("\r\nCode:%d Length:%lu Text:%s",Packet.Code,Packet.Length,(char*)Packet.Data);
ClientSessions[Ind].UserName = (char*)Packet.Data;
ClientSessions[Ind].Socket = AcceptSocket;
ClientSessions[Ind].h_MainHandler = CreateThread(
NULL,
0,
( LPTHREAD_START_ROUTINE ) ServiceThread,
&( ClientSessions[Ind]),
0,
NULL
);
// for ( Loop = 0; Loop < MAX_LOOPS; Loop++ )
//~~~~~~~~server clean up~~~~~~~~~~~
server_cleanup_3:
CleanupWorkerThreads();
server_cleanup_2:
if ( closesocket( MainSocket ) == SOCKET_ERROR )
printf("Failed to close MainSocket, error %ld. Ending program\n", WSAGetLastError() );
server_cleanup_1:
if ( WSACleanup() == SOCKET_ERROR )
printf("Failed to close Winsocket, error %ld. Ending program.\n", WSAGetLastError() );
我当前的代码是在等待客户端断开连接之前等待客户端连接的数量 这显然不是很好 我想要的是某种形式
服务器:
//accept first client
\\while( InterlockedAdd(&number_of_connected_client , 0) >0 ) ?
while(number_of_connected_client > 0)
//wait for new client
//close server
每个客户会话:
//do somthing
//once finish
InterlockedDecrement(number_of_connected_client );
我的问题是如何实现不在spin-lock
中的while()
部分
【问题讨论】:
在我看来,您不应该在这里使用无锁方法。你只会以一场比赛告终。使用锁。通过该锁保护客户端的连接和断开连接。当客户端计数变为零时,发出终止信号。也许通过发出一个主线程阻塞的事件。当主线程检测到事件发出信号时,它会关闭。 @DavidHeffernan 我不确定我是否理解,您能否发布一些代码或链接到更完整的示例 我没有时间。你知道多线程中的事件是什么吗? 【参考方案1】:我之前使用的服务器设计与您已有的非常相似。
主服务器线程正在等待来自客户端的连接(只需更改您的 for 循环以使其成为无限循环(例如 while(1) //accept connection etc. )。当主线程接受连接时,它将套接字(由接受返回)传递给工作线程。
这个设计的棘手部分是管理工作线程,以某种方式为每个客户端连接选择一个工作线程,并将套接字传递给所选的工作线程。
我注意到您为每个连接创建了一个新的工作线程。这样做的好处是简单,当然缺点是为每个连接创建一个线程会影响性能。
我多年前创建了一个服务器(当时我不知道更好),我想出的解决方案使用了 Windows Synchronization Functions。我使用 Windows 事件在主线程和工作线程之间进行协调。我不会详细介绍,因为我再也不会那样做了。这太复杂了。我基本上都是手动做的。
如果我今天必须这样做,我只会使用 Windows 线程池(请参阅QueueUserWorkItem 函数)。
【讨论】:
以上是关于C winapi中服务器的实现的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 WinAPI 和 C++ 从 Windows 系统上的服务通知客户端应用程序?
SendMessage() WINAPI 在用于连接到 DDE 服务器时挂起