2011-05-16 229 views
1

我正在基于C++控制台制作服务器,客户端应用程序。C++服务器客户端聊天

我做了什么至今:

  • 我可以连接到服务器。
  • 我可以发送消息到服务器。
  • 服务器可以发回消息。

但我不明白,我怎么能让服务器也作为客户端发送消息给客户端,而他正在处理从客户端收到的消息?

人们可以利用它作为一个例子,以及:d

嗯,我也将张贴代码的某些部分:

服务器:

#include "stdafx.h" 
    using namespace std; 
//our main function 
void main() 
    { 
int numClients; 
long antwoord; 
char chatname[100]; 
char bericht[250]; //messages 
char sbericht[250]; //smessages 
    //here we set the Winsock-DLL to start 

WSAData wsaData; 
WORD DLLVERSION; 
DLLVERSION = MAKEWORD(2,1); 

//here the Winsock-DLL will be started with WSAStartup 
       //version of the DLL 
antwoord = WSAStartup(DLLVERSION, &wsaData); 

if(antwoord != 0) 
{ 
    WSACleanup(); 
    exit(1); 
} 
else 
{ 
    cout << "WSA started successfully" <<endl; 
    cout << "The status: \n" << wsaData.szSystemStatus <<endl; 
} 
//the DLL is started 

//structure of our socket is being created 
SOCKADDR_IN addr; 

//addr is our struct 

int addrlen = sizeof(addr); 

//socket sListen - will listen to incoming connections 
SOCKET sListen; 
//socket sConnect - will be operating if a connection is found. 
SOCKET sConnect; 

//setup of our sockets 
       //opgezocht op internet - AF_INET bekend dat het lid is van de internet familie 
          //Sock_STREAM betekenend dat onze socket een verbinding georiënteerde socket is. 
sConnect = socket(AF_INET,SOCK_STREAM,NULL); 

//now we have setup our struct 

//inet_addr is our IP adres of our socket(it will be the localhost ip 
//that will be 127.0.0.1 

addr.sin_addr.s_addr = inet_addr("192.168.1.103"); 

//retype of the family 
addr.sin_family = AF_INET; 

//now the server has the ip(127.0.0.1) 
//and the port number (4444) 
addr.sin_port = htons(4444); 

//here we will define the setup for the sListen-socket 
sListen = socket(AF_INET,SOCK_STREAM,NULL); 

if (sConnect == INVALID_SOCKET) 
{ 
    cout << "Error at socket(): \n" << WSAGetLastError() <<endl; 
    WSACleanup(); 
} 
else 
{ 
    cout << "Connect socket() is OK!" <<endl; 
} 

if(sListen == INVALID_SOCKET) 
{ 
    cout << "Error at socket(): \n" << WSAGetLastError() <<endl; 
    WSACleanup(); 
} 
else 
{ 
    cout << "Listen socket() is OK!" <<endl; 
} 
//here the sListen-socket will be bind 
//we say that the socket has the IP adress of (127.0.0.1) and is on port (4444) 
//we let the socket become the struct "addr" 
if(bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) 
{ 
    cout << "bind() failed: \n" << WSAGetLastError() <<endl; 
    WSACleanup(); 
    exit(1); 
} 
else{ 
    cout << "bind() is OK!" <<endl; 
} 


//here we will tell what the server must do when a connection is found 
//therefor we will create an endless loop 
cout << "Waiting for a incoming connection..." <<endl; 
for(;;) 
{ 

     //now we let the socket listen for incoming connections 
      //SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet 
     listen(sListen, SOMAXCONN); 
     while(numClients < SOMAXCONN) 
     { 
      //if a connection is found: show the message! 
      if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen)) 
      { 
       cout << "A Connection was found!" <<endl; 

       antwoord = send(sConnect, "Welcome to our chat:", 21,NULL); 

       if(antwoord > 1) 
       { 

        antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL); 
        antwoord = recv(sConnect, chatname, sizeof(chatname), NULL); 

         while(antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL))) 
         { 
          antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL); 
          antwoord = send(sConnect, chatname, sizeof(chatname), NULL);  
         } 

       } 
       else 
       { 
       cout << "The connection to the client has been lost... \n" << "please exit the server." <<endl; 
       break; 
       } 
       numClients++; 
      } 
     } 


} 
} 

