UDP套接字:服务器向客户端发送文件 协议族不支持的地址族
UDP socket: server sending file to client Address family not supported by protocol family
我只是套接字编程的初学者,目前正在开发一个使用 UDP 处理文件传输的小程序。这个程序是用C写的。
这是我的问题:
UDP 服务器将首先使用 recvfrom() 函数从 UDP 客户端捕获消息,以便开始发送文件。我先发了文件名,发不过去,报错信息是:Address family not supported by protocol family。我检查了client_addr的sin_family,是7。此外,在我尝试设置client_addr.sin_family = AF_INET之后;服务器工作正常,除了客户端甚至无法接收任何消息。
我查了一些资料,但碰巧不是很有帮助,如果有人知道为什么并愿意告诉我,请告诉我。感谢您的帮助。
下面是服务器代码的一小部分:
int socketfd;
/* my address information */
struct sockaddr_in server_addr;
/* connector’s address information */
struct sockaddr_in client_addr;
socklen_t clientLength;
int numbytes;
char buffer[BUFLEN];
int portNum = atoi(port);
time_t timer;
char charfileSize[20];
int percent, count = 0;
struct tm *tm_info;
char timeBuf[30];
if((socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("Server-socket() sockfd error lol!");
exit(1);
} else {
printf("Server-socket() sockfd is OK...\n");
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portNum);
server_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(server_addr.sin_zero), 0, 8);
// bind the socket to the server ip address
if(bind(socketfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
perror("server socket bind to IP error lol!\n");
exit(1);
} else {
printf("successfully bind server ip with socket ...\n");
}
// client_addr.sin_port = htons(portNum);
client_addr.sin_family = AF_INET;
//* for ensuring client connection *//
int tempGet;
char tempBuf[BUFLEN];
//if( (tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength)) > 0 ) {
tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength);
if( strcmp( tempBuf, "send file" ) == 0) {
printf("Can start transferring file...\n");
}
printf("sin family:%d\n", client_addr.sin_family);
FILE *fp = fopen(filename, "rb");
if ( !fp ) {
perror("Error opening the file\n");
exit(1);
} else {
// successfully opened the file that's going to be transferred
printf("file opened: %s\n", filename);
// get file size
int file_block_size = 0;
bzero(buffer, BUFLEN);
long file_size;
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
// goes back to the beginning of file(fp)
rewind(fp);
printf("file name: %s\n", filename);
printf("file size: %ld kb\n", file_size/1024);
// get time
time(&timer);
clientLength = sizeof(client_addr);
//client_addr.sin_family = AF_INET;
int sendFileName;
// length of file name
if( (sendFileName = sendto(socketfd, filename, strlen(filename), 0, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in))) >= 0) {
printf("file name sent.\n");
} else {
//printf("%d\n", sendFileName);
perror("file name send error.\n");
exit(1);
}
}
recvfrom
的最后一个参数采用需要用 sockaddr 参数的大小初始化的 socklen_t
的地址。我没有看到 clientLength
在此调用之前初始化,因此当此函数 returns 时 client_addr
可能未正确更新。这会导致对 sendto
的后续调用失败。
如果您在调用 recvfrom
之前初始化 clientLength
,这将解决问题。
clientLength = sizeof(client_addr);
tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength);
我对 Stack Overflow 上所有不正确和半正确的 UDP 套接字程序尝试感到有点厌倦。我决定做参考UDP实现,以这道题为例。下次看到TCP问题(有时间和精力)我会做参考TCP实现。
此实现正确地执行了所有操作(据我所知:)。它使用 BSD 套接字,因此 Windows 实现将不得不进行微小的更改。除此之外,应该都是符合C-99的。
错误处理:应用程序检查可能的错误,但不尝试更正它们,只是简单地报告。众所周知,在某些错误情况下会泄漏套接字(这会导致应用程序关闭),但在这种情况下添加适当的套接字关闭只会保证更多代码,而实际上并没有增加很多好处。
编译:
gcc -std=c99 -g -D_POSIX_SOURCE -Wall -Werror -pedantic -o test_udp send_udp.c
用法:./test_udp 服务器 或 ./test_udp <主机名> <文件名>
看哪:我给你test_udp.c.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
const short port = 4321;
int run_server();
int send_file(const char* const server, const char* const file);
int main(int argc, char* argv[]) {
if (argc < 2 || argc > 3) {
printf("Error: Usage send_udp (server|<server_host>) [<file name>]\n");
return 1;
}
if (!strcmp(argv[1], "server"))
return run_server(); // runs forever
if (argc != 3) {
printf("Error: client mode accepts two arguments: server and file name.\n");
return 1;
}
return send_file(argv[1], argv[2]);
}
int run_server() {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
printf("run_server(): error creating socket: %s\n", strerror(errno));
return 2;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {
printf("run_server(): error binding socket: %s\n", strerror(errno));
return 2;
}
printf("run_server(): socket created and bound, entering endless loop.\n");
struct sockaddr_in incoming;
socklen_t incoming_sz = sizeof(incoming);
char buff[USHRT_MAX]; // maximum size of UDP datagramm
const char* buff_ptr = buff;
while (1) {
ssize_t msg_size = recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr*)(&incoming), &incoming_sz);
if (msg_size == -1) {
printf("run_server(): error receiving message: %s\n", strerror(errno));
return 3;
}
if (msg_size == 0) {
printf("run_server(): a message of 0 size received from the client, bogus. Skipping, continue to listen.\n");
continue;
}
// Message structure: unsigned 16 bit length of file name (network order), followed by filename, followed by data
uint16_t fname_len;
if (msg_size < sizeof(fname_len)) {
printf("run_server(): Bogus (too short) message received from the client. Skipping, continue to listen.\n");
continue;
}
memcpy(&fname_len, buff_ptr, sizeof(fname_len));
fname_len = ntohs(fname_len);
buff_ptr += sizeof(fname_len);
msg_size -= sizeof(fname_len);
if (msg_size < fname_len) {
printf("run_server(): Bogus (too short) message received from the client. Skipping, continue to listen.\n");
continue;
}
char file_name[fname_len + 1];
strncpy(file_name, buff_ptr, fname_len);
file_name[fname_len] = '[=11=]';
buff_ptr += fname_len;
msg_size -= fname_len;
printf("run_server(): incoming transfer for file %s, intrinsic file size: %zu\n", file_name, msg_size);
FILE* f = fopen(file_name, "wb");
if (!f) {
printf("run_server(): Could not open file for writing. Skipping the message, carrying on.\n");
continue;
}
size_t written = fwrite(buff_ptr, msg_size, 1, f);
if (written != 1)
printf("run_server(): error, could not write whole file.\n");
else
printf("run_server(): incoming file written successfully.\n");
fclose(f);
}
return 0;
}
int send_file(const char* const server, const char* const file) {
uint16_t fname_len = strlen(file);
uint16_t max_short = 0;
max_short = ~max_short;
if (fname_len > (max_short - sizeof(fname_len))) {
printf("send_file(): file name is toooo large. Can't send this file.\n");
return 2;
}
FILE* f = fopen(file, "rb");
if (!f) {
printf("send_file(): Could not open file for reading. Nothing sent.\n");
return 3;
}
fseek(f, 0, SEEK_END);
unsigned long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
if (fsize > max_short - sizeof(fname_len) - fname_len) {
printf("send_file(): file is toooo large. Can't send this file.\n");
fclose(f);
return 2;
}
char buff[sizeof(fname_len) + fname_len + fsize];
char* buff_ptr = buff;
uint16_t net_fname_len = htons(fname_len);
memcpy(buff_ptr, &net_fname_len, sizeof(net_fname_len));
buff_ptr += sizeof(net_fname_len);
memcpy(buff_ptr, file, fname_len);
buff_ptr += fname_len;
size_t rc = fread(buff_ptr, fsize, 1, f);
if (rc != 1) {
printf("send_file(): Could not read whole file. Error.\n");
fclose(f);
return 3;
}
fclose(f);
struct addrinfo* ainfo;
if (getaddrinfo(server, NULL, NULL, &ainfo) != 0) {
printf("send_file(): Unknown host %s.\n", server);
return 3;
}
struct addrinfo* ainfo_begin = ainfo;
// I will take the first IP v4 entry in possible addressess
while (ainfo->ai_family != AF_INET && ainfo->ai_next)
ainfo = ainfo->ai_next;
if (ainfo->ai_family != AF_INET) {
printf("send_file(): Couldn't resolve host %s to AF_INET address.\n", server);
return 3;
}
int addr = ((struct sockaddr_in*)(ainfo->ai_addr))->sin_addr.s_addr;
freeaddrinfo(ainfo_begin);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
printf("read_file(): error creating socket: %s\n", strerror(errno));
return 2;
}
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(port);
remote_addr.sin_addr.s_addr = addr;
ssize_t sent = sendto(sock, buff, sizeof(buff), 0, (struct sockaddr*)(&remote_addr), sizeof(remote_addr));
if (sent != sizeof(buff))
printf("read_file(): error sending message over socket: %s, only %zu bytes sent.\n", strerror(errno), sent);
else
printf("read_file(): file sent successfully.\n");
return sent == sizeof(buff) ? 0 : 3;
}
我只是套接字编程的初学者,目前正在开发一个使用 UDP 处理文件传输的小程序。这个程序是用C写的。
这是我的问题:
UDP 服务器将首先使用 recvfrom() 函数从 UDP 客户端捕获消息,以便开始发送文件。我先发了文件名,发不过去,报错信息是:Address family not supported by protocol family。我检查了client_addr的sin_family,是7。此外,在我尝试设置client_addr.sin_family = AF_INET之后;服务器工作正常,除了客户端甚至无法接收任何消息。
我查了一些资料,但碰巧不是很有帮助,如果有人知道为什么并愿意告诉我,请告诉我。感谢您的帮助。
下面是服务器代码的一小部分:
int socketfd;
/* my address information */
struct sockaddr_in server_addr;
/* connector’s address information */
struct sockaddr_in client_addr;
socklen_t clientLength;
int numbytes;
char buffer[BUFLEN];
int portNum = atoi(port);
time_t timer;
char charfileSize[20];
int percent, count = 0;
struct tm *tm_info;
char timeBuf[30];
if((socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("Server-socket() sockfd error lol!");
exit(1);
} else {
printf("Server-socket() sockfd is OK...\n");
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portNum);
server_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(server_addr.sin_zero), 0, 8);
// bind the socket to the server ip address
if(bind(socketfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
perror("server socket bind to IP error lol!\n");
exit(1);
} else {
printf("successfully bind server ip with socket ...\n");
}
// client_addr.sin_port = htons(portNum);
client_addr.sin_family = AF_INET;
//* for ensuring client connection *//
int tempGet;
char tempBuf[BUFLEN];
//if( (tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength)) > 0 ) {
tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength);
if( strcmp( tempBuf, "send file" ) == 0) {
printf("Can start transferring file...\n");
}
printf("sin family:%d\n", client_addr.sin_family);
FILE *fp = fopen(filename, "rb");
if ( !fp ) {
perror("Error opening the file\n");
exit(1);
} else {
// successfully opened the file that's going to be transferred
printf("file opened: %s\n", filename);
// get file size
int file_block_size = 0;
bzero(buffer, BUFLEN);
long file_size;
fseek(fp, 0, SEEK_END);
file_size = ftell(fp);
// goes back to the beginning of file(fp)
rewind(fp);
printf("file name: %s\n", filename);
printf("file size: %ld kb\n", file_size/1024);
// get time
time(&timer);
clientLength = sizeof(client_addr);
//client_addr.sin_family = AF_INET;
int sendFileName;
// length of file name
if( (sendFileName = sendto(socketfd, filename, strlen(filename), 0, (struct sockaddr *)&client_addr, sizeof(struct sockaddr_in))) >= 0) {
printf("file name sent.\n");
} else {
//printf("%d\n", sendFileName);
perror("file name send error.\n");
exit(1);
}
}
recvfrom
的最后一个参数采用需要用 sockaddr 参数的大小初始化的 socklen_t
的地址。我没有看到 clientLength
在此调用之前初始化,因此当此函数 returns 时 client_addr
可能未正确更新。这会导致对 sendto
的后续调用失败。
如果您在调用 recvfrom
之前初始化 clientLength
,这将解决问题。
clientLength = sizeof(client_addr);
tempGet = recvfrom(socketfd, tempBuf, BUFLEN, 0, (struct sockaddr *)&client_addr, &clientLength);
我对 Stack Overflow 上所有不正确和半正确的 UDP 套接字程序尝试感到有点厌倦。我决定做参考UDP实现,以这道题为例。下次看到TCP问题(有时间和精力)我会做参考TCP实现。
此实现正确地执行了所有操作(据我所知:)。它使用 BSD 套接字,因此 Windows 实现将不得不进行微小的更改。除此之外,应该都是符合C-99的。
错误处理:应用程序检查可能的错误,但不尝试更正它们,只是简单地报告。众所周知,在某些错误情况下会泄漏套接字(这会导致应用程序关闭),但在这种情况下添加适当的套接字关闭只会保证更多代码,而实际上并没有增加很多好处。
编译:
gcc -std=c99 -g -D_POSIX_SOURCE -Wall -Werror -pedantic -o test_udp send_udp.c
用法:./test_udp 服务器 或 ./test_udp <主机名> <文件名>
看哪:我给你test_udp.c.
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
const short port = 4321;
int run_server();
int send_file(const char* const server, const char* const file);
int main(int argc, char* argv[]) {
if (argc < 2 || argc > 3) {
printf("Error: Usage send_udp (server|<server_host>) [<file name>]\n");
return 1;
}
if (!strcmp(argv[1], "server"))
return run_server(); // runs forever
if (argc != 3) {
printf("Error: client mode accepts two arguments: server and file name.\n");
return 1;
}
return send_file(argv[1], argv[2]);
}
int run_server() {
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
printf("run_server(): error creating socket: %s\n", strerror(errno));
return 2;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {
printf("run_server(): error binding socket: %s\n", strerror(errno));
return 2;
}
printf("run_server(): socket created and bound, entering endless loop.\n");
struct sockaddr_in incoming;
socklen_t incoming_sz = sizeof(incoming);
char buff[USHRT_MAX]; // maximum size of UDP datagramm
const char* buff_ptr = buff;
while (1) {
ssize_t msg_size = recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr*)(&incoming), &incoming_sz);
if (msg_size == -1) {
printf("run_server(): error receiving message: %s\n", strerror(errno));
return 3;
}
if (msg_size == 0) {
printf("run_server(): a message of 0 size received from the client, bogus. Skipping, continue to listen.\n");
continue;
}
// Message structure: unsigned 16 bit length of file name (network order), followed by filename, followed by data
uint16_t fname_len;
if (msg_size < sizeof(fname_len)) {
printf("run_server(): Bogus (too short) message received from the client. Skipping, continue to listen.\n");
continue;
}
memcpy(&fname_len, buff_ptr, sizeof(fname_len));
fname_len = ntohs(fname_len);
buff_ptr += sizeof(fname_len);
msg_size -= sizeof(fname_len);
if (msg_size < fname_len) {
printf("run_server(): Bogus (too short) message received from the client. Skipping, continue to listen.\n");
continue;
}
char file_name[fname_len + 1];
strncpy(file_name, buff_ptr, fname_len);
file_name[fname_len] = '[=11=]';
buff_ptr += fname_len;
msg_size -= fname_len;
printf("run_server(): incoming transfer for file %s, intrinsic file size: %zu\n", file_name, msg_size);
FILE* f = fopen(file_name, "wb");
if (!f) {
printf("run_server(): Could not open file for writing. Skipping the message, carrying on.\n");
continue;
}
size_t written = fwrite(buff_ptr, msg_size, 1, f);
if (written != 1)
printf("run_server(): error, could not write whole file.\n");
else
printf("run_server(): incoming file written successfully.\n");
fclose(f);
}
return 0;
}
int send_file(const char* const server, const char* const file) {
uint16_t fname_len = strlen(file);
uint16_t max_short = 0;
max_short = ~max_short;
if (fname_len > (max_short - sizeof(fname_len))) {
printf("send_file(): file name is toooo large. Can't send this file.\n");
return 2;
}
FILE* f = fopen(file, "rb");
if (!f) {
printf("send_file(): Could not open file for reading. Nothing sent.\n");
return 3;
}
fseek(f, 0, SEEK_END);
unsigned long fsize = ftell(f);
fseek(f, 0, SEEK_SET);
if (fsize > max_short - sizeof(fname_len) - fname_len) {
printf("send_file(): file is toooo large. Can't send this file.\n");
fclose(f);
return 2;
}
char buff[sizeof(fname_len) + fname_len + fsize];
char* buff_ptr = buff;
uint16_t net_fname_len = htons(fname_len);
memcpy(buff_ptr, &net_fname_len, sizeof(net_fname_len));
buff_ptr += sizeof(net_fname_len);
memcpy(buff_ptr, file, fname_len);
buff_ptr += fname_len;
size_t rc = fread(buff_ptr, fsize, 1, f);
if (rc != 1) {
printf("send_file(): Could not read whole file. Error.\n");
fclose(f);
return 3;
}
fclose(f);
struct addrinfo* ainfo;
if (getaddrinfo(server, NULL, NULL, &ainfo) != 0) {
printf("send_file(): Unknown host %s.\n", server);
return 3;
}
struct addrinfo* ainfo_begin = ainfo;
// I will take the first IP v4 entry in possible addressess
while (ainfo->ai_family != AF_INET && ainfo->ai_next)
ainfo = ainfo->ai_next;
if (ainfo->ai_family != AF_INET) {
printf("send_file(): Couldn't resolve host %s to AF_INET address.\n", server);
return 3;
}
int addr = ((struct sockaddr_in*)(ainfo->ai_addr))->sin_addr.s_addr;
freeaddrinfo(ainfo_begin);
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
printf("read_file(): error creating socket: %s\n", strerror(errno));
return 2;
}
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(port);
remote_addr.sin_addr.s_addr = addr;
ssize_t sent = sendto(sock, buff, sizeof(buff), 0, (struct sockaddr*)(&remote_addr), sizeof(remote_addr));
if (sent != sizeof(buff))
printf("read_file(): error sending message over socket: %s, only %zu bytes sent.\n", strerror(errno), sent);
else
printf("read_file(): file sent successfully.\n");
return sent == sizeof(buff) ? 0 : 3;
}