C 套接字编程:connect() 上出现无效参数错误
C socket programming: Invalid argument error on connect()
我正在编写客户端作为 TCP 客户端服务器程序的一部分。
我的代码到达连接部分并抛出一个 Invalid argument 错误,我已经检查了几次代码但找不到问题。
代码接收3个参数,第一个是IP地址或主机名,第二个是端口,第三个是要发送的消息的最大长度。
我的代码使用 getaddrinfo 来转换 IP 地址或主机名、创建所需的变量、启动连接、读取文件、发送数据和接收数据。
我 运行 代码为:
gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c
./pcc_client 127.0.0.1 2233 4
输出为:
sockaddr_in initialized
Error starting connection : Invalid argument
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in *serv_addr;
int rv;
char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
serv_addr = (struct sockaddr_in *) p->ai_addr;
strcpy(ip, inet_ntoa(serv_addr->sin_addr));
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Initialize socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) //Error creating socket
{
perror("Error creating socket \n");
exit(1);
}
printf("socket created\n");
//Initialize sockaddr_in structure
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
//Initialize connection
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { //Error connecting
perror("Error starting connection \n");
exit(1);
}
printf("connect succesful\n");
exit(0);
}
你定义serv_addr
struct sockaddr_in *serv_addr;
那你就用吧
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
但是在代码中这两个地方之间的任何地方都没有 初始化指针! 这意味着 serv_addr
未初始化 它的值是 indeterminate 并且会指向一些看似随机的位置。取消引用指针将导致 undefined behavior.
简单自然且事实上的标准解决方案是使serv_addr
不是一个指针,而是一个结构对象:
struct sockaddr_in serv_addr;
然后当你需要一个指针时,你可以使用寻址运算符&
。
上面的问题因您在调用 connect
时 使用 &
运算符而变得更加复杂。 serv_addr
是一个指针,那么&serv_addr
是一个指针指向指针。它将是 struct sockaddr_in **
类型。这是 this 问题,指针指向指针,导致错误消息,因为您发送的指针不是指向 sockaddr_in
结构对象的指针。
通过使用如上所示的结构对象也可以解决此问题。
您使用的 serv_addr
完全错误。
您已将 serv_addr
声明为 sockaddr_in*
指针。 getaddrinfo()
成功退出后,循环输出列表,分配 serv_addr
指向列表中的每个 ai_addr
,然后释放列表,留下 serv_addr
指向在无效内存。然后,当您尝试用数据填充 serv_addr
时,您会浪费内存。然后你最终甚至根本没有将指向 sockaddr_in
的有效指针传递给 connect()
,你实际上传递的是指向指向 sockaddr_in
的指针的指针,这就是它抱怨的原因“无效参数”。
事实上,你对这种情况的看法大体上是错误的。当使用 getaddrinfo()
时,因为它 returns 可能是多个套接字地址的链表,所以您需要遍历列表尝试 connect()
到每个地址,直到其中一个成功。如果您想要升级代码以同时支持 IPv4 和 IPv6(通过设置 hints.ai_family = AF_UNSPEC;
),这一点尤其重要。
尝试更像这样的东西:
int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
struct addrinfo hints, *servinfo, *p;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // or AF_UNSPEC
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0)
{
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0) continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0) break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd < 0) //Error creating/connecting socket
{
perror("Error creating/connecting socket \n");
exit(1);
}
printf("connect successful\n");
...
close(sockfd);
exit(0);
}
我正在编写客户端作为 TCP 客户端服务器程序的一部分。
我的代码到达连接部分并抛出一个 Invalid argument 错误,我已经检查了几次代码但找不到问题。
代码接收3个参数,第一个是IP地址或主机名,第二个是端口,第三个是要发送的消息的最大长度。
我的代码使用 getaddrinfo 来转换 IP 地址或主机名、创建所需的变量、启动连接、读取文件、发送数据和接收数据。
我 运行 代码为:
gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c
./pcc_client 127.0.0.1 2233 4
输出为:
sockaddr_in initialized
Error starting connection : Invalid argument
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in *serv_addr;
int rv;
char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
serv_addr = (struct sockaddr_in *) p->ai_addr;
strcpy(ip, inet_ntoa(serv_addr->sin_addr));
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Initialize socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) //Error creating socket
{
perror("Error creating socket \n");
exit(1);
}
printf("socket created\n");
//Initialize sockaddr_in structure
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
//Initialize connection
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { //Error connecting
perror("Error starting connection \n");
exit(1);
}
printf("connect succesful\n");
exit(0);
}
你定义serv_addr
struct sockaddr_in *serv_addr;
那你就用吧
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
但是在代码中这两个地方之间的任何地方都没有 初始化指针! 这意味着 serv_addr
未初始化 它的值是 indeterminate 并且会指向一些看似随机的位置。取消引用指针将导致 undefined behavior.
简单自然且事实上的标准解决方案是使serv_addr
不是一个指针,而是一个结构对象:
struct sockaddr_in serv_addr;
然后当你需要一个指针时,你可以使用寻址运算符&
。
上面的问题因您在调用 connect
时 使用 &
运算符而变得更加复杂。 serv_addr
是一个指针,那么&serv_addr
是一个指针指向指针。它将是 struct sockaddr_in **
类型。这是 this 问题,指针指向指针,导致错误消息,因为您发送的指针不是指向 sockaddr_in
结构对象的指针。
通过使用如上所示的结构对象也可以解决此问题。
您使用的 serv_addr
完全错误。
您已将 serv_addr
声明为 sockaddr_in*
指针。 getaddrinfo()
成功退出后,循环输出列表,分配 serv_addr
指向列表中的每个 ai_addr
,然后释放列表,留下 serv_addr
指向在无效内存。然后,当您尝试用数据填充 serv_addr
时,您会浪费内存。然后你最终甚至根本没有将指向 sockaddr_in
的有效指针传递给 connect()
,你实际上传递的是指向指向 sockaddr_in
的指针的指针,这就是它抱怨的原因“无效参数”。
事实上,你对这种情况的看法大体上是错误的。当使用 getaddrinfo()
时,因为它 returns 可能是多个套接字地址的链表,所以您需要遍历列表尝试 connect()
到每个地址,直到其中一个成功。如果您想要升级代码以同时支持 IPv4 和 IPv6(通过设置 hints.ai_family = AF_UNSPEC;
),这一点尤其重要。
尝试更像这样的东西:
int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
struct addrinfo hints, *servinfo, *p;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // or AF_UNSPEC
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0)
{
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0) continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0) break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd < 0) //Error creating/connecting socket
{
perror("Error creating/connecting socket \n");
exit(1);
}
printf("connect successful\n");
...
close(sockfd);
exit(0);
}