【问题标题】:How to connect two computers over internet using socket programming in C?如何使用 C 中的套接字编程通过 Internet 连接两台计算机?
【发布时间】:2013-08-03 23:27:52
【问题描述】:

这是一个简单的客户端-服务器聊天程序。此代码在连接到网络的计算机上运行良好,如何修改它以便它可以通过 Internet 在计算机之间连接。在gethostbyname() 中使用服务器的公共 IP 不起作用。

//Client.c

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

int main(void)
{
    int clientSocket; /* Socket Decriptor for Client */
    struct sockaddr_in server_addr;
    struct hostent *ptrh;

    char message[100];
    char received[100];
    int n = 0;

    clientSocket=socket(AF_INET, SOCK_STREAM, 0);

    memset((char*)&server_addr, 0, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(10000);

    /*  bind(clientSocket, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)); */

    ptrh=gethostbyname("110.172.156.2");
    memcpy(&server_addr.sin_addr,ptrh->h_addr,ptrh->h_length);

    if( -1 == (connect(clientSocket, (struct sockaddr*)&server_addr, sizeof(server_addr)))) 
    { printf("\nServer Not Ready !!\n"); exit(1); }

while(1)
{
    printf("\nUser:-");
   // memset(message, '\0', 10);

    gets(message);

    n = write(clientSocket, message, strlen(message)+1);
if( (strcmp(message,"q") == 0 ) || (strcmp(message,"Q") == 0 ))
    {
       printf("Wrong place...Socket Closed\n");
       close(clientSocket);
       break;
    }

    //printf("Write:<%u>\n", n);

    read(clientSocket, received, sizeof(received));
    if( (strcmp(received,"q") == 0 ) || (strcmp(received,"Q") == 0 ))
    {
       printf("Wrong place...Socket Closed\n");
       close(clientSocket);
       break;
    }
    else
    printf("Server:- %s\n", received);



}

return 0;
}

//Server.c

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

int main(void) 
{
int serverSocket,client_connected,len;
struct sockaddr_in client_addr,server_addr;
struct hostent *ptrh;
int n=0; 
char message[100],received[100];

serverSocket=socket(AF_INET, SOCK_STREAM, 0);

memset((char*)&server_addr,0,sizeof(server_addr));

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(10000);

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

if(bind(serverSocket,
(struct sockaddr*)&server_addr,sizeof(server_addr)) == -1)
printf("Bind Failure\n");
else
printf("Bind Success:<%u>\n", serverSocket);




while(1)
{   
     listen(serverSocket,5);
     len=sizeof(struct sockaddr_in);

    client_connected=accept(serverSocket,
    (struct sockaddr*)&client_addr,&len);
if (-1 != client_connected)
  printf("Connection accepted:<%u>\n", client_connected);

    while(1)
    {
    n = read(client_connected, received, sizeof(received));
    if( (strcmp(received,"q") == 0 ) || (strcmp(received,"Q") == 0 ))
    {
       printf("Wrong place...Socket Closed of Client\n");
       close(client_connected);
       break;
    }
    else{
    printf("\nUser:-%s", received);}
    printf("\nServer:-");
  //  memset(message, '\0', 10);
    gets(message);             
    write(client_connected, message, sizeof(message));
    if( (strcmp(message,"q") == 0 ) || (strcmp(message,"Q") == 0 ))
    {
       printf("Wrong place...Socket Closed of Client\n");
       close(client_connected);
       break;
    }  
    }
}

close(serverSocket); printf("\nServer Socket Closed !!\n");

return 0;
}

【问题讨论】:

  • 您是否在要连接的路由器上配置了端口转发?
  • 你们中有谁在 NAT 路由器后面吗?您是否转发了相应的端口?
  • 你确定不是防火墙的问题?
  • 除了可能的防火墙问题外,当像connect 这样的系统调用失败时,检查errno(并用例如perror 打印出来)通常是个好主意。

标签: c sockets network-programming


【解决方案1】:

根据您提供的信息,我认为无法为您提出的问题提供解决方案。您已经说过,当两台计算机位于同一本地网络上时,您的代码可以工作,因此很明显,代码(是的,有问题)至少可以很好地从客户端连接到服务器。

如果(如已确定的那样)代码有效,那么客户端和服务器是在同一个网络上还是在不同的网络上都无关紧要,只要两个网络之间存在路由、路径、连接即可.因此,如果客户端无法连接到服务器,则结论是缺少这条路径。但是,路径丢失不是我们可以为您解决的问题:可能是“我的‘Windows 防火墙’阻止了这个应用程序”,也可能是“我的 ISP(或其他人的 ISP)阻止了这个端口” ,可能是“另一个人与他的 ISP 的服务条款包括“无服务器”条款,他们通过阻止 所有 端口来强制执行该条款”,甚至可能是“我的 ISP 与其他人的 ISP,不会再将数据包路由给他们”。

但是,由于您费尽心思发布此代码,而且我费尽心思 (a) 阅读它和 (b) 编写回复,因此我决定添加一些评论关于我在您的代码中看到的问题。请注意,这保证不是是一个详尽的列表。

在 Client.c 中:

  • 您正在调用memset(),并将第一个参数转换为char *memset() 函数被定义为将void * 作为第一个参数。由于您已经#included &lt;string.h&gt;,因此您在范围内拥有正确的原型,因此您传递给它的任何内容都会自动转换为 void *。因此,演员阵容既不正确也毫无意义。
  • 您正在调用 gethostbyname(),而您传递的字符串是 IPv4 地址。
  • gethostbyname()gethostbyaddr() 函数在 POSIX.1-2004 中已弃用,并从 POSIX.1-2008 中排除。它们已替换为 getaddrinfo()(和 getnameinfo()),我将您推荐给 System Calls/getaddrinfo() section of Beej's Guide to Network Programming 了解更多信息。
  • 根据 POSIX.1-2004,“gethostbyname() 在传递数字地址字符串时的行为是未指定的”。 gethostbyname() 函数需要传递一个实际的主机名,对于 IP 地址有 gethostbyaddr()。 (当然,现在是 getaddrinfo()。)
  • 您正在使用gets() 函数。 gets() 函数在 C99 中已弃用,在 POSIX.1-2008 中标记为已过时,并从 C11 中排除,因为由于无法限制输入大小,它从根本上是不安全的。通常推荐的替代方法是fgets(),注意与gets() 不同,fgets() 函数不会丢弃\n 字符。

在 Server.c 中:

  • 您仍在将第一个参数转换为 memset()char *,这仍然是不必要且错误的,
  • 您仍在使用gets() 函数,这仍然存在固有问题,
  • 你正在做write(client_connected, message, sizeof(message));。来自服务器的每个响应都将是完整的 100 字节长,在响应字符串之后写入垃圾字节。请改用strlen(message)+1

在这两个方面:

  • 您的消息是用户输入的字符串,但客户端发送消息时,不包含终端空字节。接收方只能读取发送方写入的内容,因此它没有接收到终端空字节......这只是一个问题,因为接收代码假定它接收到的是有效字符串。通过使指定的消息大小比strlen(message) 大一,确保您的消息在字符串末尾包含空值。

【讨论】:

    【解决方案2】:

    经过我的研究,我想出了这个答案。如果您想通过 Internet 连接设备,您需要一台具有唯一 IP 地址的服务器,例如您可以在线购买一台。当您尝试在家庭网络中创建一个设备作为服务器时,您需要提供全局 IP 地址,并且由于 ISP 为您提供了一个由网络上的许多设备使用路由器共享的单个公共 IP,因此您无法创建 ServerSocket通过多个设备共享的家庭网络

    【讨论】:

      【解决方案3】:

      您不能仅通过全局IP连接到设备,但是如果您打开一个端口,端口转发,仅用于服务器,则可以制作套接字。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-08-04
        • 2015-05-17
        • 1970-01-01
        • 2021-06-24
        • 2023-03-09
        • 1970-01-01
        • 2021-07-21
        相关资源
        最近更新 更多