2015-08-13 87 views
-2

我有一點難度,試圖編碼,因爲我不知道很多。我有一臺可以互相通信的兩臺PC。它可以工作,所有,但它只能發送單個字符給對方。如果命令在沒有IP地址參數的情況下執行,一臺PC就像服務器一樣,另一臺服務器的IP地址就像客戶端連接到服務器一樣。WinSock2客戶端/服務器通信:發送和接收字符串

的代碼都在這裏:

// Quick and dirty - error checks omitted for brevity. 

#include <iostream> 
#include <string> 
#include <conio.h> 

#include <WinSock2.h> 
#include <ws2tcpip.h> 

using namespace std; 

void chat (int socket_d) 

{ 
    while (true) 
    { 
     if (_kbhit()) 
     { 
      char ch; 
      ch = _getche(); 
      int n; 
      n = send (socket_d, &ch, 1, 0); 
      if (ch == '\r') 
      { 
       cout << "\n"; 
      } 
     } 

     int n; 
     int count = 0; 
     char byte; // Read one byte at a time - is this efficient? 
     n = recv (socket_d, &byte, 1, 0); 
     if (n <= 0) 
     { 
      if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. 
      { 
       cout << "Terminated " << WSAGetLastError() << "\n"; 
       return; 
      } 
     } 
     else 
     { 
      cout << (char)byte; 
      if ((char) byte == '\r') 
       cout << "\n"; 
     } 
    } 
} 

int main (int argc, char * argv []) 

{ 
    // Messy process with windows networking - "start" the networking API. 
    WSADATA wsaData; 
    int result = WSAStartup (MAKEWORD (2, 2), &wsaData); 

    unsigned short port = 25565; 

    // If argument is IP address - be a client and connect to it. Otherwise 
    // be a server. 
    if (argc > 1) 
    { 
     int socket_d; 
     socket_d = socket (AF_INET, SOCK_STREAM, 0); 

     // be a client. 
     struct sockaddr_in server_addr; 
     server_addr.sin_family = AF_INET; 
     server_addr.sin_addr.s_addr = inet_addr (argv[1]); // Parse the string and create the 32 bit address. 
     server_addr.sin_port = htons (port); // Watch out for the endian conversion! 

     connect (socket_d, (sockaddr *) &server_addr, sizeof (server_addr)); 

     u_long iMode=1; 
     ioctlsocket (socket_d, FIONBIO, &iMode); // put the socket into non-blocking mode. 

     chat (socket_d); 

     closesocket (socket_d); 
    } 
    else 
    { 
     // be a server 
     int listen_socket_d; 
     int connection_socket_d; 

     listen_socket_d = socket (AF_INET, SOCK_STREAM, 0); 

     struct sockaddr_in server_addr; 
     server_addr.sin_family = AF_INET; 
     server_addr.sin_addr.s_addr = INADDR_ANY; // A placeholder that will be replaced with my own address. 
     server_addr.sin_port = htons (port); // Watch out for the endian conversion! 

     bind (listen_socket_d, (struct sockaddr *) &server_addr, sizeof (server_addr)); 

     int backlog = 5; 
     listen (listen_socket_d, backlog); 

     // take only the first connection. 
     struct sockaddr_storage their_addr; 
     int their_addr_size = sizeof(their_addr); 
     connection_socket_d = accept (listen_socket_d, (struct sockaddr *) &their_addr, &their_addr_size); 

     u_long iMode=1; 
     ioctlsocket (connection_socket_d, FIONBIO, &iMode); // put the socket into non-blocking mode. 

     chat (connection_socket_d); 
     closesocket (connection_socket_d); 
    } 

    return 0; 
} 

我想實現的是能夠將字符串發送,而不是單個字符。我希望這樣做的方式是增加它發送的字節大小,而不是單個字節。我假設它可以工作的方式,假設一次發送和接收64個字節的總大小。

+0

這是下面的答案解決了您的問題? –

+0

只是我的方式通過你給我的,並瞭解它 – vyrix09

+0

是否有可能你可以試試你的自我,因爲我不知道爲什麼它不工作 – vyrix09

回答

0

從問題區域的實際情況來看,這並不是很清楚。

我的解決方案如下。基本思想是分配相同大小的發送和接收緩衝區,在鍵盤輸入端將字符附加到發送緩衝區,並在緩衝區滿或用戶點擊返回鍵時傳輸緩衝區。

非阻塞send函數可能不會立即發送整個緩衝區(see the docs)。我決定繼續並在此阻塞,直到發送整個緩衝區,所以我不必跟蹤單獨的輸入和發送緩衝區。我敢肯定,你可以改善這一點。

接收部分只是迴應接收到的任何字節。一般來說,沒有辦法知道其他用戶是否「完成」發送數據,因此我們只需在第一次調用後將我們收到的內容打印到recv

我總是在memset緩衝區發送和接收後的所有零,以防止奇怪的行爲丟失空終止符。它會更加優化,只需在當前字符串的末尾附加一個空字符,但我有時會得到偏執狂。

這裏是我的代碼:現在

#define BUFFER_SIZE 64 


void chat (int socket_d) 
{ 
    char sendBuffer[BUFFER_SIZE] = { 0 }; 
    char receiveBuffer[BUFFER_SIZE] = { 0 }; 
    int bufferPosition = 0; 
    int charsSent = 0; 
    int charsReceived = 0; 
    while (true) 
    { 
    if (_kbhit()) 
    { 
     sendBuffer[bufferPosition++] = _getche(); 

     if (sendBuffer[bufferPosition - 1] == '\r' || bufferPosition == BUFFER_SIZE - 1) 
     { 
     // This defeats the purpose of a non-blocking socket, I know. 
     // You can do better by keeping separate buffers for sending and 
     // collecting input. 
     while (charsSent < bufferPosition) 
     { 
      charsSent += send(
      socket_d, 
      &sendBuffer[charsSent], // Treats the address of a character as a string. 
      bufferPosition - charsSent, // Only send the part that hasn't been sent yet. 
      0); 
     } 

     memset(sendBuffer, 0, bufferPosition); // Paranoid. 
     bufferPosition = charsSent = 0; 
     cout << "\n"; 
     } 
    } 

    charsReceived = recv (socket_d, receiveBuffer, BUFFER_SIZE, 0); 
    if (charsReceived <= 0) 
    { 
     if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. 
     { 
     cout << "Terminated " << WSAGetLastError() << "\n"; 
     return; 
     } 
    } 
    else 
    { 
     cout << receiveBuffer; 
     if (receiveBuffer[charsReceived - 1] == '\r') 
     cout << "\n"; 
     memset(receiveBuffer, 0, charsReceived); // Super paranoid. 
    } 
    } 
} 

,我真的不會考慮這個「好」,直到它支持UTF-8,或至少wchar_t。 ASCII缺少很多人們希望能夠在真正的聊天應用程序中使用的字符。

PS - 根據Visual Studio 2013,inet_addr函數已被棄用。我用inet_ptons代替。 Here's the documentation on the Winsock implementation for it

相關問題