2016-11-16 28 views
0

這裏一切都很好,除了客戶端無法收到消息,或者服務器無法發送消息,我不知道。 我真的沒有什麼時間,所以我不能再浪費它來解決這個問題了,所以我轉向你們。只是我想(可能)你必須知道的一件事:服務器在我的網絡下,客戶端在我的學校網絡下。 P.S.服務器的不同IP是因爲我在NAT後面,沒有問題。C socket - recv:connection reset by peer

客戶端代碼

const char* IPSERVER = "87.21.70.136"; 

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

struct addrinfo hints, *serverInfo; 
int s; 

memset(&hints, 0, sizeof hints); 
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version 
hints.ai_socktype = SOCK_STREAM; 

if(getaddrinfo(IPSERVER, "50", &hints, &serverInfo) != 0){ 
    printf("Errore getaddrinfo(). Chiusura...\n"); 
    exit(-1); 
} 

s = socket(serverInfo->ai_family, serverInfo->ai_socktype, serverInfo->ai_protocol); 
printf("Porta: %d\n", ((struct sockaddr_in *) serverInfo->ai_addr)->sin_port); 

if(connect(s, serverInfo->ai_addr, serverInfo->ai_addrlen) < 0) 
    perror("Errore connect()"); 

char buf[2000]; 
int bytes_rec; 
if((bytes_rec = recv(s, buf, sizeof buf, 0)) < 0) 
    perror("Errore recv"); 
printf("%s\n",buf); 
close(s); 

return 0; 

} 

Server代碼

struct sockaddr_storage clientAddr; 
socklen_t addrSize; 
struct addrinfo hints, *myInfo; 
int s, newS; 

memset(&hints, 0, sizeof hints); 
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever 
hints.ai_socktype = SOCK_STREAM; 

if(getaddrinfo("192.168.1.2", "50", &hints, &myInfo) < 0) 
    perror("Errore getaddrinfo()"); 
printf("Porta: %d\n", ((struct sockaddr_in *) myInfo->ai_addr) -> sin_port); 
s = socket(myInfo->ai_family, myInfo->ai_socktype, myInfo->ai_protocol); 
if(s < 0) 
    perror("Errore socket()"); 
printf("Socket stabilita.\n"); 
if(bind(s, myInfo->ai_addr, myInfo->ai_addrlen) < 0) 
perror("Errore bind()"); 
printf("Porta creata.\n"); 
if(listen(s, 5) < 0) 
    perror("Errore listen()"); 
printf("Server in ascolto...\n"); 
addrSize = sizeof clientAddr; 
if((newS = accept(s, (struct sockaddr *)&clientAddr, &addrSize) < 0)) 
    perror("Errore accept()"); 
printf("Invio messaggio in corso...\n"); 
char *msg = "ciao, mi vedi?"; 
int len, bytes_sent; 
len = strlen(msg); 
if((bytes_sent = send(newS, msg, len, 0)) < 0) 
    perror("Errore send()"); 
printf("Messaggio inviato.\n"); 
closesocket(newS); 
closesocket(s); 
WSACleanup(); 

return 0; 

} 

客戶端輸出

Porta: 12800 
Errore recv: Connection reset by peer 

服務器輸出

Porta: 12800 
Socket stabilita. 
Porta creata. 
Server in ascolto... 
Invio messaggio in corso... 
Errore send(): No error 
Messaggio inviato. 

Process returned 0 (0x0) execution time : 4.789 s 
Press any key to continue. 
+0

當打印'sin_port'值,你需要使用'還有ntohs()'。以網絡字節順序表示12800爲50,因此在打印之前將其翻轉爲主機字節順序。此外,'recv()'不會空終止緩衝區,因此您必須手動執行該操作,或者更好地使用'%。* s',以便可以將'bytes_rec'傳遞給'printf()'。 –

+0

