向多个客户端 UDP 发送数据时不支持地址族
Address family not supported when sending data to multiple clients UDP
我目前正在 linux 中编写程序,其中:
客户端向服务器发送一个"password"
服务器等待n个人发送密码并记录发送者地址
收到n条消息后,发送一条起始消息给发送者。
问题是当我尝试将 "start" 发送回客户端时出现非法搜索错误(发送到错误:非法搜索)。并且只有第一个客户端收到启动消息(clientaddrs[0]
)
注意:经过30分钟的测试,错误现在变成:Address family not supported by protocol。(代码中绝对没有任何变化)
这是我的代码(我粘贴了一个最小的可重现示例)
重现问题:
运行 带有参数的服务器代码:8080
在提示符下输入 2
运行 两个不同参数的客户端代码:127.0.0.1 8080
选择两个不同的密码并在客户端提示时输入它们
服务器代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <chrono>
#include <errno.h>
#include <ifaddrs.h>
#define TO_CLI_BUF_SIZE 32
#define FROM_CLI_BUF_SIZE 8
void printAddr(){ // for printing my ip
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
} else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
}
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
}
int main(int argc, char ** argv){
//seed rand
srand(time(NULL));
int sockfd; // socket
int port; // my port to listen on
struct sockaddr_in serveraddr; // server's address
struct sockaddr_in clientaddrs[4];
socklen_t clientLens[4];
int currentAddrMax = 0;
struct hostent * hostp; //host info
char * hostaddrp; // host adddr string
char toClientBuf[TO_CLI_BUF_SIZE];
char fromClientBuf[FROM_CLI_BUF_SIZE];
if(argc != 2){
perror("usage: file <port>");
exit(1);
}
port = atoi(argv[1]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0){
perror("ERROR: opening socket.");
exit(1);
}
//int option = 1;
//setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&option, sizeof(int));
//internet stuff
bzero((char*) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("ERROR on bind");
exit(1);
}
int playerKeys[4];
int playerJoined[4];
printf("(you can enter 1,2,3 or 4)\n");
printf("Enter amount of players: \n");
int amountPlayers = 0;
scanf("%d",&amountPlayers);
// hacky way to clear screen
printf("3[H3[J");
printAddr();
printf("PORT: %d\n", port);
printf("player| key| in\n");
for(int i = 0; i < 4; i++){
bool keyExists = true;
while (keyExists == true ){
playerKeys[i] = rand()%10000;
keyExists = false;
for(int j = 0; j < i; j++){
if(playerKeys[i] == playerKeys[j]){
keyExists = true;
}
}
}
printf("%d |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" o\n");
}else{
printf(" x\n");
}
fflush(stdin);
}
for(int i = 0; i < amountPlayers;i++){
bzero(fromClientBuf, FROM_CLI_BUF_SIZE);
int n = recvfrom(sockfd, fromClientBuf,FROM_CLI_BUF_SIZE, 0, (struct sockaddr*) &clientaddrs[currentAddrMax], &(clientLens[currentAddrMax]));
//TODO store senders
if(n>0){
int key = (fromClientBuf[0]-'0')*1000; //TODO change the way keys are extracted.
key += (fromClientBuf[1]-'0')*100;
key += (fromClientBuf[2]-'0')*10;
key += (fromClientBuf[3]-'0');
for (int i = 0; i < 4; i++){
if(playerKeys[i] == key && playerJoined[i] == 0){
playerJoined[i] = 1;
currentAddrMax++;
}
}
printf("3[H3[J");
printAddr();
printf("PORT: %d\n", port);
printf("player| key| in\n");
for(int i = 0; i < 4; i++){
printf("%d |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" o\n");
}else{
printf(" x\n");
}
}
// decode key
}
}
//TODO finished waiting for all senders. send them start signal
//MAY BE USEFULL:n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddr, clientlen);
strcpy(toClientBuf, "start");
for(int j = 0; j < currentAddrMax; j++){
int n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddrs[j], (clientLens[j]));
if(n < 0) {
perror("ERROR in sendto");
printf("%d\n",j);
exit(1);
}
}
// wait for connections
//main loop
//set some options
struct timeval read_timeout;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 100;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,&read_timeout,sizeof(read_timeout)) < 0) {
perror("Error with options");
}
printf("start loop\n");
return 0;
}
客户代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define FROM_SER_BUF_SIZE 32
#define TO_SER_BUF_SIZE 8
int main(int argc, char **argv){
int sockfd, portno, n;
socklen_t serverlen;
struct sockaddr_in serveraddr;
struct hostent *server;
char *hostname;
char toServerBuf[TO_SER_BUF_SIZE];
char fromServerBuf[FROM_SER_BUF_SIZE];
if (argc != 3) {
perror("usage: filename <hostname> <port>\n");
exit(0);
}
hostname = argv[1];
portno = atoi(argv[2]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("ERROR: opening sockets\n");
exit(0);
}
// get host
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
exit(0);
}
// build server's internet address
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
serveraddr.sin_port = htons(portno);
bzero(toServerBuf, TO_SER_BUF_SIZE);
int key = 0;
printf("Please enter your key: ");
scanf("%d",&key);
if(key > 9999 || key < 0){
printf("INVALID KEY\n");
exit(1);
}
toServerBuf[0] = key/1000 + '0';
toServerBuf[1] = (key%1000)/100 + '0';
toServerBuf[2] = (key%100)/10 +'0';
toServerBuf[3] = key%10 + '0';
serverlen = sizeof(serveraddr);
n = sendto(sockfd, toServerBuf, strlen(toServerBuf), 0, ( struct sockaddr *) &serveraddr, serverlen);
if (n < 0){
perror("ERROR: sendto\n");
exit(0);
}
//TODO wait for server to get send start signal
bzero(fromServerBuf, FROM_SER_BUF_SIZE);
n = recvfrom(sockfd, fromServerBuf, FROM_SER_BUF_SIZE, 0,( struct sockaddr *) &serveraddr, &serverlen);
printf("wow we got here");
return 0;
}
原来是g++的问题。但是,不知道为什么 g++ 不起作用,一旦我切换到 clang,所有错误都消失了。但是,clang++ 也不起作用。
编辑:用 valgrind 发现的 clientaddr 长度有问题。结果在客户端长度的数组中(以我的示例为例),我们必须首先使用
行对其进行初始化
clientLens[currentAddrMax] = sizeof(clientaddrs[currentAddrMax]);
正如 Bodo 提到的,我们必须始终初始化客户端长度,否则将出现未指定的行为。
我目前正在 linux 中编写程序,其中:
客户端向服务器发送一个"password"
服务器等待n个人发送密码并记录发送者地址
收到n条消息后,发送一条起始消息给发送者。
问题是当我尝试将 "start" 发送回客户端时出现非法搜索错误(发送到错误:非法搜索)。并且只有第一个客户端收到启动消息(clientaddrs[0]
)
注意:经过30分钟的测试,错误现在变成:Address family not supported by protocol。(代码中绝对没有任何变化)
这是我的代码(我粘贴了一个最小的可重现示例)
重现问题:
运行 带有参数的服务器代码:8080
在提示符下输入 2
运行 两个不同参数的客户端代码:127.0.0.1 8080
选择两个不同的密码并在客户端提示时输入它们
服务器代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <chrono>
#include <errno.h>
#include <ifaddrs.h>
#define TO_CLI_BUF_SIZE 32
#define FROM_CLI_BUF_SIZE 8
void printAddr(){ // for printing my ip
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_addr) {
continue;
}
if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
} else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
// is a valid IP6 Address
tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
char addressBuffer[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
}
}
if (ifAddrStruct!=NULL) freeifaddrs(ifAddrStruct);
}
int main(int argc, char ** argv){
//seed rand
srand(time(NULL));
int sockfd; // socket
int port; // my port to listen on
struct sockaddr_in serveraddr; // server's address
struct sockaddr_in clientaddrs[4];
socklen_t clientLens[4];
int currentAddrMax = 0;
struct hostent * hostp; //host info
char * hostaddrp; // host adddr string
char toClientBuf[TO_CLI_BUF_SIZE];
char fromClientBuf[FROM_CLI_BUF_SIZE];
if(argc != 2){
perror("usage: file <port>");
exit(1);
}
port = atoi(argv[1]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd<0){
perror("ERROR: opening socket.");
exit(1);
}
//int option = 1;
//setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&option, sizeof(int));
//internet stuff
bzero((char*) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0){
perror("ERROR on bind");
exit(1);
}
int playerKeys[4];
int playerJoined[4];
printf("(you can enter 1,2,3 or 4)\n");
printf("Enter amount of players: \n");
int amountPlayers = 0;
scanf("%d",&amountPlayers);
// hacky way to clear screen
printf("3[H3[J");
printAddr();
printf("PORT: %d\n", port);
printf("player| key| in\n");
for(int i = 0; i < 4; i++){
bool keyExists = true;
while (keyExists == true ){
playerKeys[i] = rand()%10000;
keyExists = false;
for(int j = 0; j < i; j++){
if(playerKeys[i] == playerKeys[j]){
keyExists = true;
}
}
}
printf("%d |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" o\n");
}else{
printf(" x\n");
}
fflush(stdin);
}
for(int i = 0; i < amountPlayers;i++){
bzero(fromClientBuf, FROM_CLI_BUF_SIZE);
int n = recvfrom(sockfd, fromClientBuf,FROM_CLI_BUF_SIZE, 0, (struct sockaddr*) &clientaddrs[currentAddrMax], &(clientLens[currentAddrMax]));
//TODO store senders
if(n>0){
int key = (fromClientBuf[0]-'0')*1000; //TODO change the way keys are extracted.
key += (fromClientBuf[1]-'0')*100;
key += (fromClientBuf[2]-'0')*10;
key += (fromClientBuf[3]-'0');
for (int i = 0; i < 4; i++){
if(playerKeys[i] == key && playerJoined[i] == 0){
playerJoined[i] = 1;
currentAddrMax++;
}
}
printf("3[H3[J");
printAddr();
printf("PORT: %d\n", port);
printf("player| key| in\n");
for(int i = 0; i < 4; i++){
printf("%d |%04d|",i+1, playerKeys[i]);
if(playerJoined[i] == 1){
printf(" o\n");
}else{
printf(" x\n");
}
}
// decode key
}
}
//TODO finished waiting for all senders. send them start signal
//MAY BE USEFULL:n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddr, clientlen);
strcpy(toClientBuf, "start");
for(int j = 0; j < currentAddrMax; j++){
int n = sendto(sockfd, toClientBuf, strlen(toClientBuf), 0, (struct sockaddr *) &clientaddrs[j], (clientLens[j]));
if(n < 0) {
perror("ERROR in sendto");
printf("%d\n",j);
exit(1);
}
}
// wait for connections
//main loop
//set some options
struct timeval read_timeout;
read_timeout.tv_sec = 0;
read_timeout.tv_usec = 100;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,&read_timeout,sizeof(read_timeout)) < 0) {
perror("Error with options");
}
printf("start loop\n");
return 0;
}
客户代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define FROM_SER_BUF_SIZE 32
#define TO_SER_BUF_SIZE 8
int main(int argc, char **argv){
int sockfd, portno, n;
socklen_t serverlen;
struct sockaddr_in serveraddr;
struct hostent *server;
char *hostname;
char toServerBuf[TO_SER_BUF_SIZE];
char fromServerBuf[FROM_SER_BUF_SIZE];
if (argc != 3) {
perror("usage: filename <hostname> <port>\n");
exit(0);
}
hostname = argv[1];
portno = atoi(argv[2]);
// create socket
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("ERROR: opening sockets\n");
exit(0);
}
// get host
server = gethostbyname(hostname);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host as %s\n", hostname);
exit(0);
}
// build server's internet address
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serveraddr.sin_addr.s_addr, server->h_length);
serveraddr.sin_port = htons(portno);
bzero(toServerBuf, TO_SER_BUF_SIZE);
int key = 0;
printf("Please enter your key: ");
scanf("%d",&key);
if(key > 9999 || key < 0){
printf("INVALID KEY\n");
exit(1);
}
toServerBuf[0] = key/1000 + '0';
toServerBuf[1] = (key%1000)/100 + '0';
toServerBuf[2] = (key%100)/10 +'0';
toServerBuf[3] = key%10 + '0';
serverlen = sizeof(serveraddr);
n = sendto(sockfd, toServerBuf, strlen(toServerBuf), 0, ( struct sockaddr *) &serveraddr, serverlen);
if (n < 0){
perror("ERROR: sendto\n");
exit(0);
}
//TODO wait for server to get send start signal
bzero(fromServerBuf, FROM_SER_BUF_SIZE);
n = recvfrom(sockfd, fromServerBuf, FROM_SER_BUF_SIZE, 0,( struct sockaddr *) &serveraddr, &serverlen);
printf("wow we got here");
return 0;
}
原来是g++的问题。但是,不知道为什么 g++ 不起作用,一旦我切换到 clang,所有错误都消失了。但是,clang++ 也不起作用。
编辑:用 valgrind 发现的 clientaddr 长度有问题。结果在客户端长度的数组中(以我的示例为例),我们必须首先使用
行对其进行初始化clientLens[currentAddrMax] = sizeof(clientaddrs[currentAddrMax]);
正如 Bodo 提到的,我们必须始终初始化客户端长度,否则将出现未指定的行为。