【问题标题】:C Socket does not connect and returns exit value of 255C Socket 未连接并返回退出值 255
【发布时间】:2021-10-30 04:21:32
【问题描述】:

我有一段 C 代码应该连接到 www.google.com 并发出 HTTP GET 请求,但是当我运行它时,它会在“正在连接..”上停留大约 30 秒,然后返回“连接失败”和退出返回值为 255。我做错了什么?

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define PORT 8000
struct hostent *hostinfo;

int main(void) {
  int sock = 0, valread;
  struct sockaddr_in serv_addr;
  char *hostname = "www.google.com";
  char *request = "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n";
  hostinfo = gethostbyname(hostname);
  
  char *ip = inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[0]);

  char buffer[1024] = {0};
  printf("Creating socket...\n");
  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    printf("\n Socket creation error \n");
    return -1;
  }
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(PORT);
  printf("Checking address...\n");
  if(inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0){
    printf("\n Invalid IP/Address not supported \n");
    return -1;
  }
  printf("Connecting to host %s...\n", ip);
  if(connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
    printf("\n Connection Failed \n");
    return -1;
  }
  send(sock, request, strlen(request), 0);
  printf("Message sent\n");
  valread = read(sock, buffer, 1024);
  printf("%s\n", buffer);
  return 0;
}

【问题讨论】:

  • @TedLyngmo - 就是那样的一天 :) 已删除
  • 好像连接失败了。 Google 是否使用端口 8000?
  • 可能没有直接关系,但connect 的第三个参数应该是sizeof struct sockaddr 而不是sizeof struct sockaddr_in
  • @WilliamPursell 这是错误的。 connect() 期望传入的结构的 actual 大小。所以 sizeof struct sockaddr_in 是正确的值,因为 serv_addr 实际上是 sockaddr_in 而不是 sockaddr(它们只是 恰好在大多数系统上大小相同)。如果 serv_addr 是 IPv6 的 sockaddr_in6,则需要传入 sizeof struct sockaddr_in6。如果您在使用 sockaddr_in6 时尝试传入 sizeof struct sockaddrconnect() 将失败并出现不同的错误。
  • 附带说明:Google 现在完全使用 HTTPS,因此在端口 80 (http:) 上向 www.google.com 发送未加密的 HTTP 请求实际上不会检索该页面,它会返回HTTP 重定向到端口 443 (https:) 上的 www.google.com。此代码尚未设置为处理 HTTPS,因此它实际上无法从 Google 上的任何位置检索任何内容。

标签: c sockets


【解决方案1】:

我看到了两个主要问题。

  • 您使用了错误的端口。为http 使用端口80
  • 您的readprintf 是一个危险的组合,很容易导致访问越界(以及未定义的行为)。您从套接字读取的内容不会以空值终止。您可以改为执行以下操作:
    ...
    printf("Message sent\n");
    while((valread = read(sock, buffer, sizeof(buffer))) > 0) {
      fwrite(buffer, valread, 1, stdout);
    }
    
    但是,当所有内容都已读取时,这将阻塞。查看非阻塞 I/O 或考虑使用 selectepollpoll 等待套接字上的可用数据。

如果您只想获取响应然后断开连接,则可以在服务器发送响应后使用Connection: close 关闭连接。完整代码如下:

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define PORT 80

int main(void) {
  int sock = 0, valread;
  struct hostent *hostinfo;
  struct sockaddr_in serv_addr;
  const char *hostname = "www.google.com";
  const char *request = "GET / HTTP/1.1\r\n"
                        "Host: www.google.com\r\n"
                        "Connection: close\r\n\r\n";   // <- added
  hostinfo = gethostbyname(hostname);
  
  char *ip = inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[0]);

  char buffer[1024] = {0};
  printf("Creating socket...\n");
  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    printf("\n Socket creation error \n");
    return -1;
  }
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(PORT);
  printf("Checking address...\n");
  if(inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0){
    printf("\n Invalid IP/Address not supported \n");
    return -1;
  }
  printf("Connecting to host %s...\n", ip);
  if(connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0){
    perror("connect()");
    return -1;
  }
  send(sock, request, strlen(request), 0);
  printf("Message sent\n");
  while((valread = read(sock, buffer, sizeof(buffer))) > 0) {
    fwrite(buffer, valread, 1, stdout);
  }
}

【讨论】:

  • Re "然而,当所有内容都被读取时,这将阻塞。",当所有 received 都被读取时。当新的东西到达或连接关闭时它会解除阻塞,当连接关闭时循环将退出。这里很好。不需要select/epoll/poll
  • @ikegami 我刚刚注意到 OP 将 valread 设为 int 而不是 ssize_t,但可以读取的最大长度为 1024,它应该是安全的,因为它会检查 &gt; 0。我关于使用select 或类似的提示是是否要保持连接。然而,我建议的代码告诉服务器断开连接,所以不,不需要select
  • Re“它应该是安全的,因为它检查了&gt; 0”,忽略了!评论已删除。
  • Re "我关于使用select 或类似的提示是如果要保持连接。",select 不需要“保持连接”。 select/epoll/poll 仅在您需要并行性(例如同时从两个套接字读取)时才需要,这里不需要。
  • @ikegami 是的,我描述得很糟糕。另一个可以使用select 的功能是超时。
猜你喜欢
  • 1970-01-01
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 2021-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多