2014-06-29 21 views
1

我想寫一個基本的套接字應用程序,它將接受來自客戶端的字符串,並對我在內存中的數據結構做一些工作。我正在嘗試使用C++ 11,因爲我想爲多線程提供語言支持,並且發現了一個奇怪的問題。我有代碼在端口1234上創建偵聽套接字,並接受一個連接,打印出一個虛擬字符串並將其關閉。如果我與編譯:聽套接字工作在g ++只有-std = c + + 11

g++ -o socket_test socket_test.cpp

它完美,但如果我與編譯:

g++ -std=c++11 -o socket_test socket_test.cpp

它編譯並運行正常,但如果我嘗試連接到端口它會在連接被拒絕後反彈回來。測試代碼是在這裏:

#include <iostream> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

using namespace std; 

int main() 
{ 
    cout << "Hello world, I'm going to open a socket on port 1234 now!" << endl; 

    int m_socket = 0; 
    int m_client = 0; 
    int port = 1234; 
    sockaddr_in serv_addr, client_addr; 
    socklen_t client_len; 

    m_socket = socket(AF_INET, SOCK_STREAM, 0); 
    memset(&serv_addr, 0, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(port); 
    bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 
    cout << "Listen returns: " << listen(m_socket, SOMAXCONN) << endl; 
    m_client = accept(m_socket, (struct sockaddr *)&client_addr, &client_len); 
    write(m_client, "Numa numa\n", 10); 
    close(m_client); 
    close(m_socket); 

    return 0; 
} 

我的編譯環境是OSX 10.9.2,使用G ++如下:

battra:socket_test matt$ g++ --version 
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr 
--with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/ 
MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1 
Apple LLVM version 5.1 (clang-503.0.38) (based on LLVM 3.4svn) 
Target: x86_64-apple-darwin13.2.0 
Thread model: posix 
battra:socket_test matt$ 
+7

請了解*返回值檢查和錯誤處理*。在不可代碼的代碼上浪費時間毫無意義。 –

+0

這是一個精簡的測試版本,完整的代碼行爲相同,並檢查所有返回值,沒有錯誤代碼出現。我也在調用listen()之後在端口上添加了一個打印輸出,並且C++ 11版本始終是上面60000s中隨機分配的端口,另一個版本始終是端口1234.所以這似乎是問題所在,我仍然不確定爲什麼bind()在兩個版本之間抓住不同的端口。 –

回答

7

此問題是爲什麼導入整個名稱空間不好主意的經典示例。最終,導入的命名空間的內容將發生變化 - 在這種情況下,在C++ 11中添加std::bind - 以及在靜默地改變含義之前工作得很好的非限定名稱。

假若你是檢查bind返回值:

cout << "Bind returns: " 
    << bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) 
    << endl; 

所產生的編譯錯誤將會使問題更加顯而易見:

main.cpp:24:8: error: invalid operands to binary expression ('basic_ostream<char, std::__1::char_traits<char> >' and '__bind<int &, sockaddr *, unsigned long>')

<< bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) 
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

簡單的解決方案是使用合格的名稱::bind明確表示您的意思是全局命名空間中的bind。更好的解決方案是從您的程序中完全刪除using namespace std,並避免將來出現類似問題。

+0

謝謝你!它爲我工作! –

1

我懷疑問題無關的事實,您正在使用-std=c++11 ,而更多的是你第二次運行該版本的事實。

如果您嘗試兩次運行第一個版本,則可能會在第二次運行時注意到相同的錯誤,因爲該端口尚未從第一次運行中釋放。

如果更改端口號並再次運行,它應該再次工作。

要處理這個問題,你應該看看here的討論。

我修改了你的程序,使用SO_REUSEADDR,它看起來可以和c++11選項一致。請注意,我在Ubuntu上運行,因此您的OS X行爲可能會有所不同。

#include <iostream> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 

using namespace std; 

int main() 
{ 
    cout << "Hello world, I'm going to open a socket on port 1234 now!" << endl; 

    int m_socket = 0; 
    int m_client = 0; 
    int port = 1236; 
    sockaddr_in serv_addr, client_addr; 
    socklen_t client_len; 


    m_socket = socket(AF_INET, SOCK_STREAM, 0); 
    memset(&serv_addr, 0, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr = INADDR_ANY; 
    serv_addr.sin_port = htons(port); 

    // Set to reuse 
    int so_reuseaddr = 1; 
    setsockopt(m_socket,SOL_SOCKET,SO_REUSEADDR, 
      &so_reuseaddr, 
      sizeof so_reuseaddr); 


    bind(m_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 
    cout << "Listen returns: " << listen(m_socket, SOMAXCONN) << endl; 
    m_client = accept(m_socket, (struct sockaddr *)&client_addr, &client_len); 
    write(m_client, "Numa numa\n", 10); 
    close(m_client); 
    close(m_socket); 

    return 0; 
} 
+0

我的確想到了這一點,如果我在兩次運行之間等待幾分鐘,同樣的情況發生,並且第一個版本連續運行沒有問題。我在兩次測試之間等待了30分鐘,但仍然發生。 –

+0

@ user3788528,您是否嘗試更改端口?新標準很可能會更改綁定的默認選項。 – merlin2011

+0

無論如何我都嘗試改變端口,沒有什麼區別,但正如我在上面發佈的那樣,我在偵聽之後添加了一個端口綁定的打印,並且它在C++ 11版本中不是1234,它似乎是一個隨機分配的端口。不知道爲什麼。 –