C语言中的初级套接字编程

3
我刚开始学习socket编程,觉得非常有趣。目前我正在同一台计算机上制作服务器和客户端,因此我可以将ip地址设置为回环地址127.0.0.1,一切似乎都很正常!但是现在我想要用两台计算机来完成这个任务。我有以下问题:
  1. 假设一台计算机是服务器,另一台是客户端。那么,应该将服务器代码放在服务器计算机上,将客户端代码放在客户端计算机上吗?
  2. 在提供bind()的ip地址时,服务器代码中应该是我们可以通过ipconfig找到的系统的ip地址,还是仍然保持回环地址呢?
  3. 在客户端代码中,目标的ip地址应该是服务器计算机的ip地址,对吗?
  4. 最后也是最重要的一点,我该如何连接这两台计算机?
我附上了我开始使用的简单服务器和客户端消息传递代码,请指导我需要进行哪些更改。
服务器代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#define MYPORT 3500

int main()
{
    int sockfd;
    int clientfd;
    int bytes_read;
    char buf[100];
    int struct_size;
    struct sockaddr_in my_addr;
    struct sockaddr_in con_addr;
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(MYPORT);
    my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    my_addr.sin_zero[8]='\0';

    bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr));

    listen(sockfd,5);
    
    struct_size = sizeof(con_addr);
    clientfd = accept(sockfd, (struct sockaddr*)&con_addr, &struct_size);

    bytes_read = read(clientfd, buf, 100);
    buf[bytes_read] = '\0';
    printf("Message from client:%d is %s \n",clientfd, buf);

    close(sockfd);
    close(clientfd);
}

客户端代码

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<fcntl.h>
#include<string.h>
#include<stdio.h>

#define DESTPORT 3500

int main()
{

    struct sockaddr_in dest_addr;
    
    int sockfd = socket(AF_INET,SOCK_STREAM,0);

    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(DESTPORT);
    dest_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    dest_addr.sin_zero[8]='\0';

    connect(sockfd,(struct sockaddr*)&dest_addr, sizeof(struct sockaddr));

    char msg[100];
    printf("Enter you message: ");
    gets(&msg); 
    
    int w = write(sockfd, msg, strlen(msg));
    
    close(sockfd);
    printf("Client Dying.....\n"); 

    return 0;
}
4个回答

4

1) 正确。

2) 在服务器端,您可以绑定到0.0.0.0,这意味着“所有(IPv4)接口”。

3) 是的,你是对的。

4) 最常见的方式是通过以太网交换机(或交叉以太网电缆,但这些更难找到)。


2

服务器应该绑定到0.0.0.0(任意IP地址),除非您尝试限制访问(在这种情况下,您应该使用防火墙而不是端口绑定)。正确的方法实际上是:

struct addrinfo *ai, hints = { .ai_flags = AI_PASSIVE };
if (getaddrinfo(0, "1234", &hints, &ai)) goto error;
int fd = socket(ai->ai_family, SOCK_STREAM, 0);
bind(fd, ai->ai_addr, ai->ai_addrlen);

添加一些错误检查,当然要将“1234”替换为您的端口号。

解决了我的问题,就像我想要的那样。非常感谢 :-) 你能推荐一些关于现代套接字编程的好资源吗? - sachin11
基本上,getaddrinfogetnameinfosocketbindconnectlistenacceptsendtorecvfromselect的手册(或POSIX文档,在此处:http://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html)就是你所需要的全部。在旧的示例和教程中看到的所有复杂性都只是遗留负担,并且对IPv6支持和可移植性是一种妨碍。 - R.. GitHub STOP HELPING ICE
为什么你没有在任何地方指定端口? - JeremyP
愚蠢的错误。已修复。当然,你可以让 bind 分配一个端口号,然后使用 getsocknamegetnameinfo 记录并发布给客户端使用。 - R.. GitHub STOP HELPING ICE
1
当你使用 getaddrinfo() 时,应该始终循环遍历所有结果,并为每个监听地址创建一个套接字。如果你只想简单地做IPv4中的 任何 地址,则没有理由从一开始就使用 getaddrinfo()。你的代码可能会导致意外情况,这取决于系统配置。 - Pavel Šimerda
我的希望是如果支持/配置了IPv6,则获取IPv6任何地址,否则获取IPv4。但有些破损的系统被配置成IPv6任何地址无法接受v4连接... 因此循环和绑定所有可能是最好的选择。 - R.. GitHub STOP HELPING ICE

0

1)假设一台计算机是服务器,另一台是客户端。那么,服务器代码应该放在服务器电脑上,客户端代码应该放在客户端电脑上吗?

我觉得我没有正确理解这个问题...哈哈。如果你的客户端代码在服务器端,你怎么能分配任何内存或在客户端计算机上调用东西呢?

2)在服务器代码中,当我们为bind()提供ip地址时,它应该是通过ipconfig找到的系统的ip地址,还是仍然保持回环地址?

根据man页:

 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

"bind()将地址addr指定的地址分配给文件描述符sockfd所引用的套接字。 addrlen指定由addr指向的地址结构的大小(以字节为单位)。传统上,这个操作被称为“给套接字分配一个名称”。 在SOCK_STREAM套接字接收连接之前,通常需要使用bind()分配本地地址(参见accept(2))。"

在你的情况下,我想127.0.0.1是你正在寻找的东西。简而言之,你所说的地址更可能是你设置到server_addr结构中的那个。

3) 在客户端代码中,我猜目标的IP地址应该是服务器计算机的地址,对吗?

是的。

4) 最后也是最重要的一件事,怎样连接两台计算机?

听起来像是一个经典的聊天室应用程序。 据我所知(我是一个新手...),UDP(User Datagram Protocol)编程和API值得一试,或许可以考虑。如果您想坚持使用TCP/IP,两台任意计算机本质上不能相互连接,但都与服务器保持联系,而服务器将是它们之间的基石。

另外,我查阅了一些套接字手册:

 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

"connect()系统调用将文件描述符 sockfd 引用的套接字连接到由 addr 指定的地址。addrlen 参数指定了 addr 的大小。addr 中的地址格式由套接字 sockfd 的地址空间确定;有关详细信息,请参见 socket(2)."

我认为这在理论上完美地解释了您的问题。


我对套接字编程很新,以下概念是否正确:只要我们可以通过局域网或广域网将服务器和客户端计算机连接在一起,那么服务器和客户端就分别驻留在两台不同的计算机上,并且服务器仍然可以在服务器端计算机上为客户端分配一些内存。我认为这个概念上是正确的,请纠正我如果我错了。 - Dulangi_Kanchana

0

127.0.0.1 是本地主机的环回地址,您当前所在的机器上使用它。

如果您使用两个独立的盒子,则可能希望将其更改为客户端中的真实IP。

是的,客户端通常但并不总是在与服务器不同的物理盒子上。如果您想要,可以在一个盒子上运行两个部分。


谢谢您先生。所以,我将客户端代码中的 IP 地址更改为服务器主机的 IP 地址。但是在服务器代码中我要怎么做才能修改 IP 地址呢?这里有 my_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - sachin11
1
你永远不应该手动操作 sin_addr.s_addr 等内容。这是一种早期90年代的糟糕套接字编程方式,会给你带来很多麻烦,并使IPv6支持变得困难。请查看我的答案,了解绑定套接字以进行监听的现代方法。 - R.. GitHub STOP HELPING ICE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接