2011-09-06 73 views
1

我有在recv函數的使用問題Socket編程的C/C++ - recv函數在服務器

我已經從客戶端發送一些數據的應用程序,這些數據由服務器接收&發送掛起基於數據的響應。

當我發送少於16個請求時,實現正常工作。 但是,當我從客戶端發送超過16個請求(一個接一個)時,從服務器到第16個請求的響應都沒有問題,但在此之後服務器掛起。我可以看到數據是從客戶端傳輸的,但沒有被服務器接收。我正在使用功能recv

接收發生在僅從客戶端收到終止請求時退出的循環中。

服務器代碼:

// Request Winsock version 2.2 
    fprintf(stderr,"Performing WSAStartup\n"); 
    if ((retval = WSAStartup(0x202, &wsaData)) != 0) 
    { 
     fprintf(stderr,"FAILED with error %d\n", retval); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    if (port == 0) 
    { 
     Usage(argv[0]); 
    } 


    /* open socket connection */ 
    printf("Opening socket\n"); 
    local.sin_family = AF_INET; 
    local.sin_addr.s_addr = (!ip_address) ? INADDR_ANY:inet_addr(ip_address); 
    /* Port MUST be in Network Byte Order */ 
    local.sin_port = htons(port); 
    // TCP socket 
    listen_socket = socket(AF_INET, socket_type,0); 

    if (listen_socket == INVALID_SOCKET){ 
     fprintf(stderr,"socket() failed with error %d\n", WSAGetLastError()); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    // bind() associates a local address and port combination with the socket just created. 
    // This is most useful when the application is a 
    // server that has a well-known port that clients know about in advance. 
    printf("Bind address and port to socket\n"); 
    if (bind(listen_socket, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR) 
    { 
     fprintf(stderr,"bind() failed with error %d\n", WSAGetLastError()); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    // So far, everything we did was applicable to TCP as well as UDP. 
    // However, there are certain steps that do not work when the server is 
    // using UDP. We cannot listen() on a UDP socket. 
    if (socket_type != SOCK_DGRAM) 
    { 
     printf("TCP: listening on socket\n"); 
     if (listen(listen_socket,5) == SOCKET_ERROR) 
     { 
     fprintf(stderr,"listen() failed with error %d\n", WSAGetLastError()); 
     WSACleanup(); 

     return -1; 
     } 
     else 
     { 
     printf("OK\n"); 

     } 
    } 


//Perform Applcation task 
//initisations 

    printf("Server is listening and waiting for a connection\non port %d, protocol %s\n",port, (socket_type == SOCK_STREAM)?"TCP":"UDP"); 

    executeServer = 1; 
    while(executeServer == 1) 

    { 
     fromlen =sizeof(from); 
     // accept() doesn't make sense on UDP, since we do not listen() 
     if (socket_type != SOCK_DGRAM) 
     { 
     printf("TCP: Waiting for connection (accept())\n"); 
     msgsock = accept(listen_socket, (struct sockaddr*)&from, &fromlen); 
     if (msgsock == INVALID_SOCKET) 
     { 
      fprintf(stderr,"accept() error %d\n", WSAGetLastError()); 
      WSACleanup(); 
      return -1; 
     } 
     else 
     { 
      printf("OK\n"); 
      printf("accepted connection from %s, port %d\n", inet_ntoa(from.sin_addr), htons(from.sin_port)) ; 
     } 
     } 
     else 
     { 
     msgsock = listen_socket; 
     } 

     // In the case of SOCK_STREAM, the server can do recv() and send() on 
     // the accepted socket and then close it. 

     // However, for SOCK_DGRAM (UDP), the server will do recvfrom() and sendto() in a loop. 

     printf("Receiving data"); 
     if (socket_type != SOCK_DGRAM) 
     { 
     retval = recv(msgsock, Buffer, sizeof(Buffer), 0); 
     } 
     else 
     { 
     retval = recvfrom(msgsock,Buffer, sizeof(Buffer), 0, (struct sockaddr *)&from, &fromlen); 
     printf("Received datagram from %s\n", inet_ntoa(from.sin_addr)); 

     } 


     if (retval == SOCKET_ERROR) 
     { 
     fprintf(stderr,"recv() failed: error %d\n", WSAGetLastError()); 
     closesocket(msgsock); 
     return -2; 
     } 
     else 
     { 
     printf("OK\n"); 
     } 

     if (retval == 0) 
     { 
     printf("Client closed connection.\n"); 
     closesocket(msgsock); 

     } 
     else 
     { 
     printf("Received %d bytes, data \"%s\" from client\n", retval, Buffer); 
     } 

     printf("Processing Data\n"); 
     if (!stricmp(Buffer, "exit")) 
     { 
     wsprintf(AckBuffer,"ACK"); 
     executeServer = 0; 
     } 
     else 
     { 
     // Perform use task here based on recieved data 

     } 


     printf("Sending answer to client\n"); 
     if (socket_type != SOCK_DGRAM) 
     { 
     retval = send(msgsock, AckBuffer, sizeof(AckBuffer), 0); 
     } 
     else 
     { 
     retval = sendto(msgsock, AckBuffer, sizeof(AckBuffer), 0, (struct sockaddr *)&from, fromlen); 
     } 


     if (retval == SOCKET_ERROR) 
     { 
     fprintf(stderr,"send() failed: error %d\n", WSAGetLastError()); 
     } 
     else 
     { 
     printf("OK\n"); 
     } 

     /* close TCP connection */ 
     if (socket_type != SOCK_DGRAM) 
     { 
     closesocket(msgsock); 
     } 


    } 
    printf("terminating server\n"); 
    closesocket(msgsock); 
    WSACleanup(); 

客戶端代碼:

fprintf(stderr,"Performing WSAStartup"); 
    if ((retval = WSAStartup(0x202, &wsaData)) != 0) 
    { 

     fprintf(stderr,"WSAStartup() failed with error %d\n", retval); 
     WSACleanup(); 

     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    if (port == 0) 

    { 
     Usage(argv[0]); 
    } 

    // Attempt to detect if we should call gethostbyname() or gethostbyaddr() 
    printf("Translate hastname to address -> gethostbyaddr()\n"); 
    if (isalpha(server_name[0])) 
    { // server address is a name 
     hp = gethostbyname(server_name); 
    } 
    else 
    { // Convert nnn.nnn address to a usable one 
     addr = inet_addr(server_name); 
     hp = gethostbyaddr((char *)&addr, 4, AF_INET); 
    } 
    if (hp == NULL) 
    { 
     fprintf(stderr,"Cannot resolve address \"%s\": Error %d\n", server_name, WSAGetLastError()); 
     WSACleanup(); 
     exit(1); 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    // Copy the resolved information into the sockaddr_in structure 
    printf("Opening socket\n"); 
    memset(&server, 0, sizeof(server)); 
    memcpy(&(server.sin_addr), hp->h_addr, hp->h_length); 
    server.sin_family = hp->h_addrtype; 
    server.sin_port = htons(port); 

    conn_socket = socket(AF_INET, socket_type, 0); /* Open a socket */ 
    if (conn_socket <0) 
    { 
     fprintf(stderr,"Error Opening socket: Error %d\n", WSAGetLastError()); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    // Notice that nothing in this code is specific to whether we 
    // are using UDP or TCP. 
    // We achieve this by using a simple trick. 
    // When connect() is called on a datagram socket, it does not 
    // actually establish the connection as a stream (TCP) socket 
    // would. Instead, TCP/IP establishes the remote half of the 
    // (LocalIPAddress, LocalPort, RemoteIP, RemotePort) mapping. 
    // This enables us to use send() and recv() on datagram sockets, 
    // instead of recvfrom() and sendto() 
    printf("Client connecting to: %s.\n", hp->h_name); 
    if (connect(conn_socket, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) 
    { 
     fprintf(stderr,"connect() failed: %d\n", WSAGetLastError()); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    /* copy options string to buffer */  
    strcpy(Buffer,Options); 
    printf("Sending Data \"%s\"\n", Buffer); 
    retval = send(conn_socket, Buffer, sizeof(Buffer), 0); 
    if (retval == SOCKET_ERROR) 
    { 
     fprintf(stderr,"send() failed: error %d.\n", WSAGetLastError()); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 

    printf("Receiving status from server\n"); 
    retval = recv(conn_socket, Buffer, sizeof(Buffer), 0); 
    if (retval == SOCKET_ERROR) 
    { 
     fprintf(stderr,"recv() failed: error %d.\n", WSAGetLastError()); 
     closesocket(conn_socket); 
     WSACleanup(); 
     return -1; 
    } 
    else 
    { 
     printf("OK\n"); 
    } 


    // We are not likely to see this with UDP, since there is no 
    // 'connection' established. 
    if (retval == 0) 
    { 
     printf("Client: Server closed connection.\n"); 
     closesocket(conn_socket); 
     WSACleanup(); 
     return -1; 
    } 

    printf("Received %d bytes, data \"%s\" from server.\n", retval, Buffer); 
    closesocket(conn_socket); 
    WSACleanup(); 
+5

沒有代碼,沒有幫助。 –

+0

什麼操作系統? TCP還是UDP? – selbie

+0

@Mitch Wheat:添加代碼.. – hemanth

回答

1

如果使用recv不使你的插座非阻塞模式,你recv是做了正確的事情。它阻塞,直到它有東西可讀。

[UPDATE]

從代碼中,你的確在使用阻塞套接字。我建議你至少爲你的服務器使用非阻塞套接字。我相信你可以很容易地谷歌如何使非阻塞套接字和處理Windows中的異步IO。祝你好運!

[UPDATE2]

首先,你的服務器代碼似乎一旦關閉連接recv讀的東西。由於TCP不關心數據邊界,因此不能關閉連接。記住一個send在客戶端的呼叫可能需要在服務器中使用TCP進行多個recv呼叫,反之亦然。

對於您的具體問題,我敢肯定沒有什麼可讀的,這就是爲什麼recv阻止您的程序。確保你的客戶端真的發送成功。

+0

請求在服務器中正確接收〜16次之後服務器中沒有接收發生:( – hemanth

+0

感謝您的反饋。 。我是套接字編程的新手。[UPDATE2]:只有在接收錯誤或零字節接收時連接纔會關閉。對於大約16條命令,整個東西看起來工作正常,之後沒有接收服務器端,客戶端似乎沒問題,並且沒有錯誤報告此傳輸失敗的情況。 – hemanth

+1

嗯,但你在發送服務器後關閉套接字/ *關閉TCP連接*/ if(socket_type!= SOCK_DGRAM) { closesocket(msgsock); } – young