客户:

// ChatServer.cpp : Defines the entry point for the console application. 
    // 
    //include of the stdafx.h file where importent files are being included 

    #include "stdafx.h" 

    using namespace std; 

    void smessage() 
    { 

    } 
    //our main function 
    int main() 
    { 
//here we set the Winsock-DLL to start 
string bevestiging; 

char chatname[100]; 

char bericht[250]; 
char sbericht[250]; 

string strbericht; 

string strsbericht; 

long antwoord; 
//here the Winsock-DLL will be started with WSAStartup 
       //version of the DLL 
WSAData wsaData; 
WORD DLLVERSION; 
DLLVERSION = MAKEWORD(2,1); 
antwoord = WSAStartup(DLLVERSION, &wsaData); 
if(antwoord != 0) 
{ 
    exit(1); 
} 
else 
{ 
    cout << "WSA started successfully" <<endl; 
    cout << "The status: \n" << wsaData.szSystemStatus <<endl; 
} 

SOCKADDR_IN addr; 

int addrlen = sizeof(addr); 

SOCKET sConnect; 

sConnect = socket(AF_INET, SOCK_STREAM, NULL); 

if (sConnect == INVALID_SOCKET) 
{ 
    cout << "Error at socket(): \n" << WSAGetLastError() <<endl; 
} 
else 
{ 
    cout << "socket() is OK!\n" <<endl; 
} 



addr.sin_addr.s_addr = inet_addr("192.168.1.103"); 

addr.sin_family = AF_INET; 

addr.sin_port = htons(4444); 

cout << "What is your chat name?" <<endl; 

cin.getline(chatname, 100); 


cout << "Do you want to connect to the server? [Y/N]" <<endl; 

cin >> bevestiging; 


if (bevestiging == "N") 
{ 
    exit(1); 
} 
else 
{ 
    if(bevestiging == "Y") 
    { 

     connect(sConnect, (SOCKADDR*)&addr, sizeof(addr)); 

     antwoord = recv(sConnect, bericht, sizeof(bericht), NULL); 

     strbericht = bericht; 

     cout << strbericht << chatname <<endl; 

     while(true) 
     { 
      if(antwoord > 1) 
      { 

       cin.clear(); 
       cin.sync(); 
       cout << chatname << " :" <<endl; 
       cin.getline(sbericht, 250); 
       antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL); 
       antwoord = send(sConnect, chatname, sizeof(chatname), NULL); 

       while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL))) 
       { 
        antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL); 
        antwoord = recv(sConnect, chatname, sizeof(chatname), NULL); 
        cout << chatname << ":" <<endl; 
        cout << sbericht <<endl; 
        cin.getline(sbericht, 250); 

       } 

      } 

      else 
      { 
      cout << "The connection to the server has been lost... \n" << "please exit the client." <<endl; 

      } 
     } 
    } 
} 
    } 

回答

0

您可能必须打开另一个套接字。客户端也必须充当服务器。

0

首先:将一个20mb的压缩文件放入网络中以获取大约4个有趣的源文件,这不是一个好的选择。您的目标文件和调试输出对我们来说不感兴趣,因为我们想要帮助您的源代码。尝试上传下一次仅包含源文件的zip文件。其次:如果其他人想要了解你的源代码,并且不熟悉你的母语,他们必须猜测。尝试使用英语作为源代码语言和其他各种原因。

现在回答你的问题:

答案已经在你的代码中。目前,服务器正在循环,直到连接达到最大数量,接收输入并发送回答。所以实际上你已经实现了它。我想如果你想用两种方式发送启动的消息,你必须改变你的软件体系结构。

+0

