C语言 如何使用C中的套接字编程通过互联网连接两台计算机?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18021189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:03:13  来源:igfitidea点击:

How to connect two computers over internet using socket programming in C?

csocketsnetwork-programming

提问by dushyantashu

This is simple Client-Server chat program.This code is working fine on computers connected on Network, how to modify it such that it can connect between computers over the Internet. Using Public IP of the server in gethostbyname()isn't working.

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

//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, '##代码##', 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, '##代码##', 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;
}

回答by This isn't my real name

Based on the information you've given, I don't think it's possible to provide a solution to the question you're asking. You've stated that your code works when the two computers are on the same local network, so clearly the code (which, yes, has issues) works at least well enough to connect from client to server.

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

If (as is established,) the code works, then it shouldn't matter whether the client and server are on the same network or separate networks, so long as there's a route, a path, a connection between the two networks. Therefore, if the client can't connect to the server, the conclusion is that this path is missing. The path being missing, however, is not a problem we can troubleshoot for you: It might be "my 'Windows Firewall' is blocking this app", it might be "my ISP (or the other guy's ISP) is blocking this port, it might be "The other guy's terms of service with his ISP includes a "no servers" clause which they enforce by blocking allports", it might even be "my ISP is on the outs with the other guy's ISP and won't route packets to them anymore".

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

However, since you've gone to all the trouble of posting this code, and I've gone to the trouble of (a) reading it, and (b) writing a response, I've decided to include some commentary on issues I see in your code. Note that this is guaranteed notto be an exhaustive list.

但是,由于您已经费尽心力地发布了此代码,而我也费了 (a) 阅读它和 (b) 写了一个回复,因此我决定对我遇到的问题进行一些评论在你的代码中看到。请注意,这保证不是详尽的列表。

In Client.c:

在 Client.c 中:

  • You're calling memset(), and casting the first argument to char *. The memset()function is defined to take a void *as the first argument. Since you've #included <string.h>you have a correct prototype in scope, so whatever you pass to it will be converted to a void *automatically. The cast is therefore both incorrect and pointless.
  • You're calling gethostbyname(), and the string you're passing is an IPv4 address.
    • The gethostbyname()and gethostbyaddr()functions were deprecated in POSIX.1-2004 and were excluded from POSIX.1-2008. They are replaced by getaddrinfo()(and getnameinfo()), and I refer you to Section 5.1 of Beej's Guide to Network Programmingfor further information.
    • According to POSIX.1-2004, "the behavior of gethostbyname() when passed a numeric address string is unspecified". The gethostbyname()function expects to be passed an actual hostname, for IP addresses there's gethostbyaddr(). (Or getaddrinfo()now, of course.)
  • You're using the gets()function. The gets()function was deprecated in C99, marked as Obsolete in POSIX.1-2008, and excluded from C11, because it is fundamentally unsafe due to not having any way of limiting input size. The generally recommended alternative is fgets(), note that unlike gets(), the fgets()function doesn't discard the \ncharacter.
  • 您正在调用memset(),并将第一个参数转换为char *。该memset()函数被定义为将 avoid *作为第一个参数。由于您已经#included,<string.h>您在范围内拥有正确的原型,因此您传递给它的任何内容都将void *自动转换为a 。因此,演员表既不正确又毫无意义。
  • 您正在调用gethostbyname(),您传递的字符串是一个 IPv4 地址。
    • gethostbyname()gethostbyaddr()功能在POSIX.1-2004被弃用,被排除在POSIX.1-2008。它们由getaddrinfo()(和getnameinfo())代替,我建议您参阅第 5.1 节以Beej's Guide to Network Programming了解更多信息。
    • 根据 POSIX.1-2004,“未指定传递数字地址字符串时 gethostbyname() 的行为”。该gethostbyname()函数期望传递一个实际的主机名,对于 IP 地址,有gethostbyaddr(). (getaddrinfo()当然,或者现在。)
  • 您正在使用该gets()功能。该gets()函数在 C99 中被弃用,在 POSIX.1-2008 中被标记为 Obsolete,并从 C11 中排除,因为它从根本上是不安全的,因为它没有任何限制输入大小的方法。通常推荐的替代方法是fgets(),请注意,与 不同的是gets(),该fgets()函数不会丢弃该\n字符。

In Server.c:

在 Server.c 中:

  • You're still casting the first argument to memset()to char *, which is still unnecessary and wrong,
  • You're still using the gets()function, which is still inherently problematic,
  • You're doing write(client_connected, message, sizeof(message));. Every response from the server will be the full 100 bytes long, with garbage bytes being written after the response string. Use strlen(message)+1instead.
  • 您仍在将第一个参数强制转换为memset()to char *,这仍然是不必要且错误的,
  • 您仍在使用该gets()功能,这仍然存在固有问题,
  • 你在做write(client_connected, message, sizeof(message));。来自服务器的每个响应都将是完整的 100 字节长,在响应字符串之后写入垃圾字节。使用strlen(message)+1来代替。

In both:

同时:

  • Your message is a string that was input by the user, but when the client sends the message, it doesn't include the terminal null byte. The recipient can only read what the sender writes, so it isn't receiving a terminal null byte... which is only a problem because the receiving code assumes that what it received is a valid string. Make sure your messages include the null at the end of the string, by having the specified message size be one more than strlen(message).
  • 您的消息是用户输入的字符串,但是当客户端发送消息时,它不包含终端空字节。收件人只能读取发件人写的内容,因此它不会接收终端空字节......这只是一个问题,因为接收代码假定它收到的是有效字符串。通过使指定的消息大小大于 1,确保您的消息在字符串末尾包含 null strlen(message)

回答by Kartik Gupta

Well after my research I came up with this answer. If you want to connect Devices over Internet you need to have a Server having a unique IP Address Eg you could buy one online. When you try to create a device in your home network as Server you need to provide the Global IP Address and since the ISP provides you with a single Public IP shared by a lot of devices over the Network using a router, you cant create a ServerSocket over a Home network shared by many Devices

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

回答by Alexandru Cancescu

You can't connect to a device only by the global IP but if you open a port, port forward, only for the server then the socket can be made.

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