С ++ Сокеты recv() системный вызов, возвращающий -1
У меня сейчас проблема с передачей сообщений между сервером и клиентом. Насколько я знаю, я правильно следую рекомендациям по программированию сокетов, изложенным в руководстве по программированию сокетов Beej.
Когда я запускаю два процесса, системный вызов recv() возвращает -1 (ошибка), а не количество полученных байтов. Кроме того, при попытке вывести buf, есть куча символов gobbledygook. Что имеет смысл, из-за ошибки.
Мне интересно, если кто-то может направить меня в правильном направлении, почему у меня возникают проблемы с recv()? Ниже приведены соответствующие фрагменты кода.
Сервер:
struct sockaddr_storage their_addr;
socklen_t addr_size;
int sockfd, newfd, byte_count, status;
char buf[512];
struct addrinfo hints, *res;
// first, load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// get address info, print stuff if error
if((status = getaddrinfo("nunki.usc.edu", "21957", &hints, &res)) !=0){
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
// make a socket:
if((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){
cout << "socket fail" << endl;
}
// bind the socket to the port
bind(sockfd, res->ai_addr, res->ai_addrlen);
// required output
cout << "Phase1: Login server has TCP port number " << "21957 "
<< "and IP address " << getIPfromHost("nunki.usc.edu") << endl;
// listen for incoming connections
listen(sockfd, 10);
cout << "after listen" << endl;
// halt until receipt
addr_size = sizeof(their_addr);
newfd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
cout << "after accept" << endl;
// Now that we're connected, we can receive some data
byte_count = recv(sockfd, buf, sizeof buf, 0);
printf("recv()'d %d bytes of data in buf\n", byte_count);
printf("Msg is %s\n", buf);
Клиент:
struct addrinfo hints, *res;
int sockfd;
// first, load up address structs with getaddrinfo():
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("nunki.usc.edu", "21957", &hints, &res);
// make a socket:
if((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){
cout << "socket fail" << endl;
}
// attempt connection to port
if(connect(sockfd, res->ai_addr, res->ai_addrlen) == -1){
cout << "connect fail" << endl;
}
// send message to server
cout << "sockfd " << sockfd << endl;
int byte_count = send(sockfd, "Hello", 5, 0);
cout << byte_count << endl;
Ниже приведен вывод для сервера:
Phase1: Login server has TCP port number 21957 and IP address 68.181.201.3
after listen
after accept
recv()'d -1 bytes of data in buf
Msg is ÿhÿ?sÈ
Glæ
Ниже приводится вывод для клиента:
sockfd 4
5
4 ответа
Ты звонишь recv
на неправильной розетке. Вам нужно recv
на newfd
:
byte_count = recv(newfd, buf, sizeof buf, 0); /* newfd instead of sockfd. */
Теперь, когда это не так,
Насколько я знаю, я должным образом следую рекомендациям для программирования сокетов
Я полностью не согласен.
- Вы не проверяете статусы возврата для
listen
,bind
,getaddrinfo
так далее - Нет
strerror
или жеperror
в вашей программе
Вы хотите, чтобы recv(), используя сокет, возвращенный от accept()
byte_count = recv(newfd, buf, sizeof buf, 0);
sockfd
просто используется для прослушивания клиентов, newfd
используется для передачи данных.
Может быть, я должен написать это не как ответ, а как комментарий. Тем не менее, ИМХО ваше использование getaddrinfo()
мне кажется неправильным
На стороне клиента он должен вызываться, а затем повторяться в результатах, пока не будет установлено соединение.
так
struct addrinfo * r2 sockfd = -1; for (r2=res; r2; r2=r2->ai_next) { // make a socket: if((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){ continue; // next result } // attempt connection to port if(connect(sockfd, res->ai_addr, res->ai_addrlen) == -1){ close(sockfd); sockfd = -1; continue; } } if (sockfd == -1) { // do error handling }
Таким образом, вы можете проверить все возможные соединения.
На стороне сервера это довольно необычно для использования
getaddrinfo()
, Обычно вы создаете сокет IPv6 и включаете его для прослушивания IPv4, используяsetsockopt()
сброситьIPV6_V6ONLY
флаг. Таким образом, сокет слушает как IPv6, так и IPv4. (Увы, не на Windows XP AFAIK.)