如何将此echo服务器转换为聊天服务器?我希望这个程序要做的是让服务器将每个收到的消息发送给每个客户端。我想应该有某种队列,但我对这种实现毫无头绪。代码示例会有很大帮助。这是代码:
服务器:
#include <winsock2.h>
#include <iostream>
using namespace std;
struct CLIENT_INFO
{
SOCKET hClientSocket ;
struct sockaddr_in clientAddr ;
} ;
char szServerIPAddr[ ] = "192.168.1.221" ; // Put here the IP address of the server
int nServerPort = 5050 ; // The server port that will be used by
// clients to talk with the server
bool InitWinSock2_0( ) ;
BOOL WINAPI ClientThread( LPVOID lpData ) ;
int main( )
{
if ( ! InitWinSock2_0( ) )
{
cout << "Unable to Initialize Windows Socket environment" << WSAGetLastError( ) << endl ;
return -1 ;
}
SOCKET hServerSocket ;
hServerSocket = socket(
AF_INET, // The address family. AF_INET specifies TCP/IP
SOCK_STREAM, // Protocol type. SOCK_STREM specified TCP
0 // Protoco Name. Should be 0 for AF_INET address family
) ;
if ( hServerSocket == INVALID_SOCKET )
{
cout << "Unable to create Server socket" << endl ;
// Cleanup the environment initialized by WSAStartup()
WSACleanup( ) ;
return -1 ;
}
// Create the structure describing various Server parameters
struct sockaddr_in serverAddr ;
serverAddr . sin_family = AF_INET ; // The address family. MUST be AF_INET
serverAddr . sin_addr . s_addr = inet_addr( szServerIPAddr ) ;
serverAddr . sin_port = htons( nServerPort ) ;
// Bind the Server socket to the address & port
if ( bind( hServerSocket, ( struct sockaddr * ) &serverAddr, sizeof( serverAddr ) ) == SOCKET_ERROR )
{
cout << "Unable to bind to " << szServerIPAddr << " port " << nServerPort << endl ;
// Free the socket and cleanup the environment initialized by WSAStartup()
closesocket( hServerSocket ) ;
WSACleanup( ) ;
return -1 ;
}
// Put the Server socket in listen state so that it can wait for client connections
if ( listen( hServerSocket, SOMAXCONN ) == SOCKET_ERROR )
{
cout << "Unable to put server in listen state" << endl ;
// Free the socket and cleanup the environment initialized by WSAStartup()
closesocket( hServerSocket ) ;
WSACleanup( ) ;
return -1 ;
}
// Start the infinite loop
while ( true )
{
// As the socket is in listen mode there is a connection request pending.
// Calling accept( ) will succeed and return the socket for the request.
SOCKET hClientSocket ;
struct sockaddr_in clientAddr ;
int nSize = sizeof( clientAddr ) ;
hClientSocket = accept( hServerSocket, ( struct sockaddr *) &clientAddr, &nSize ) ;
if ( hClientSocket == INVALID_SOCKET )
{
cout << "accept( ) failed" << endl ;
}
else
{
HANDLE hClientThread ;
struct CLIENT_INFO* clientInfo = (struct CLIENT_INFO*) malloc(sizeof(struct CLIENT_INFO)) ;
DWORD dwThreadId ;
clientInfo -> clientAddr = clientAddr ;
clientInfo -> hClientSocket = hClientSocket ;
cout << "Client connected from " << inet_ntoa( clientAddr . sin_addr ) << endl ;
// Start the client thread
hClientThread = CreateThread( NULL, 0,
( LPTHREAD_START_ROUTINE ) ClientThread,
( LPVOID ) &(*clientInfo), 0, &dwThreadId ) ;
if ( hClientThread == NULL )
{
cout << "Unable to create client thread" << endl ;
}
else
{
CloseHandle( hClientThread ) ;
}
}
}
closesocket( hServerSocket ) ;
WSACleanup( ) ;
return 0 ;
}
bool InitWinSock2_0( )
{
WSADATA wsaData ;
WORD wVersion = MAKEWORD( 2, 0 ) ;
if ( ! WSAStartup( wVersion, &wsaData ) )
return true ;
return false ;
}
BOOL WINAPI ClientThread( LPVOID lpData )
{
CLIENT_INFO *pClientInfo = ( CLIENT_INFO * ) lpData ;
char szBuffer[ 1024 ] ;
int nLength ;
while ( 1 )
{
nLength = recv( pClientInfo -> hClientSocket, szBuffer, sizeof( szBuffer ), 0 ) ;
if ( nLength > 0 )
{
szBuffer[ nLength ] = '\0' ;
cout << "Received " << szBuffer << " from " << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << endl ;
// Convert the string to upper case and send it back, if its not QUIT
strupr( szBuffer ) ;
if ( strcmp( szBuffer, "QUIT" ) == 0 )
{
closesocket( pClientInfo -> hClientSocket ) ;
return TRUE ;
}
// send( ) may not be able to send the complete data in one go.
// So try sending the data in multiple requests
int nCntSend = 0 ;
char *pBuffer = szBuffer ;
while ( ( nCntSend = send( pClientInfo -> hClientSocket, pBuffer, nLength, 0 ) != nLength ) )
{
if ( nCntSend == -1 )
{
cout << "Error sending the data to " << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << endl ;
break ;
}
if ( nCntSend == nLength )
break ;
pBuffer += nCntSend ;
nLength -= nCntSend ;
}
}
else
{
cout << "Error reading the data from " << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << endl ;
}
}
return TRUE ;
}
客户端:
#include <winsock2.h>
#include <iostream>
using namespace std;
char szServerIPAddr[ 20 ] = "192.168.1.221" ; // Put here the IP address of the server
int nServerPort = 5050 ; // The server port that will be used by // clients to talk with the server
bool InitWinSock2_0( ) ;
int main( )
{
cout << "Enter the server IP Address: " ;
cin >> szServerIPAddr ;
cout << "Enter the server port number: " ;
cin >> nServerPort ;
if ( ! InitWinSock2_0( ) )
{
cout << "Unable to Initialize Windows Socket environment" << WSAGetLastError( ) << endl ;
return -1 ;
}
SOCKET hClientSocket ;
hClientSocket = socket(
AF_INET, // The address family. AF_INET specifies TCP/IP
SOCK_STREAM, // Protocol type. SOCK_STREM specified TCP
0 // Protoco Name. Should be 0 for AF_INET address family
) ;
if ( hClientSocket == INVALID_SOCKET )
{
cout << "Unable to create Server socket" << endl ;
// Cleanup the environment initialized by WSAStartup()
WSACleanup( ) ;
return -1 ;
}
// Create the structure describing various Server parameters
struct sockaddr_in serverAddr ;
serverAddr . sin_family = AF_INET ; // The address family. MUST be AF_INET
serverAddr . sin_addr . s_addr = inet_addr( szServerIPAddr ) ;
serverAddr . sin_port = htons( nServerPort ) ;
// Connect to the server
if ( connect( hClientSocket, ( struct sockaddr * ) &serverAddr, sizeof( serverAddr ) ) < 0 )
{
cout << "Unable to connect to " << szServerIPAddr << " on port " << nServerPort << endl ;
closesocket( hClientSocket ) ;
WSACleanup( ) ;
return -1 ;
}
char szBuffer[ 1024 ] = "" ;
while ( strcmp( szBuffer, "QUIT" ) != 0 )
{
cout << "Enter the string to send (QUIT) to stop: " ;
cin >> szBuffer ;
int nLength = strlen( szBuffer ) ;
// send( ) may not be able to send the complete data in one go.
// So try sending the data in multiple requests
int nCntSend = 0 ;
char *pBuffer = szBuffer ;
while ( ( nCntSend = send( hClientSocket, pBuffer, nLength, 0 ) != nLength ) )
{
if ( nCntSend == -1 )
{
cout << "Error sending the data to server" << endl ;
break ;
}
if ( nCntSend == nLength )
break ;
pBuffer += nCntSend ;
nLength -= nCntSend ;
}
strupr( szBuffer ) ;
if ( strcmp( szBuffer, "QUIT" ) == 0 )
{
break ;
}
nLength = recv( hClientSocket, szBuffer, sizeof( szBuffer ), 0 ) ;
if ( nLength > 0 )
{
szBuffer[ nLength ] = '\0' ;
cout << "Received " << szBuffer << " from server" << endl ;
}
}
closesocket( hClientSocket ) ;
WSACleanup( ) ;
return 0 ;
}
bool InitWinSock2_0( )
{
WSADATA wsaData ;
WORD wVersion = MAKEWORD( 2, 0 ) ;
if ( ! WSAStartup( wVersion, &wsaData ) )
return true ;
return false ;
}
答案 0 :(得分:0)
只是为了提出这个想法(如果您需要进一步的帮助,请在本网站上实施您的服务器并提出新的更具体的问题):
std::vector
的缺点是,无论是插入还是删除消息,您都会一直在移动数据(具体取决于您的实现方式)。某种环形缓冲区更有效 - 它可以基于std :: vector实现。accept
ed连接,创建一个从套接字读取的新线程,并将接受的数据放入队列中。请注意,您需要保护消息队列和客户端列表不受竞争条件影响,因为两者都可以从多个线程访问!