2013-09-26 5 views
1

У меня возникла проблема с связью клиент/сервер через сокет, когда я делаю последовательную отправку и после этого recv на клиенте. Пример:C++/Winsock TCP send/recv проблема между клиентом/сервером

Случай A:

Client    Server 
send(...);----------->While(recv(...)>0){ 
send(...);-----------> print(message); 
send(...);----------->} 
recv(...);----------->Send(...); 

Сервер принимает 3 сообщения и отправить последний ответ, но RECV на клиенте не удалось с SOCKET_ERROR с WSAGetLastError() значения 10060. Единственный способ сделать этот случай работать, когда я добавляю shutdown (..., SD_SEND) после последней отправки на клиент.

Почему у A есть такое поведение? и почему он работает только тогда, когда я добавляю команду shutdown()?

Но если я:

Случай B:

Client    Server 
send(...);----------->While(recv(...)>0){ 
recv(...);-----------> send(...); 
send(...);-----------> ... 
recv(...);-----------> ... 
send(...);-----------> ... 
recv(...);----------->} 

Он отлично работает, сервер/клиент получает и отправлять каждое сообщение.

Вот код для случая А: Клиент:

#include <iostream> 
#include <winsock2.h> 
#include <ws2tcpip.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib 
#pragma comment (lib, "Ws2_32.lib") 
#pragma comment (lib, "Mswsock.lib") 
#pragma comment (lib, "AdvApi32.lib") 

#define DEFAULT_BUFLEN 1024 
#define DEFAULT_PORT "27015" 

int main() { 
WSADATA wsaData; 
SOCKET ConnectSocket = INVALID_SOCKET; 
ADDRINFOA *ptr = NULL, *result = NULL, hints; 
char *ans, *sendbuf = "message\0"; 
char recvbuf[1024]; 
int iResult; 
int recvbuflen = DEFAULT_BUFLEN; 
ans=new char[1024]; 

// Initialize Winsock 
iResult = WSAStartup(MAKEWORD(2,2), &wsaData); 
printf("--> Initializing Winsock...\n*** Version: %s\n", wsaData.szDescription); 
if (iResult != 0) { 
    printf("*** Could not initialize Socket.\n*** Error code: %d", iResult); 
    return 1; 
} 

ZeroMemory(&hints, sizeof(hints)); 
hints.ai_family = AF_UNSPEC; 
hints.ai_socktype = SOCK_STREAM; 
hints.ai_protocol = IPPROTO_TCP; 

// Resolve the server address and port 
iResult = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result); 
printf("--> Setting server address...\n"); 
printf("--> local ip: 127.0.0.1 at port: %s...\n",DEFAULT_PORT); 
if (iResult != 0) { 
    printf("*** Error in setting server address.\n*** Error code: %d", iResult); 
    WSACleanup(); 
    return 1; 
} 

// Attempt to connect to an address until one succeeds 
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) { 
    // Create a SOCKET for connecting to server 
    printf("--> Creating client socket object...\n"); 
    ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); 
    if (ConnectSocket == INVALID_SOCKET) { 
     printf("*** Error creating socket.\n*** Error code: %d\n",WSAGetLastError()); 
     freeaddrinfo(result); 
     WSACleanup(); 
     return 1; 
    } 
    if(setsockopt(ConnectSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)new int(1000), sizeof(int))){ 
     WSACleanup(); 
     //strcpy(recvbuf, "EX_95"); 
     return -5; // Error setting recv timeout. 
    } 
    // Connect to server. 
    iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); 
    if (iResult == SOCKET_ERROR) { 
     closesocket(ConnectSocket); 
     ConnectSocket = INVALID_SOCKET; 
     continue; 
    } 
    printf("*** Client ready *** \n\n"); 
    break; 
} 

freeaddrinfo(result); 

if (ConnectSocket == INVALID_SOCKET) { 
    printf("*** Unable to connect to server!\n"); 
    WSACleanup(); 
    return 1; 
} 

// Send an initial buffer 
iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); 
if (iResult == SOCKET_ERROR) { 
    printf("*** Send failed: %d\n", WSAGetLastError()); 
    closesocket(ConnectSocket); 
    WSACleanup(); 
    return 1; 
} 

