Linux API 書。 綜合指南»


Linux API 書。 綜合指南»

下午好我提請您注意《Linux API》一書。 綜合指南》(該書譯本 Linux 編程接口)。 可以在出版商的網站上訂購,如果您應用促銷代碼 LinuxAPI 您將獲得 30% 的折扣。

摘錄自該書以供評論:

套接字:服務器架構

在本章中,我們將討論設計迭代和並行服務器的基礎知識,以及有助於創建服務器端 Internet 應用程序的特殊 inetd 守護程序。

迭代和並行服務器

有兩種常見的基於套接字的網絡服務器架構:

  • 迭代:服務器一次為一個客戶端提供服務,首先處理一個客戶端的請求(或多個請求),然後繼續處理下一個客戶端;

  • 並行:服務器被設計為同時為多個客戶端提供服務。

第 44.8 節已經提供了基於 FIFO 隊列的迭代服務器的示例。

迭代服務器通常僅適用於可以相當快地處理客戶端請求的情況,因為每個客戶端都必須等待,直到它前面的任何其他客戶端都得到服務。 這種方法的一個常見用例是在客戶端和服務器之間交換單個請求和響應。

並行服務器適用於每個請求需要大量時間來處理,或者客戶端和服務器需要長時間交換消息的情況。 在本章中,我們將主要關注設計並行服務器的傳統(也是最簡單的)方法,即為每個新客戶端創建一個單獨的子進程。 這樣的過程完成了為客戶端提供服務的所有工作,然後結束。 由於每個進程都是獨立運行的,因此可以同時為多個客戶端提供服務。 主服務器進程(父進程)的主要任務是為每個新客戶端創建一個單獨的子進程(或者,您可以創建執行線程而不是進程)。

在以下部分中,我們將查看基於互聯網域套接字的迭代和並行服務器的示例。 這兩個服務器實現了 echo 服務 (RFC 862) 的簡化版本,該服務返回客戶端發送給它的任何消息的副本。

echo迭代udp服務器

在本節和下一節中,我們將介紹 echo 服務的服務器。 它在端口號 7 上可用,並且可以通過 UDP 和 TCP 運行(該端口已保留,因此回顯服務器必須以管理員權限運行)。

echo UDP 服務器不斷讀取數據報並將其副本返回給發送者。 由於服務器一次只需要處理一條消息,因此迭代架構就足夠了。 服務器的頭文件如清單 56.1-XNUMX 所示。

清單 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-XNUMX 顯示了服務器實現。 值得注意的是以下幾點:

  • 為了將服務器置於守護進程模式,我們使用第37.2節中的becomeDaemon()函數;

  • 為了使程序更加緊湊,我們使用了55.12節中開發的internet域socket庫;

  • 如果服務器無法向客戶端返迴響應,它將使用 syslog() 調用將消息寫入日誌。

在實際應用中,我們很可能會對使用 syslog() 記錄消息的頻率施加一定的限制。 這將消除攻擊者溢出系統日誌的可能性。 另外,請記住,每個 syslog() 調用都非常昂貴,因為它默認使用 fsync()。

清單 56.2。 實現 echo UDP 服務的迭代服務器

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

INT
main(int argc, char *argv[])
{
int sfd;
ssize_t numRead;
socklen_tlen;
struct sockaddr_storage cladr;
charbuf[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)",
strerror(errno));
退出(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)
!=numRead)
syslog(LOG_WARNING, "回顯響應 %s (%s) 時出錯",
inetAddressStr((struct sockaddr *) &cladr, len,
addrStr, IS_ADDR_STR_LEN),
strerror(errno));
}
}
_________________________________________________________________套接字/id_echo_sv.c

我們使用清單 56.3 中的程序來測試服務器。 它還使用第 55.12 節中開發的 Internet 域套接字庫。 客戶端程序將服務器所在的網絡主機名作為命令行的第一個參數。 客戶端進入一個循環,將每個剩餘參數作為單獨的數據報發送到服務器,然後讀取並輸出從服務器接收到的數據報作為響應。

清單 56.3。 echo UDP 服務的客戶端

#include“id_echo.h”

INT
main(int argc, char *argv[])
{
int sfd,j;
大小_tlen;
ssize_t numRead;
charbuf[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);
}
退出(EXIT_SUCCESS);
}
_________________________________________________________________套接字/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