40、UNIX网络编程实用技术与工具

UNIX网络编程实用技术与工具

1. TLI网络编程基础

TLI(Transport Layer Interface)是UNIX网络编程中替代套接字接口的一种选择。它具有协议独立性,理论上比套接字接口更优,但实际使用并不广泛。若追求可移植性,建议优先选择套接字接口。
- 异步事件返回值 :当调用相关TLI函数时,若出错返回 -1,并将错误信息存储在 t_errno (可能还有 errno )中;成功时返回异步事件的指示,具体如下:
| 事件指示 | 含义 |
| ---- | ---- |
| T_LISTEN | 端点收到连接请求 |
| T_CONNECT | 端点收到连接确认 |
| T_DATA | 端点收到正常数据 |
| T_EXDATA | 端点收到加急数据 |
| T_DISCONNECT | 端点收到断开连接指示 |
| T_UDERR | 端点收到数据报错误指示 |
| T_ORDREL | 端点收到有序释放指示 |
- 地址转换 :可以在地址的内部表示和字符串之间进行转换。字符串由十进制字节值组成,用点分隔,包含主机地址和服务端口号。相关转换函数如下:

#include <netdir.h>

char *taddr2uaddr(const struct netconfig *config,
        const struct netbuf *addr);

struct netbuf *uaddr2taddr(const struct netconfig *config,
        const char *uaddr);

taddr2uaddr 函数将 struct netbuf 结构中的TLI地址转换为字符串形式的“通用地址”并返回该字符串; uaddr2taddr 函数将 uaddr 中的通用地址转换为TLI地址,并返回指向 struct netbuf 的指针。这两个函数都需要在 config 参数中传入当前网络选择信息。不过,这些函数在HP - UX 10.x中不可用。
- 使用read和write与TLI :在没有特殊准备的情况下,不能直接在传输端点上使用 read write 函数。TLI基于STREAMS子系统实现,该子系统最初由Dennis Ritchie发明并包含在Research UNIX Version 8中,后经AT&T改进并在System V Release 3.0中首次发布,SVR4是首个完全支持所有带STREAMS驱动设备的版本。

STREAMS子系统本质上在用户和设备(如磁盘、终端或网络接口)之间提供原始数据流,去除了不同设备的专用驱动(但仍有驱动,且具有通用接口)。用户可以随意向数据流添加(“push”)或移除(“pop”)中间处理元素(即模块),模块可以堆叠,多个模块可同时处理数据流。由于STREAMS通过在相邻处理元素之间传递消息工作,而 read write 期望的是普通字节流,无法处理消息头,因此若要在传输端点使用 read write ,需执行以下操作:

graph LR
    A[开始使用read和write] --> B[push处理模块]
    B --> B1[移除消息头(读操作)]
    B --> B2[将写入转换为消息(写操作)]
    B --> C[使用read和write传输数据]
    C --> D[使用TLI函数时]
    D --> E[pop处理模块]

具体代码实现如下:

#include <sys/ioctl.h>
#include <sys/stropts.h>

// push模块
ioctl(fd, I_PUSH, "tirdwr");

// 使用read和write传输数据

// pop模块
ioctl(fd, I_POP, "tirdwr");

不过,由于操作较为繁琐,一般情况下不太值得这样做。

2. 错误处理与日志记录

在编程过程中,错误处理和日志记录是非常重要的环节。
- 错误发生时退出程序
- abort函数 :在调试程序时,获取程序当前状态的核心转储文件对调试非常有帮助。 abort 函数可随时用于生成核心转储,其原型如下:

#include <stdlib.h>

void abort(void);

调用 abort 时,它会尝试关闭所有打开的文件,然后向调用进程发送 SIGABRT 信号。若进程未捕获或忽略该信号,将产生核心转储。
- assert函数 assert 函数(实际上是一个预处理器宏)为调试时使用 abort 提供了简便方式,原型如下:

#include <assert.h>

void assert(int expression);

assert 宏会计算 expression 的值,若为假(零),则在标准错误输出上打印包含表达式、源文件名和行号的信息,然后调用 abort 。以下是一个示例程序:

#include <assert.h>
#include <stdio.h>

int
main(int argc, char **argv)
{
    int total;

    total = 0;

    while (--argc)
        total += atoi(*++argv);

    assert(total > 100);

    printf("%d\n", total);
    exit(0);
}