printf("<-- Bytes Sent: %ld\n", iResult); 

    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); 
if (iResult == SOCKET_ERROR) { 
    printf("*** Send failed: %d\n", WSAGetLastError()); 
    closesocket(ConnectSocket); 
    WSACleanup(); 
    return 1; 
} 

printf("<-- Bytes Sent: %ld\n", iResult); 


iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0); 
if (iResult == SOCKET_ERROR) { 
    printf("*** Send failed: %d\n", WSAGetLastError()); 
    closesocket(ConnectSocket); 
    WSACleanup(); 
    return 1; 
} 

printf("<-- Bytes Sent: %ld\n", iResult); 

// shutdown the connection since no more data will be sent 
/*iResult = shutdown(ConnectSocket, SD_SEND); 
if (iResult == SOCKET_ERROR) { 
    printf("shutdown failed with error: %d\n", WSAGetLastError()); 
    closesocket(ConnectSocket); 
    WSACleanup(); 
    return 1; 
}*/ 

iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0); 
if (iResult > 0) 
    printf("Bytes received: %d\n", iResult); 
else if (iResult == 0) 
    printf("Connection closed\n"); 
else 
    printf("recv failed with error: %d\n", WSAGetLastError()); 


// cleanup 
closesocket(ConnectSocket); 
WSACleanup(); 
system("pause"); 
return 0; 
} 

Сервер:

// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib 
#include <iostream> 
#include <Winsock2.h> 
#include <Ws2tcpip.h> 
#include <string> 

#pragma comment (lib, "Ws2_32.lib") 
#pragma comment (lib, "Mswsock.lib") 
#pragma comment (lib, "AdvApi32.lib") 

const char* DEFAULT_PORT = "27015"; 
const int DEFAULT_BUFLEN = 1024; 

using namespace std; 

int main(){ 
//Connection Variables. 
WSADATA wsaData; 
ADDRINFOA *result = NULL, hints; 
SOCKADDR *clientInfo = NULL; 
int iResult; 
//Receive Variables. 
char recvBuff[DEFAULT_BUFLEN]; 
int recvBuffLen = DEFAULT_BUFLEN; 
int iSendResult; 
//Server/Client sockets. 
SOCKET ListenSocket = INVALID_SOCKET; //SOCKET for the server to listen for client connections. 
SOCKET ClientSocket = INVALID_SOCKET; 

//Initialize Winsock 
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); 
printf("--> Initializing Winsock...\n*** Version: %s\n", wsaData.szDescription); 
if (iResult != 0){ 
    printf("*** Could not initialize Socket.\n*** Error code: %d", iResult); 
    return 1; 
} 
//Initialize hints allocated memory. 
ZeroMemory(&hints, sizeof(hints)); 

hints.ai_family = AF_INET;  //AF_INET is used to specify the IPv4 address family. 
hints.ai_socktype = SOCK_STREAM; //SOCK_STREAM is used to specify a stream socket. 
hints.ai_protocol = IPPROTO_TCP; //IPPROTO_TCP is used to specify the TCP protocol. 
hints.ai_flags = AI_PASSIVE; //AI_PASSIVE The socket address will be used in a call to the bind function. 

printf("--> Getting address info from server...\n"); 
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result); 
if(iResult != 0){ 
    printf("*** Error in getting address info from server.\n*** Error code: %d", iResult); 
    WSACleanup(); 
    return 2; 
} 

printf("--> Creating server socket object...\n"); 
//printf("Create socket to ip: %s at port: %s\n", inet_ntoa(((SOCKADDR_IN*)(result->ai_addr))->sin_addr),DEFAULT_PORT); 
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol); 
if(ListenSocket == INVALID_SOCKET){ 
    printf("*** Error creating socket.\n*** Error code: %d\n",WSAGetLastError()); 
    freeaddrinfo(result); 
    WSACleanup(); 
    return 3; 
} 

