2011-05-16 223 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