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
最新评论
这个软件有bug的,客户端windows有些键不能用如逗号、句号
没有收到邮件通知
我的评论通知貌似坏掉了,定位一下问题
测试一下重新部署后的邮件功能
居然看到自己公司的MIB库,诚惶诚恐
那可能是RobotFramework-ride的版本问题。我装的1.7.4.2,有这个限制。我有空再尝试下旧版本吧,感谢回复。
你好!我在python2.7中安装RobotFramework-ride的时候提示wxPython的版本最高是2.18.12,用pip下载的wxPython版本是4.10,而且我在那个路径下没有找到2
真的太好了,太感谢了,在bilibili和CSDN上都找遍了,终于在你这里找到了