//BIND()->Associates a local address to a socket. 
printf("--> Bind listen object to local ip: 127.0.0.1 at port: %s...\n",DEFAULT_PORT); 
iResult = bind(ListenSocket, result->ai_addr, result->ai_addrlen); 
if(iResult == SOCKET_ERROR){ 
    printf("*** Binding failed.\n*** Error code: %d\n", WSAGetLastError()); 
    closesocket(ListenSocket); 
    freeaddrinfo(result); 
    WSACleanup(); 
    return 4; 
} 
freeaddrinfo(result); 

printf("*** Server ONLINE: Listening...\n\n"); 
iResult = listen(ListenSocket, SOMAXCONN); 
if(iResult == SOCKET_ERROR){ 
    printf("*** Failed start listening.\n*** Error code: %d\n",WSAGetLastError()); 
    closesocket(ListenSocket); 
    freeaddrinfo(result); 
    WSACleanup(); 
    return 5; 
} 

for(;1;){ 
    ClientSocket = accept(ListenSocket,clientInfo, NULL); 
    if(ClientSocket == INVALID_SOCKET){ 
     printf("*** Failed accepting connection from client.\n*** Error code: %d\n",WSAGetLastError()); 
     continue; 
    } 
    else{ 
     printf("--> Connection accepted from client.\n"); 
     iResult=1; 
     while(iResult > 0){ 
      iResult = recv(ClientSocket, recvBuff, recvBuffLen, 0); 
      if(iResult > 0){ 
       printf("--> Message received: %s\n--> Total: %d\n", recvBuff, iResult); 
      } 
     } 
     iSendResult = send(ClientSocket, "Answer\0", DEFAULT_BUFLEN, 0); 
     if(iSendResult == SOCKET_ERROR){ 
      printf("*** Sending data failed.\n*** Error code: %d\n",WSAGetLastError()); 
      continue; 
     } 
     else{ 
      printf("--> Sent: %d bytes\n", iSendResult); 
     } 
     printf("*** Closing connection... \n\n"); 
     iResult = shutdown(ClientSocket, SD_BOTH); 
     if(iResult == SOCKET_ERROR){ 
      printf("*** Shutdown client failed.\nError code: %d\n",WSAGetLastError()); 
      closesocket(ClientSocket); 
      WSACleanup(); 
      return 9; 
     } 
    } 
} 
closesocket(ClientSocket); 
WSACleanup(); 
system("pause"); 
return 0; 
} 

Заранее спасибо !!!! Nicolas Miranda S.

+1

Что такое valueOf WSAGetLastError(), когда вы получаете SOCKET_ERROR? – EJP

+0

Привет, EJP, спасибо за ответ. Значение WSAGetLastError() равно 10060 –

ответ

1

Сервер заблокирован внутри вызова recv, когда клиент ждет ответа и, следовательно, ничего не может отправить. Тайм-аут приемника составляет 1 секунду, поэтому через 1 секунду клиент генерирует ошибку тайм-аута (WSAETIMEDOUT == 10060).

Когда вы завершаете работу, вы указываете только SD_SEND, поэтому соединение не закрывается, но заставляет сервер выходить из recv, и поэтому он может отправить ответ.

Примечание: recv будет блокироваться, пока что-то не будет получено для сокета потока (SOCK_STREAM). Вы должны посмотреть на функцию select(), чтобы увидеть, как вы можете «заглянуть», есть ли данные до вызова recv.

Вот пример использования выберите:

fd_set fds; 
    timeval tv; 
    tv.tv_sec = 5000; 
    fds.fd_count = 1; 
    fds.fd_array[0] = ClientSocket; 

    int select_result = select(1, &fds, NULL, NULL, &tv); 

Если select_result == 0, был тайм-аут. В противном случае один из сокетов в fd_set готов с данными. Здесь есть только один, и он был указан как readfds в выбранном вызове.

Необходимо перенастроить приложение таким образом, чтобы у вас было какое-либо событие (например, получить третье сообщение или некоторый тайм-аут без какого-либо получения или что-то в самом сообщении), что заставляет сервер отправлять ответ. Вы можете использовать второй поток для ответов, но это выходит за рамки того, что я могу показать в коротком ответе.

+0

СПАСИБО ТАК МНОГО !!! Теперь он работает с вашим подходом = D –

Смежные вопросы