這裏真正的問題是在'send()'錯誤後'perror()'打印'沒有錯誤'。我懷疑更多的防火牆問題,因爲在你的[上一個問題使用相同的代碼](http://stackoverflow.com/questions/40600160/c-sockets-cannot-bind-server-public-ip)。注意如果您在系統調用中遇到錯誤,那麼繼續執行它幾乎是無效的,就好像它沒有發生過一樣。 'socket(),listen(),bind(),connect(),accept()'中的錯誤對這段代碼是致命的,並且應該導致退出或返回。 – EJP

+0

謝謝你們。我明天再試。 – UrbiJr

回答

1

服務器代碼顯然是使用Winsock API,這意味着服務器在Windows上運行。

Windows上的套接字句柄是無符號值,因此int是用於它們的錯誤數據類型。您需要改爲使用SOCKET類型。並且比較socket()accept()的返回值再次爲INVALID_SOCKET常數。

忽略套接字創建錯誤並將無效套接字傳遞到send()recv()可能會導致它們失敗。

perror()errno上運行,WinSock(和一般的Win32 API)不使用。這將解釋send()失敗後"no error"消息,因爲errno爲0.您必須使用WSAGetLastError()而不是在WinSock函數失敗時獲取WinSock錯誤代碼。

隨着中說,試試這個服務器代碼:

void psocketerror(const char *msg, int err) 
{ 
    fprintf(stderr, "%s: %d\n", msg, err); 
} 

void psocketerror(const char *msg) 
{ 
    psocketerror(msg, WSAgetLastError()); 
} 

int main(int argc, char *argv[]) 
{ 
    struct sockaddr_storage clientAddr; 
    socklen_t addrSize; 
    struct addrinfo hints, *myInfo; 
    SOCKET s, newS; 
    int err; 

    WSADATA wsa; 
    err = WSAStartup(MAKEWORD(2, 0), &wsa); 
    if (err != 0) { 
     psocketerror("Errore WSAStartup()", err); 
     return 1; 
    } 

    memset(&hints, 0, sizeof hints); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 

    if ((err = getaddrinfo("192.168.1.2", "50", &hints, &myInfo)) < 0) { 
     psocketerror("Errore getaddrinfo()", err); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Porta: %d\n", ntohs(((struct sockaddr_in *)(myInfo->ai_addr))->sin_port)); 

    s = socket(myInfo->ai_family, myInfo->ai_socktype, myInfo->ai_protocol); 
    if (s == INVALID_SOCKET) { 
     psocketerror("Errore socket()"); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Socket stabilita.\n"); 

    if (bind(s, myInfo->ai_addr, myInfo->ai_addrlen) < 0) { 
     psocketerror("Errore bind()"); 
     closesocket(s); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Porta creata.\n"); 

    if (listen(s, 5) < 0) { 
     psocketerror("Errore listen()"); 
     closesocket(s); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Server in ascolto...\n"); 

    addrSize = sizeof clientAddr; 
    newS = accept(s, (struct sockaddr *)&clientAddr, &addrSize) 
    if (newS === INVALID_SOCKET) { 
     psocketerror("Errore accept()"); 
     closesocket(s); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Invio messaggio in corso...\n"); 

    char *msg = "ciao, mi vedi?"; 
    int len, bytes_sent; 
    len = strlen(msg); 

    if ((bytes_sent = send(newS, msg, len, 0)) < 0) { 
     psocketerror("Errore send()"); 
     closesocket(newS); 
     closesocket(s); 
     WSACleanup(); 
     return 1; 
    } 

    printf("Messaggio inviato.\n"); 

    closesocket(newS); 
    closesocket(s); 

    WSACleanup(); 

    return 0; 
} 
+0

謝謝。我明天再試。 – UrbiJr

+0

我試過你的代碼,它的工作原理,謝謝。 Obv只需要用fprintf更改printfs。關於客戶,我注意到了%。*它打印隨機字符,而不是%s,而不用\ 0手動終止數組,它通常打印接收到的字符串。只是說。 – UrbiJr

+0

'%。* s'可以正常使用,例如:'printf(「%。* s \ n」,bytes_rec,buf);'。指定'%s'的精度告訴'printf()'要打印的最大字符數,因此緩衝區不需要以空值終止。 '*'只是告訴'printf()'從參數列表中獲取精度值,而不是格式字符串。如果您沒有爲'%s'指定精度,那麼緩衝區必須以空值終止,否則如果緩衝區不包含任何嵌入的空字符,它將超出緩衝區的範圍並從周圍的內存中打印數據。 –