简体   繁体   English

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

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

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.这是一个简单的客户端-服务器聊天程序。此代码在网络上连接的计算机上运行良好,如何修改它以便它可以通过 Internet 在计算机之间连接。 Using Public IP of the server in gethostbyname() isn't working.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;
}

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 all ports", 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.但是,由于您已经费尽心力地发布了此代码,而我也费了 (a) 阅读它和 (b) 写了回复,因此我决定对我遇到的问题进行一些评论在你的代码中看到。 Note that this is guaranteed not to be an exhaustive list.请注意,这保证不是详尽的列表。

In Client.c:在 Client.c 中:

  • You're calling memset() , and casting the first argument to char * .您正在调用memset() ,并将第一个参数转换为char * The memset() function is defined to take a void * as the first argument. memset()函数被定义为将void *作为第一个参数。 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.由于您已经 #included <string.h>范围内有一个正确的原型,因此无论您传递给它什么,都将自动转换为void * The cast is therefore both incorrect and pointless.因此,演员表既不正确又毫无意义。
  • You're calling gethostbyname() , and the string you're passing is an IPv4 address.您正在调用gethostbyname() ,您传递的字符串是一个 IPv4 地址。
  • The gethostbyname() and gethostbyaddr() functions were deprecated in POSIX.1-2004 and were excluded from POSIX.1-2008. gethostbyname()gethostbyaddr()函数在 POSIX.1-2004 中已弃用,并从 POSIX.1-2008 中排除。 They are replaced by getaddrinfo() (and getnameinfo() ), and I refer you to System Calls/getaddrinfo() section of Beej's Guide to Network Programming for further information.它们被getaddrinfo() (和getnameinfo() )取代,我建议您参阅Beej's Guide to Network Programming系统调用/getaddrinfo() 部分Beej's Guide to Network Programming获取更多信息。
  • According to POSIX.1-2004, " the behavior of gethostbyname() when passed a numeric address string is unspecified ".根据 POSIX.1-2004,“未指定传递数字地址字符串时 gethostbyname() 的行为”。 The gethostbyname() function expects to be passed an actual hostname, for IP addresses there's gethostbyaddr() . gethostbyname()函数期望传递一个实际的主机名,对于 IP 地址,有gethostbyaddr() (Or getaddrinfo() now, of course.) (当然,或者现在使用getaddrinfo() 。)
  • You're using the gets() function.您正在使用gets()函数。 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. gets()函数在 C99 中被弃用,在 POSIX.1-2008 中被标记为过时,并从 C11 中排除,因为它从根本上是不安全的,因为它没有任何方法来限制输入大小。 The generally recommended alternative is fgets() , note that unlike gets() , the fgets() function doesn't discard the \\n character.通常推荐的替代方法是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,您仍在将memset()的第一个参数转换为char * ,这仍然是不必要且错误的,
  • You're still using the gets() function, which is still inherently problematic,您仍在使用gets()函数,这仍然存在固有的问题,
  • You're doing write(client_connected, message, sizeof(message));你正在做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.来自服务器的每个响应都将是完整的 100 字节长,在响应字符串之后写入垃圾字节。 Use strlen(message)+1 instead.改用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) .通过使指定的消息大小比strlen(message) ,确保您的消息在字符串末尾包含空值。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM