c - 使用C使用TCP连接到端口

标签 c sockets tcp

我对套接字和任何类型的网络编程都是99%的新手,所以请多多包涵。

我的目标是连接到本地计算机(192.168.0.1)上的端口(在这种情况下为2111)。从那里,我正计划发送和接收基本信息,但是那是另一天。

我目前已经尝试过:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char **argv)
{
    int sd;
    int port;
    int start;
    int end;
    int rval;
    struct hostent *hostaddr;
    struct sockaddr_in servaddr;

    start = 2111;
    end   = 2112;
    for(port = start; port <= end; port++)
    {
        sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(sd == -1)
        {
            perror("Socket()\n");
            return (errno);
        }

        memset(&servaddr, 0, sizeof(servaddr));

        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(port);

        hostaddr = gethostbyname("192.168.0.1");

        memcpy(&servaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);

        rval = connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        if(rval == -1)
        {
            printf("Port %d is closed\n", port);
            close(sd);
        }
        else printf("Port %d is open\n", port);

        close(sd);
    }

    return 0;
}

但是,我的connect()调用挂起约90秒,然后返回-1。

该设备直接连接到Mac Mini的以太网端口,并且制造商已确认该端口为2111或2112。

我究竟做错了什么?另外,它可以采用ELI5(就像我5岁时那样解释)格式吗?我举几个例子要好得多。

最佳答案

当您调用connect()连接到主机时,您的计算机将发送SYN数据包以开始TCP连接的three-way handshake。从这里开始,有3种可能的情况:

  • 如果对等方正在该端口上监听,它将以SYN + ACK数据包作为响应,您的计算机以最终的ACK作为响应,并且连接已建立-connect()成功返回。
  • 如果对等方不在该端口上监听,它将以类型和代码表示该端口已关闭的ICMP数据包作为响应,这会导致connect()调用几乎立即失败,并显示错误ECONNREFUSED(连接被拒绝)。在正常情况下,这需要1个网络往返时间(RTT),通常为数十或数百毫秒。
  • 如果您的计算机从未收到适当的SYN + ACK TCP数据包或连接被拒绝的ICMP数据包,则它假定其原始SYN数据包已被某个地方的网络丢弃,并将尝试多次重新发送SYN数据包,直到获得其中一个数据包为止。 back或遇到与操作系统有关的超时,此时connect()调用失败,并显示ETIMEDOUT。这通常是1-2分钟,具体取决于操作系统及其TCP设置。

  • 您显然正在处理第3个案例。这可能是由几个不同的问题引起的:
  • 您的原始SYN数据包已在网络中丢失,可能是由于链路故障,路由器重载或防火墙
  • 对等方的SYN + ACK或ICMP响应数据包在网络中丢失,可能是由于链路故障,路由器重载或防火墙
  • 目的地地址可能无法路由/无法访问
  • 对等方可能根本无法正确响应SYN + ACK或ICMP数据包

  • 如果您是直接通过以太网连接到设备,则排除#1和#2。 #4是可能的,但我认为#3是最可能的解释。

    简要介绍数据包路由

    您的计算机具有多个网络接口(interface)-以太网(有时为多个以太网接口(interface)),Wi-Fi,环回设备,VPN隧道等。每当您创建套接字时,都必须将其绑定(bind)到一个或多个特定的网络接口(interface),以便进行连接。操作系统知道实际通过哪个NIC发送数据包。对于服务器的监听套接字,通常绑定(bind)到所有网络接口(interface)(以监听所有接口(interface)),但是也可以绑定(bind)到特定的网络接口(interface)以仅监听该接口(interface)。

    对于客户端套接字,将它们连接到其他对等端时,通常不会将它们绑定(bind)到特定接口(interface)。默认情况下,您的计算机使用其内部路由表以及目标IP地址来确定要使用的网络接口(interface)。例如,如果您有一台具有两个NIC的网关计算机,其中一个通过IP 54.xyz连接到公共(public)互联网,而另一个通过IP 192.168.1.1连接到内部专用网络,则该计算机将路由表极有可能说“对于发往192.168.0.0/16的数据包,请使用NIC 2,对于所有其他数据包,请使用NIC 1”。如果要绕过路由表,可以通过在调用bind()之前在套接字上调用connect()来将套接字绑定(bind)到所需的网络接口(interface)。

    全部放在一起

    那么,这对您意味着什么呢?

    首先,确保192.168.0.1实际上是您应该连接的正确目标地址。该地址如何确定?您的计算机是否充当DHCP服务器,以将该地址分配给另一台主机?该主机是否使用静态IP配置?

    接下来,确保您的路由表正确。如果另一台计算机为其自身分配了静态IP,则很可能是您的Mac不知道如何路由到该目的地,并且可能正在尝试通过错误的接口(interface)进行路由。您可以使用 route(8) utility在Mac OS X上手动调整路由,但是每次重新启动时都会重置这些路由。 this blog post显示了一个示例,该示例使用启动项在启动时自动添加新路由。您将要使用与连接到目标主机的以太网接口(interface)关联的IP地址。

    另外,除了使用路由表之外,您还可以在套接字上调用bind(),然后再将connect()绑定(bind)到要使用的接口(interface)的本地地址,但这不适用于其他程序,除非它们也提供了该功能。例如,curl(1)实用程序使您可以传递--interface <name>命令行标志来指示它绑定(bind)到特定接口(interface)。

    关于c - 使用C使用TCP连接到端口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24766627/

    相关文章:

    c - 在旧 C 中使用前声明变量

    sockets - 所有端口都执行相同的工作?

    c - 为什么服务器在关闭客户端连接时进入无限循环

    java - 在 jmeter 上执行负载测试时出现套接字关闭错误

    C#:无法在 TCP 服务器上接收大数据

    字符数组比较问题

    c - stdint.h 中定义的 C99 有符号整数类型是否在溢出时表现出明确定义的行为?

    c - 从 C 中的字符串中删除最常见的单词

    asp.net - 如何在 Windows 上篡改源 IP 地址

    c# - 需要有关 TCP 服务器类型的建议以满足此类应用程序