Внедрение сервера на C winapi
Я новичок в многопоточности и хочу разработать сервер
это мой текущий код:
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 );
}
мой вопрос заключается в том, как реализовать while()
часть не в spin-lock
1 ответ
Дизайн сервера, который я использовал ранее, очень похож на тот, который у вас уже есть.
Поток основного сервера ожидает подключения от клиентов (просто измените цикл for, чтобы сделать его бесконечным циклом (например, while(1) { // принять подключение и т. Д. }). Когда основной поток принимает подключение, он проходит через сокет (возвращается через accept) в рабочий поток.
Сложная часть этого проекта заключается в управлении рабочими потоками, выборе рабочего потока для каждого клиентского соединения и передаче сокета выбранному рабочему потоку.
Я заметил, что вы создаете новый рабочий поток для каждого соединения. Преимущество этого состоит в простоте, недостатком, конечно, является снижение производительности при создании потока для каждого соединения.
Я создал сервер много лет назад (когда я не знал ничего лучше), и в предложенном мною решении использовались функции синхронизации Windows. Я использовал Windows Events для координации между основным потоком и рабочим потоком. Я не буду вдаваться в подробности, потому что я никогда не буду делать это снова. Это было слишком сложно. Я делал все вручную.
Если бы мне пришлось сделать это сегодня, я бы просто использовал пул потоков Windows (см. Функцию QueueUserWorkItem).