欢迎光临!
若无相欠,怎会相见

翻译 – Build Your Own Redis with C/C++ – 03

03. Hello Server/Client

本章继续 socket 编程的介绍。 我们将编写两个简单的 (不完整的) 程序来演示上一章中的系统调用。 第一个程序是 server, 它接受客户端的连接, 读取一条信息, 并写入一条回复。 第二个程序是客户端, 它连接 server, 写入一条信息, 并读取一条回复。 我们先从 server 开始。

首先, 我们需要获取一个套接字 fd: int fd = socket(AF_INET, SOCK_STREAM, 0)

AF_INET 用于 IPv4, 使用 AF_INET6 用于 IPv6 或双协议栈套接字。 为简单起见, 我们在本书中将一直使用 AF_INET

SOCK_STREAM 用于 TCP。 在本书中, 我们不会使用 TCP 以外的任何其他协议。 在本书中, socket() 调用的所有 3 个参数都是固定的。

接下来, 我们将引入一个新的系统调用:

int val = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

setsockopt() 调用用于配置套接字的各个方面。 这一特殊调用启用了 SO_REUSEADDR 选项。 如果没有该选项, 服务器在重启后将无法绑定到相同的地址。 读者练习: 找出 SO_REUSEADDR 到底是什么, 为什么需要它。

下一步是 bind()listen(), 我们将绑定通配符地址 0.0.0.0:1234

// bind, this is the syntax that deals with IPv4 addresses
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = ntohs(1234);
addr.sin_addr.s_addr = ntohl(0); // wildcard address 0.0.0.0 int rv = bind(fd, (const sockaddr *)&addr, sizeof(addr));
if (rv) { 
    die("bind()");
}
// listen
rv = listen(fd, SOMAXCONN); 
if (rv) {
    die("listen()"); 
}

对每个连接进行循环, 并对其进行处理。

while (true) { 
    // accept
    struct sockaddr_in client_addr = {};
    socklen_t socklen = sizeof(client_addr);
    int connfd = accept(fd, (struct sockaddr *)&client_addr, &socklen); 
    if (connfd < 0) {
        continue; 
        // error 
    }
    do_something(connfd);
    close(connfd); 
}

do_something() 函数只是简单的读写。

static void do_something(int connfd) {
    char rbuf[64] = {};
    ssize_t n = read(connfd, rbuf, sizeof(rbuf) - 1); 
    if (n < 0) {
        msg("read() error");
        return; 
    }
    printf("client says: %s\n", rbuf);
    char wbuf[] = "world";
    write(connfd, wbuf, strlen(wbuf)); 
}

请注意, read()write() 调用返回的是读取或写入的字节数。 真正的程序员必须处理函数的返回值, 但在本章中, 为了简洁起见, 我省略了很多内容。 而且本章中的代码也不是建立网络的正确方法。

client 程序要简单得多:

int fd = socket(AF_INET, SOCK_STREAM, 0); 
if (fd < 0) {
    die("socket()"); 
}
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = ntohs(1234);
addr.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); // 127.0.0.1
int rv = connect(fd, (const struct sockaddr *)&addr, sizeof(addr)); 
if (rv) {
    die("connect"); 
}
char msg[] = "hello"; 
write(fd, msg, strlen(msg));

char rbuf[64] = {};
ssize_t n = read(fd, rbuf, sizeof(rbuf) - 1); 
if (n < 0) {
    die("read"); 
}
printf("server says: %s\n", rbuf); 
close(fd);

使用以下命令行编译我们的程序:

g++ -Wall -Wextra -O2 -g 03_server.cpp -o server
g++ -Wall -Wextra -O2 -g 03_client.cpp -o client

在一个窗口中运行 ./server, 然后在另一个窗口中运行 ./client。 您应该会看到以下结果:

$ ./server
client says: hello
$ ./client
server says: world

读者练习: 阅读本章使用的 API 的手册, 或查找相关的在线教程。 请确保您知道如何查找有关 API 使用的帮助, 因为本书不会涉及 API 的使用细节。

结语

接着上一篇文章继续

如有错误, 敬请指出, 感谢指正! — 2024-11-30  22:08:57

赞(0) 打赏
转载请注明:飘零博客 » 翻译 – Build Your Own Redis with C/C++ – 03
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

欢迎光临