书籍《Linux API》。 综合指南”


书籍《Linux API》。 综合指南”

下午好我向您推荐《Linux API》一书。 综合指南”(该书的翻译 Linux 编程接口)。 可以在出版商的网站上订购,如果您应用促销代码 LinuxAPI ,您将获得 30% 的折扣。

摘自书中供参考:

套接字:服务器架构

在本章中,我们将讨论设计迭代和并行服务器的基础知识,还将了解一个名为 inetd 的特殊守护进程,它使创建 Internet 服务器应用程序变得更加容易。

迭代和并行服务器

有两种常见的基于套接字的网络服务器架构:

  • 迭代:服务器一次为一个客户端提供服务,首先处理来自一个客户端的一个请求(或多个请求),然后继续处理下一个客户端;

  • 并行:服务器旨在同时为多个客户端提供服务。

第 44.8 节中已经介绍了基于 FIFO 队列的迭代服务器的示例。

迭代服务器通常仅适用于可以相当快地处理客户端请求的情况,因为每个客户端都被迫等待,直到它前面的任何其他客户端都得到服务。 这种方法的一个常见用例是在客户端和服务器之间交换单个请求和响应。

并行服务器适用于每个请求需要花费大量时间来处理的情况,或者客户端和服务器进行长消息交换的情况。 在本章中,我们将主要关注设计并行服务器的传统(也是最简单)方法,即为每个新客户端创建一个单独的子进程。 该过程执行所有服务客户端的工作,然后结束。 由于每个进程都是独立运行的,因此可以同时为多个客户端提供服务。 主服务器进程(父进程)的主要任务是为每个新客户端创建一个单独的子进程(或者,可以创建执行线程而不是进程)。

在以下部分中,我们将查看迭代和并行互联网域套接字服务器的示例。 这两个服务器实现了 echo 服务 (RFC 862) 的简化版本,该服务返回客户端发送给它的任何消息的副本。

迭代 UDP 服务器回显

在本节和下一节中,我们将介绍 echo 服务的服务器。 它可在端口号 7 上使用,并可通过 UDP 和 TCP 运行(此端口已保留,因此回显服务器必须以管理员权限运行)。

echo UDP 服务器连续读取数据报并将其副本返回给发送方。 由于服务器一次只需要处理一条消息,因此迭代架构就足够了。 服务器的头文件如清单 56.1 所示。

清单 56.1。 程序 id_echo_sv.c 和 id_echo_cl.c 的头文件

#include "inet_sockets.h" /* 声明套接字的功能 */
#include“tlpi_hdr.h”

#define SERVICE "echo" /* UDP 服务名称 */

#define BUF_SIZE 500 /* 数据报的最大大小
客户端和服务器端均可读取 */
_____________________________________________________________________套接字/id_echo.h

清单 56.2 显示了服务器实现。 以下几点值得注意:

  • 为了将服务器置于守护进程模式,我们使用 37.2 节中的 comeDaemon() 函数;

  • 为了使程序更加紧凑,我们使用第 55.12 节中开发的用于处理 Internet 域套接字的库;

  • 如果服务器无法向客户端返回响应,它将使用 syslog() 调用将消息写入日志。

在实际应用程序中,我们可能会对使用 syslog() 记录消息的频率施加一些限制。 这将消除攻击者溢出系统日志的可能性。 此外,不要忘记每次调用 syslog() 的开销都非常大,因为它默认使用 fsync()。

清单 56.2。 实现UDP echo服务的迭代服务器

_________________________________________________________________套接字/id_echo_sv.c
#包括
#include“id_echo.h”
#include“become_daemon.h”

INT
主要(int argc,char *argv[])
{
int sfd;
ssize_t numRead;
socklen_t len;
struct sockaddr_storage cladr;
字符 buf[BUF_SIZE];
char addrStr[IS_ADDR_STR_LEN];

if (becomeDaemon(0) == -1)
errExit("成为守护进程");

sfd = inetBind(SERVICE, SOCK_DGRAM, NULL);
如果(sfd==-1){
syslog(LOG_ERR, "无法创建服务器套接字 (%s)",
错误(错误号));
退出(EXIT_FAILURE);

/* 接收数据报并将其副本返回给发送者 */
}
为了 (;;) {
len = sizeof(struct sockaddr_storage);
numRead = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &cladr, &len);

if (numRead == -1)
errExit("recvfrom");
if (sendto(sfd, buf, numRead, 0, (struct sockaddr *) &claddr, len)
!= 读数)
syslog(LOG_WARNING, "回显响应 %s (%s) 时出错",
inetAddressStr((struct sockaddr *) &cladr, len,
addrStr, IS_ADDR_STR_LEN),
错误(错误号));
}
}
_________________________________________________________________套接字/id_echo_sv.c

为了测试服务器的操作,我们使用清单 56.3 中的程序。 它还使用在第 55.12 节中开发的库来处理 Internet 域套接字。 作为第一个命令行参数,客户端程序采用服务器所在的网络节点的名称。 客户端进入一个循环,将每个剩余参数作为单独的数据报发送到服务器,然后读取并打印从服务器接收到的数据报作为响应。

清单 56.3。 UDP 回显服务的客户端

#include“id_echo.h”

INT
主要(int argc,char *argv[])
{
int sfd,j;
size_t 长度;
ssize_t numRead;
字符 buf[BUF_SIZE];

if (argc < 2 || strcmp(argv[1], "--help") == 0)
useErr("%s 主机消息…n", argv[0]);

/* 根据第一个命令行参数形成服务器地址 */
sfd = inetConnect(argv[1], 服务, SOCK_DGRAM);
如果(sfd==-1)
fatal("无法连接到服务器套接字");

/* 以单独数据报的形式将剩余参数发送到服务器 */
for (j = 2; j < argc; j++) {
len = strlen(argv[j]);
if (write(sfd, argv[j], len) != len)
fatal("部分/写入失败");

numRead = 读取(sfd, buf, BUF_SIZE);
if (numRead == -1)
errExit("读");
printf("[%ld bytes] %.*sn", (long) numRead, (int) numRead, buf);
}
退出(退出成功);
}
_________________________________________________________________套接字/id_echo_cl.c

下面是运行服务器和两个客户端实例时我们将看到的示例:

$su // 绑定到保留端口需要权限
密码:
# ./id_echo_sv // 服务器进入后台模式
# exit //放弃管理员权限
$ ./id_echo_cl localhost hello world // 该客户端发送两个数据报
[5 bytes] hello // 客户端显示从服务器收到的响应
[5字节]世界
$ ./id_echo_cl localhost goodbye // 该客户端发送一个数据报
[7 字节] 再见

祝您阅读愉快)

来源: linux.org.ru