谢谢,我会记住这一点,下一次。 你是对的,但问题是:当服务器recv从客户端输入时,它会在while循环中将消息发送回客户端。 但我的问题是更多的地方和方式,而服务器recv消息我可以在代码中创建一个输入域的服务器,而不中断recv循环? – 2011-05-16 09:56:14

0

您的代码有几个基本问​​题:

  • 服务器只能同时处理一个客户端。如果你的服务器上有超过一个用户(就像聊天服务器一样),你需要能够一次侦听多个连接。 selectWSAEventSelectWaitForMultipleObjects,在这里会有很大的帮助。

  • 您认为一次会出现整个固定大小的消息。 TCP不能保证(因为“流”的概念认为数据只是一个潜在的无限序列的单个字节),并且一个半发送的消息可能会冻结服务器,等待其余的消息。如果全部都在你的局域网上,这并不是什么大不了的事情,但是如果你将这个服务暴露给互联网,你就会要求随机锁定。为了防止这种情况发生,获取数据并将其放入缓冲区中,仅当您收到完整的消息时才处理它。

  • 对话是在锁步完成的。也就是说,客户端发送一条消息,并等待响应,然后(然后只有然后)期望控制台输入。采用这种设计,每发送一条消息都会收到一条消息。为了解决这个问题,我会经常为每个方向的数据提供一个线程 - 一个获取控制台输入并将其发送到服务器,另一个收听服务器并打印收到的消息。 (注意,这意味着你可以在输入的时候接收到消息,但这会让控制台输入变得烦人)。线程是一个半高级的话题 - 一旦你开始创建新线程,你通常必须担心同步等。但在这种情况下,它通常比替代品更清洁。

样品线程代码(非常粗略的,因为我没有一个C++编译器方便):

const int MessageLength = 250; 
const int NameLength = 250; 

char myname[NameLength]; 

bool sendFully(SOCKET s, char* buffer, size_t buffer_len, int flags) 
{ 
    char *end = buffer + buffer_len; 
    while (buffer != buffer_len) 
    { 
     int sent = send(s, buffer, end - buffer, flags); 
     if (sent == 0) return false; 
     buffer += sent; 
    } 
    return true; 
} 

DWORD WINAPI watchConsoleInput(void*) 
{ 
    char input[MessageLength]; 
    while (true) 
    { 
     std::cin.getline(input, MessageLength); 
     if (!sendFully(sConnect, input, sizeof(input), 0)) 
      break; 
     if (!sendFully(sConnect, myname, sizeof(myname), 0)) 
      break; 
    } 
    return 0; 
} 

int main() 
{ 
    char chatname[NameLength]; 
    char sbericht[MessageLength]; 

    ... get our name in myname ... 

    ... do the connect stuff ... 

    HANDLE watcher = CreateThread(NULL, 0, watchConsoleInput, NULL, 0, NULL); 

    while (true) 
    { 
     // Added MSG_WAITALL to work around the whole-message-at-a-time thing 
     if (recv(sConnect, sbericht, sizeof(sbericht), MSG_WAITALL) != sizeof(sbericht)) 
      break; 
     if (recv(sConnect, chatname, sizeof(chatname), MSG_WAITALL) != sizeof(sbericht)) 
      break; 
    } 

    // Don't care about errors; we're just being polite 
    shutdown(sConnect, SD_BOTH); 

    closesocket(sConnect); 
    cout << "Connection lost\n"; 

    // ExitProcess rather than just 'return', so we know the watcher thread dies 
    ExitProcess(0); 
} 
+0

我完全同意你的看法。 我现在正在尝试多线程部分,并希望成功? :P 你可以为我的代码做一个例子吗?所以我可以与你们比较,并仍然有参考。?在此先感谢cHao :) – 2011-05-16 11:12:23

+0

这是一个更好的使用? #define MessageLength 250 – 2011-05-17 07:39:58

+0

如果可以,最好避免使用宏,但在这种情况下'#define'和'const'一样好。然而,一个常见的惯例是,宏的名称将全部大写,如“MESSAGE_LENGTH”。 – cHao 2011-05-18 17:29:59