当输入的数字总和小于等于100时, assert 会触发错误信息并调用 abort
- 错误日志记录 :系统程序遇到错误时,难以确定错误消息的输出位置。对于用户执行的命令,可将消息输出到终端屏幕;但对于守护进程、 at cron 运行的程序等,情况则较为复杂。
- syslog守护进程 :4.2BSD中引入的 syslog 守护进程被大多数厂商采用。 syslogd 程序在系统启动时启动并一直运行,程序(包括操作系统本身)可将错误或其他信息发送给它。 syslogd 根据配置文件(通常为 /etc/syslog.conf )的指令,对消息进行如下处理:
- 在系统控制台上打印消息,消息前会显示当前日期和时间、发送程序的名称,还可选择显示程序的进程ID号。
- 将消息打印到日志文件,不同类型的消息可发送到同一日志文件,也可发送到不同文件。
- 将消息发送到另一主机上运行的 syslogd ,由远程主机处理消息。常见做法是将客户端系统的所有消息发送到文件服务器进行日志记录,既利用服务器的额外磁盘空间,又减少日志记录位置。
- 忽略消息,常见于忽略调试消息,若需要可临时让 syslogd 处理。
- 相关函数
```c
#include

    void openlog(char *ident, int logopts, int facility);
    void closelog(void);
    void syslog(int priority, char *mesg, /* args */ ...);
    int vsyslog(int priority, char *mesg, va_list ap);
    int setlogmask(int maskpri);
    ```
    - `openlog`函数用于打开日志记录,`ident`参数用于标识程序,通常可使用`argv[0]`去除前导路径名后的值;`logopts`参数指定多个日志记录选项,可通过按位或组合,如`LOG_PID`(记录每个消息的进程ID)、`LOG_CONS`(若无法发送到`syslogd`,将消息写入系统控制台)等;`facility`参数指定默认设施(类别),用于在`syslogd`配置文件中对消息进行分组。
    - `closelog`函数用于关闭日志文件。
    - `syslog`函数用于实际记录消息,`mesg`参数类似于`printf`的格式字符串,包含额外的转换说明`%m`,会被系统错误消息替换;`priority`参数由设施和级别按位或组成,级别包括`LOG_EMERG`(紧急情况)、`LOG_ALERT`(需立即纠正的情况)等。
    - `vsyslog`函数与`syslog`的关系类似于`vprintf`与`printf`的关系,它接受可变长度参数列表。不过,该函数在HP - UX 10.x中不可用。
    - `setlogmask`函数用于控制哪些消息实际发送到`syslogd`,它会设置当前掩码优先级并返回先前的优先级,优先级不在`maskpri`中的消息不会发送到`syslogd`。

以下是根据调试状态设置日志掩码的示例代码:

#include <syslog.h>

// ...

openlog(ident, logopt, facility);

if (debug)
    setlogmask(LOG_UPTO(LOG_DEBUG));
else
    setlogmask(LOG_UPTO(LOG_ERR));

一般来说,大多数系统程序适合记录到 LOG_DAEMON LOG_LOCALn 设施。若程序生成大量日志信息,可预留 LOG_LOCALn 设施,或直接打开自己的日志文件,不使用 syslog

UNIX网络编程实用技术与工具

3. 标准搜索例程

SVR4提供了一系列用于在内存中进行标准搜索的实用例程,包括线性搜索、二分搜索和哈希表。这些任务经常被执行,一套提供良好算法实现的库例程是UNIX编程库的宝贵补充。不过,大多数其他实现并未提供这些函数。

3.1 线性搜索

线性搜索是效率最低的搜索方法,但适用于小列表。搜索特定项时,从列表开头开始,依次比较每个项,直到找到所需项。平均每次搜索需进行n/2次比较,其中n是列表的大小。

线性搜索算法由 lsearch lfind 函数实现:

#include <search.h>

void *lsearch(const void *key, void *base, size_t *nelp,
        size_t width, int (*compar)(const void *, const void *));

void *lfind(const void *key, const void *base, size_t *nelp,
        size_t width, int (*compar)(const void *, const void *));

这两个函数实现了Donald Knuth所著《计算机程序设计艺术》第3卷第6.1节中的算法S。

在这两个函数中, key 是要在表中查找的数据, base 指向表中的第一个元素, nelp 指向一个整数,该整数包含表中当前元素的数量, width 是表元素的字节大小。 compar 参数是一个指向比较函数(如 strcmp )的指针,用于比较表中的两个元素。如果元素相等,该函数必须返回0,否则返回非零值。

lsearch 函数在表中搜索 key ,并返回指向它的指针。如果未找到 key ,则将其添加到表的末尾, nelp 的值加1,并返回指向新条目的指针。

lfind 函数在表中搜索 key ,并返回指向它的指针。如果未找到 key ,则不将其添加到表中,而是返回空指针。

需要注意的是, key 和表基元素的指针可以是任何类型。比较函数不需要比较其参数的每个字节,这使得可以搜索任意数据类型(字符串、整数、结构)。使用 lsearch 创建表的一个副作用是可以从列表中删除重复项,因为它只在元素不存在时才将其添加到列表中。

以下是一个演示 lsearch lfind 使用的小程序:

#include <search.h>
#include <string.h>
#include <stdio.h>

#define TABLESIZE   10      /* max. size of the table       */
#define ELEMENTSIZE 16      /* max. size of a table element */

int compare(const void *, const void *);

