本文内容来自 http://www.rohitab.com/discuss/topic/26991-cc-how-to-code-a-multi-client-server-in-c-using-threads/
服务器
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
/* our thread for recving commands */
DWORD WINAPI receive_cmds( LPVOID lpParam )
{
printf( "thread created\r\n" );
/* set our socket to the socket passed in as a parameter */
SOCKET current_client = (SOCKET) lpParam;
/* buffer to hold our recived data */
char buf[100];
/* buffer to hold our sent data */
char sendData[100];
/* for error checking */
int res;
/* our recv loop */
while ( true )
{
res = recv( current_client, buf, sizeof(buf), 0 ); /* recv cmds */
Sleep( 10 );
if ( res == 0 )
{
MessageBox( 0, "error", "error", MB_OK );
closesocket( current_client );
ExitThread( 0 );
}
if ( strstr( buf, "hello" ) ) /* greet this user */
{
printf( "\nrecived hello cmd" );
strcpy( sendData, "hello, greetz from KOrUPt\n" );
Sleep( 10 );
send( current_client, sendData, sizeof(sendData), 0 );
}
else if ( strstr( buf, "bye" ) ) /* dissconnected this user */
{
printf( "\nrecived bye cmd\n" );
strcpy( sendData, "cya\n" );
Sleep( 10 );
send( current_client, sendData, sizeof(sendData), 0 );
/* close the socket associted with this client and end this thread */
closesocket( current_client );
ExitThread( 0 );
}
else
{
strcpy( sendData, "Invalid cmd\n" );
Sleep( 10 );
send( current_client, sendData, sizeof(sendData), 0 );
}
/* clear buffers */
strcpy( sendData, "" );
strcpy( buf, "" );
}
}
int main()
{
printf( "Starting up multi-threaded TCP server by KOrUPt\r\n" );
/* our masterSocket(socket that listens for connections) */
SOCKET sock;
/* for our thread */
DWORD thread;
WSADATA wsaData;
sockaddr_in server;
/* start winsock */
int ret = WSAStartup( 0x101, &wsaData ); /* use highest version of winsock avalible */
if ( ret != 0 )
{
return(0);
}
/* fill in winsock struct ... */
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 123 ); /* listen on telnet port 23 */
/* create our socket */
sock = socket( AF_INET, SOCK_STREAM, 0 );
if ( sock == INVALID_SOCKET )
{
return(0);
}
/* bind our socket to a port(port 123) */
if ( bind( sock, (sockaddr *) &server, sizeof(server) ) != 0 )
{
return(0);
}
/* listen for a connection */
if ( listen( sock, 5 ) != 0 )
{
return(0);
}
/* socket that we snedzrecv data on */
SOCKET client;
sockaddr_in from;
int fromlen = sizeof(from);
/* loop forever */
while ( true )
{
/* accept connections */
client = accept( sock, (struct sockaddr *) &from, &fromlen );
printf( "Client connected\r\n" );
/* create our recv_cmds thread and parse client socket as a parameter */
CreateThread( NULL, 0, receive_cmds, (LPVOID) client, 0, &thread );
}
/* shutdown winsock */
closesocket( sock );
WSACleanup();
/* exit */
return(0);
}
客户端
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#include <signal.h>
#include <stdio.h>
/*
* DECLARATIONS
* error trapping signals
*/
#define SIGINT 2
#define SIGKILL 9
#define SIGQUIT 3
/* SOCKETS */
SOCKET sock, client;
void s_handle( int s )
{
if ( sock )
closesocket( sock );
if ( client )
closesocket( client );
WSACleanup();
Sleep( 1000 );
std::cout << "EXIT SIGNAL :" << s;
exit( 0 );
}
void s_cl( char *a, int x )
{
std::cout << a;
s_handle( x + 1000 );
}
int main()
{
HANDLE hStdout = GetStdHandle( STD_OUTPUT_HANDLE );
SetConsoleTextAttribute( hStdout, FOREGROUND_GREEN | FOREGROUND_INTENSITY );
SetConsoleTitle( ".:: Basic Echo Client By KOrUPt 07 ::. " );
/* Declarations */
DWORD poll;
int res, i = 1, port = 999;
char buf[100];
char msg[100] = "";
char ip[15];
WSADATA data;
signal( SIGINT, s_handle );
signal( SIGKILL, s_handle );
signal( SIGQUIT, s_handle );
std::cout << "\t\tEcho Client by KOrUPt";
std::cout << "\n\n\n\t\tEnter IP to connect to: ";
gets( ip );
sockaddr_in ser;
sockaddr addr;
ser.sin_family = AF_INET;
ser.sin_port = htons( 123 ); /* Set the port */
ser.sin_addr.s_addr = inet_addr( ip ); /* Set the address we want to connect to */
memcpy( &addr, &ser, sizeof(SOCKADDR_IN) );
res = WSAStartup( MAKEWORD( 1, 1 ), &data ); /* Start Winsock */
std::cout << "\n\nWSAStartup"
<< "\nVersion: " << data.wVersion
<< "\nDescription: " << data.szDescription
<< "\nStatus: " << data.szSystemStatus << std::endl;
if ( res != 0 )
s_cl( "WSAStarup failed", WSAGetLastError() );
sock = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); /* Create the socket */
if ( sock == INVALID_SOCKET )
s_cl( "Invalid Socket ", WSAGetLastError() );
else if ( sock == SOCKET_ERROR )
s_cl( "Socket Error)", WSAGetLastError() );
else
std::cout << "Socket Established" << std::endl;
res = connect( sock, &addr, sizeof(addr) ); /* Connect to the server */
if ( res != 0 )
{
s_cl( "SERVER UNAVAILABLE", res );
}
else
{
std::cout << "\nConnected to Server: ";
memcpy( &ser, &addr, sizeof(SOCKADDR) );
}
char RecvdData[100] = "";
int ret;
while ( true )
{
strcpy( buf, "" );
std::cout << "\nEnter message to send ->\n";
fgets( buf, sizeof(buf), stdin );
Sleep( 5 );
res = send( sock, buf, sizeof(buf), 0 );
if ( res == 0 )
{
/* 0==other side terminated conn */
printf( "\nSERVER terminated connection\n" );
Sleep( 40 );
closesocket( client );
client = 0;
break;
}
else if ( res == SOCKET_ERROR )
{
/* -1 == send error */
printf( "Socket error\n" );
Sleep( 40 );
s_handle( res );
break;
}
ret = recv( sock, RecvdData, sizeof(RecvdData), 0 );
if ( ret > 0 )
{
std::cout << std::endl << RecvdData;
strcpy( RecvdData, "" );
}
}
closesocket( client );
WSACleanup();
}
说明
原文是从单线程说的,感兴趣可以去看一下。
关于客户端发送消息,发送 hello
时服务器会正常回复,发送 bye
会断开连接,发送其他的服务器都只会回复 Invalid cmd
,具体看服务器的 receive_cmds
函数。
关键的地方就在服务器端使用的多线程:
/* loop forever */
while ( true )
{
/* accept connections */
client = accept( sock, (struct sockaddr *) &from, &fromlen );
printf( "Client connected\r\n" );
/* create our recv_cmds thread and parse client socket as a parameter */
CreateThread( NULL, 0, receive_cmds, (LPVOID) client, 0, &thread );
}
TODO
- 加点解释