使用TCP与C连接到端口

时间:2014-07-15 19:31:21

标签: 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)格式吗?我的例子好多了。

2 个答案:

答案 0 :(得分:6)

当您致电connect()以连接主机时,您的计算机会发送一个SYN数据包以开始TCP连接的three-way handshake。从这里开始,有三种可能的情况:

  1. 如果对等方正在侦听该端口,它会以SYN + ACK数据包响应,您的计算机会以最终的ACK响应,并建立连接 - connect()成功返回。
  2. 如果对等方没有侦听该端口,它会使用ICMP数据包进行响应,该数据包的类型和代码表明该端口已关闭,这导致connect()调用几乎立即失败并出现错误{{1 }} (拒绝连接)。在正常情况下,这需要1个网络往返时间(RTT),通常是几十或几百毫秒。
  3. 如果您的计算机从未收到适当的SYN + ACK TCP数据包或连接被拒绝的ICMP数据包,它会假定其原始SYN数据包被某个地方的网络丢弃,并将尝试多次重新发送SYN数据包,直到获得其中一个这些数据包返回或它发出与操作系统相关的超时,此时ECONNREFUSED调用失败并显示connect()。这通常为1-2分钟,具体取决于操作系统及其TCP设置。
  4. 你明显打击了#3案例。这可能是由几个不同的问题引起的:

    1. 您的原始SYN数据包在网络中丢失,可能是由于链路故障,路由器过载或防火墙
    2. 对等体的SYN + ACK或ICMP响应数据包在网络中丢失,可能是由于链路故障,路由器过载或防火墙
    3. 目的地地址可能无法路由/无法访问
    4. 对等体可能无法正确响应SYN + ACK或ICMP数据包
    5. 如果您通过以太网直接连接到设备,那么排除#1和#2。 #4是可能的,但我认为#3是最可能的解释。

      关于分组路由的简要说明

      您的计算机有多个网络接口 - 以太网(有时是多个以太网接口),Wi-Fi,环回设备,VPN隧道等。无论何时创建套接字,它都必须绑定到一个或多个特定的网络接口。命令操作系统知道实际发送数据包的NIC。对于服务器的侦听套接字,通常绑定到所有网络接口(以侦听所有网络接口上的连接),但您也可以绑定到特定的网络接口以仅侦听该接口。

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

      全部放在一起

      那么,这对你来说意味着什么?

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

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

      或者,不是使用路由表,而是可以在connect()之前调用套接字上的bind()绑定到您要使用的接口的本地地址,但这不会起作用其他程序,除非他们也提供该功能。例如,connect()实用程序允许您传递curl(1)命令行标志,以指示它绑定到特定接口。

答案 1 :(得分:1)

基本上,connect()失败了(检查errno是为什么)。

您可能会考虑为连接实施某种超时。为此,请将套接字设置为非阻塞模式。然后调用connect(),然后使用select()等待超时响应。

SPOILER Linux {/ 1}}的示例

相关问题