int
main(void)
{
    int i;
    char *p;
    size_t nel;
    char line[ELEMENTSIZE];
    char table[TABLESIZE][ELEMENTSIZE];

    /*
     * Tell the user what to do.
     */
    printf("Enter %d strings, not all unique.\n\n", TABLESIZE);

    /*
     * Read in some strings.
     */
    nel = 0;
    for (i = 0; i < TABLESIZE; i++) {
        /*
         * Prompt for each string.
         */
        printf("%2d> ", i + 1);

        /*
         * Read the string.
         */
        if (fgets(line, sizeof(line), stdin) == NULL)
            exit(0);

        /*
         * Strip the newline.
         */
        line[strlen(line) - 1] = '\0';

        /*
         * Search for the string.  If it's not in the table,
         * lsearch will add it for us.
         */
        (void) lsearch(line, table, &nel, ELEMENTSIZE, compare);
    }

    /*
     * Print the contents of the table.
     */
    printf("\nContents of the table:\n");

    for (i = 0; i < nel; i++)
        printf("\t%s\n", table[i]);

    /*
     * Let the user search for things.
     */
    for (;;) {
        /*
         * Prompt for a search string.
         */
        printf("\nSearch for: ");

        /*
         * Read the search string.
         */
        if (fgets(line, sizeof(line), stdin) == NULL) {
            putchar('\n');
            exit(0);
        }

        /*
         * Strip the newline.
         */
        line[strlen(line) - 1] = '\0';

        /*
         * Search for the string.  lfind will return null
         * if it's not there.
         */
        p = (char *) lfind(line, table, &nel, ELEMENTSIZE, compare);

        /*
         * Print the search results.
         */
        if (p == NULL) {
            printf("String not found.\n");
        }
        else {
            printf("Found at location %d.\n",
                   ((int) p - (int) table) / ELEMENTSIZE + 1);
        }
    }
}

/*
 * compare - compare two strings, return 0 if equal, non-zero if not.
 */
int compare(const void *a, const void *b)
{
    return strcmp((const char *)a, (const char *)b);
}

该程序的执行流程如下:

graph LR
    A[开始程序] --> B[提示用户输入字符串]
    B --> C[循环读取字符串]
    C --> D{字符串是否已存在}
    D -- 否 --> E[使用lsearch添加到表中]
    D -- 是 --> C
    C --> F{是否达到最大输入数}
    F -- 否 --> C
    F -- 是 --> G[打印表内容]
    G --> H[提示用户搜索字符串]
    H --> I[读取搜索字符串]
    I --> J{字符串是否存在于表中}
    J -- 是 --> K[输出找到的位置]
    J -- 否 --> L[输出未找到信息]
    K --> H
    L --> H

总结

本文介绍了UNIX网络编程中的TLI接口,包括其异步事件处理、地址转换以及如何使用 read write 函数。同时,还讨论了错误处理和日志记录的方法,如使用 abort assert 进行错误处理,以及使用 syslog 守护进程进行日志记录。最后,介绍了SVR4提供的线性搜索函数 lsearch lfind 。这些技术和工具在UNIX网络编程中具有重要的应用价值,能够帮助开发者更高效地进行程序开发和调试。

在实际应用中,开发者可以根据具体需求选择合适的技术和工具。例如,在处理网络连接时,可以使用TLI接口;在调试程序时,可以使用 abort assert ;在记录系统错误和信息时,可以使用 syslog 。对于小列表的搜索任务,可以使用线性搜索函数。通过合理运用这些技术和工具,能够提高程序的可靠性、可维护性和性能。

考虑柔性负荷的综合能源系统低碳经济优化调度【考虑碳交易机制】(Matlab代码实现)内容概要:本文围绕“考虑柔性负荷的综合能源系统低碳经济优化调度”展开,重点研究在碳交易机制下如何实现综合能源系统的低碳化经济性协同优化。通过构建包含风电、光伏、储能、柔性负荷等多种能源形式的系统模型,结合碳交易成本能源调度成本,提出优化调度策略,以降低碳排放并提升系统运行经济性。文中采用Matlab进行仿真代码实现,验证了所提模型在平衡能源供需、平抑可再生能源波动、引导柔性负荷参调度等方面的有效性,为低碳能源系统的设计运行提供了技术支撑。; 适合人群:具备一定电力系统、能源系统背景,熟悉Matlab编程,从事能源优化、低碳调度、综合能源系统等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究碳交易机制对综合能源系统调度决策的影响;②实现柔性负荷在削峰填谷、促进可再生能源消纳中的作用;③掌握基于Matlab的能源系统建模优化求解方法;④为实际综合能源项目提供低碳经济调度方案参考。; 阅读建议:建议读者结合Matlab代码深入理解模型构建求解过程,重点关注目标函数设计、约束条件设置及碳交易成本的量化方式,可进一步扩展至多能互补、需求响应等场景进行二次开发仿真验证。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值