UNIX网络编程:IPC之消息队列

本文详细介绍了消息队列的概念、限制及关键操作函数如msgget、msgsnd、msgrcv和msgctl的功能与用法,并提供了单服务器单客户的示例代码。

消息队列:将消息队列按队列的方式组织成的链表,每个消息都是其中的一个节点;
注意:消息队列的长度及每个消息的大小是有限制的

消息队列的操作函数如下:
msgget

  int msgget(key_t key,int msgflg);
  该函数作用是:创建一个消息队列或访问一个已经存在的消息队列;成功返回标示符,出错返回-1;
  其第一个参数是一个整数IPC键,由ftok函数产生,或者直接是常值IPC_PRIVATE;
  msgflg是IPC_CREAT, IPC_EXCL与读写权限或的结果

msgsnd

  int msgsnd(int msqid, const void *msgptr, int msgsz,int msgflg);
  该函数的作用是:在msgget打开消息队列后,往消息队列发送消息;成功返回0, 出错返回-1;
  msqid是消息队列的标示符;
  msgsz指的是要发送的消息的长度;
  msgflg参数设置的是一些标志位;设置当消息队列满等情况出现时的处理方式,如果msgflg设置为IPC_NOWAIT,则不发送消息并且立即返回-1;否则发送进程挂起等待。
  msgptr是一个指向要发送的消息的指针,并指向缓冲区的第一个子段应为长整形,指定消息类型,消息内容存放在该缓冲区的紧跟在消息类型子段的区域;
msgptr是结构体指针,该结构体如下:

 struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };

msgrcv

  int msgrcv(int msqid, void *msgptr, int msgsz, long msgtyp, int msgflg);
该函数作用是从某个消息队列中读出一个消息
参数msqid是消息队列标示符;
msgptr和msgsnd函数中的作用相同,指向消息的类型子段;
msgsz是指要读出的消息的数据部分的大小,是msgrcv能返回的最大数据量,该长度只包含数据部分的大小,不包含类型子段大小;
msgtyp是想要读出的消息的类型;

msgtyp > 0 时,该函数返回的是消息队列中类型值为msgtyp的第一个消息;
msgtyp = 0 时,该函数返回的是消息队列中的第一个消息;
msgtyp < 0 时, 该函数返回的是消息队列中类型值小于或等于该类型值绝对值的消息类型中最小的第一个消息;

msgflg设置当消息队列满等情况出现时的进程处理方式,若设置为IPC_NOWAIT时,表示不发送消息并立即返回-1,否则发送进程挂起等待

msgctl

 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
消息队列的控制函数
msqid是消息队列的id;
cmd是采取的控制操作,有三个值,如下所示:
  参数值   说明   
  IPC_SET   设置消息队列的属性,将buf指向的结构体中的数值设置为消息队列的相关性   
  IPC_STAT   获取消息队列的属性信息并保存到buf指向的结构体中   
  IPC_RMID   移除ID为msqid的消息队列   
  
buf是一个结构体指针,该结构体如下:

  struct msqid_ds {
               struct ipc_perm msg_perm;     /* Ownership and permissions */
               time_t          msg_stime;    /* Time of last msgsnd(2) */
               time_t          msg_rtime;    /* Time of last msgrcv(2) */
               time_t          msg_ctime;    /* Time of last change */
               unsigned long   __msg_cbytes; /* Current number of bytes in
                                                queue (nonstandard) */
               msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
               msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
               pid_t           msg_lspid;    /* PID of last msgsnd(2) */
               pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
           };

以下是一个用来测试消息队列的这几个函数的使用的简单的单服务器单客户的程序代码:

服务器端的代码:

#include "utility.h"

#define MSGSND 100
#define MSGRCV 200

int main(int ac, char *av[])
{
    key_t msg_key = ftok(av[1], 0xFF);    //得到一个IPC键值
    if(msg_key == -1){
        printf("msg ftok error.\n");
        exit(1);
    }

    int msg_id = msgget(msg_key, IPC_CREAT|IPC_EXCL|0666);   //创建或打开一个消息队列
    if(msg_id == -1){
        printf("msgget error.\n");
        exit(1);
    }

    Msg msg;
    while(1){
        printf("Ser:>");
        scanf("%s", msg.MsgText);  //输入消息的内容
        if(strncmp(msg.MsgText, "quit", 4) == 0){
            msgctl(msg_id, IPC_RMID, NULL);   //删除消息队列
            break;
        }

        msg.MsgType = MSGSND; 
        msgsnd(msg_id, &msg, strlen(msg.MsgText) + 1, IPC_NOWAIT);   //发送消息队列的内容

        msgrcv(msg_id, &msg, 256, MSGRCV, 0);   //接收消息
        printf("cli:>%s\n", msg.MsgText);   //输出消息的内容
    }

    return 0;
}

以下是客户端的代码:

#include "utility.h"

#define MSGSND 200
#define MSGRCV 100

int main(int ac, char *av[])
{
    key_t msg_key  = ftok(av[1], 0xFF);   //得到消息队列的键值
    if(msg_key == -1){
        printf("ftok error.\n");
        exit(1);
    }

    int msg_id = msgget(msg_key, 0);    //打开一个消息队列
    if(msg_id == -1){
        printf("msgget error.\n");
        exit(1);
    }

    Msg msg;
    while(1){
        msgrcv(msg_id, &msg, 256, MSGRCV, 0);  //接收消息
        printf("ser:>%s\n", msg.MsgText);

        printf("cli:");
        scanf("%s", msg.MsgText);
        msg.MsgType = MSGSND;
        if(strncmp(msg.MsgText, "quit", 4) == 0){
            msgsnd(msg_id, &msg, strlen(msg.MsgText) + 1, IPC_NOWAIT);
            break;
        }
        msgsnd(msg_id, &msg, strlen(msg.MsgText) + 1, IPC_NOWAIT);  //发送消息
    }
    return 0;
}

以下是utility.h头文件的代码:

#pragma once

#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
    void *__pad;
};

msg.h头文件

#include "utility.h"

typedef struct Msg{
    long MsgType;
    char MsgText[256];
}Msg;

makefile文件:

all:ser cli

ser:ser.cpp
    g++ ser.cpp -o ser

cli:cli.cpp
    g++ cli.cpp -o cli

.PHONY:clean

clean:
    rm ser cli

若要实现一个服务器对多个客户,并且仅使用一个消息队列,则可以将客户的ID作为消息的类型,不过这种仅使用一个IPC通道很容易造成死锁解决方法是:客户可以填满消息队列,防止服务器发送应答,于是客户阻塞在msgsnd中,服务器也是如此;
检测这种死锁的一个方法是约定服务器对消息队列总是非阻塞写

实现一个服务器对多个客户并且每个客户一个消息队列:服务器队列有共用的键,但是客户以IPC_PRIVATE创建各自私有的队列;每个客户把各自的私有队列的标示符传递给服务器,服务器把自己的应答发送到由客户指出的队列中; 这种设计的潜在问题是某个客户中途死亡,这种情况下它的死有队列中可能永远残留消息(或者至少到内核自举或i某个用户显示的删除该队列为止)。

<think> 首先,用户的问题是关于理解一个特定的正则表达式:/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$。我需要解释这个正则表达式的含义。 回顾系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 最后,生成相关问题。 用户提供了三个引用: - 引用[1]:关于常见正则表达式,如正浮点数。 - 引用[2]:包含各种正则表达式的例子,如字母数字下划线字符串、整数、浮点数、中文字符、身份证号等。 - 引用[3]:关于正则表达式的简化建议。 但用户说这些引用仅供参考,我应该基于用户的问题来回答。用户的问题是解释正则表达式/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$的含义。 所以,我需要解析这个正则表达式。 分解正则表达式- /^:表示字符串的开始。 - (?=.*[A-Z]):这是一个正向先行断言(positive lookahead)。意思是:字符串中必须包含至少一个大写字母(A-Z)。 - (?=.*[a-z]):另一个正向先行断言,表示字符串中必须包含至少一个小写字母(a-z)。 - (?=.*[0-9]):另一个正向先行断言,表示字符串中必须包含至少一个数字(0-9)。 - .{8,}:表示任意字符(除换行符外)至少8次。意思是字符串长度至少为8个字符。 - $:表示字符串的结束。 所以,整体来看,这个正则表达式匹配的字符串必须: 1. 包含至少一个大写字母。 2. 包含至少一个小写字母。 3. 包含至少一个数字。 4. 长度至少为8个字符。 这通常用于密码强度验证,要求密码有大小写字母、数字,并且长度至少8位。 现在,构建回答: - 使用中文。 - 结构清晰:逐步解释。 - 引用相关部分:用户提供了引用,但它们是关于其他正则表达式的。我可以提到引用[2]中有类似的正则表达式例子,比如强密码的表达式,但用户的具体表达式不同。引用[2]中有类似的东西:/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,15}$/,这很相似,但长度不同。 在引用[2]中:强密码:必须包含数字、字母,字符长度为6~15位 /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,15}$/ 或 /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])[0-9a-zA-Z]{6,15}$/。 用户的正则是/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$,长度至少8,没有上限,并且使用.*允许任何字符。 所以,在回答中,我可以引用这个。 引用[3]是关于简化正则表达式的建议,但可能不直接相关。 回答结构: 1. 介绍正则表达式的整体目的:用于验证密码强度。 2. 分解表达式部分: - ^$:表示整个字符串。 - (?=.*[A-Z]):解释先行断言的含义。 - 类似地解释其他部分。 - .{8,}:长度要求。 3. 总结匹配的条件。 4. 自然地添加引用:例如,在解释先行断言时,可以提到引用中的类似例子。 引用标识:在引用的段落末尾添加[^n]。 最后,生成相关问题。 相关问题应该基于这个主题,比如: 1. 如何使用这个正则表达式在代码中? 2. 这个正则表达式的局限性是什么? 3. 如何修改它来添加特殊字符要求? 4. 相关的正则表达式例子。 确保LaTeX:在回答中,如果有数学表达式,比如.*[A-Z],这不算数学表达式,所以不需要用$...$。但如果有像范围或计数,可能不需要。指令是针对数学表达式的,所以这里可能不适用。 在引用中,用户有像^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$这样的表达式,但没有要求用LaTeX格式。指令说“所有行内数学表达式(包括变量、不等式、方程等)必须使用$...$格式”,但正则表达式不是数学表达式,所以我应该直接写正则表达式。 在回答中,我会直接写正则表达式代码,用反引号表示代码块或内联代码。 指令有示例:在“快速排序”例子中,有代码块用```python```。 所以,我可以将正则表达式放入代码块。 回答步骤: 1. 解释整体含义。 2. 分解每个部分。 3. 给出示例匹配的字符串。 4. 引用相关资源。 5. 生成相关问题。 用中文。 </think> ### 正则表达式 `/^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$` 的含义解析 这个正则表达式主要用于验证密码强度,要求字符串满足特定的复杂度条件。以下我将逐步分解其结构,帮助您理解每个部分的含义。整个表达式的核心是使用**正向先行断言(positive lookahead)** 来确保字符串中包含大写字母、小写字母和数字,同时要求最小长度。正则表达式语法可参考站内引用中的常见模式[^2]。 #### 1. **整体结构** - `/^ ... $/`:这表示正则表达式匹配整个字符串,从开头 (`^`) 到结尾 (`$`)。例如,字符串 `"Abc12345"` 会被匹配,但 `" Abc12345"`(开头有空格)则不会,因为空格导致开头不匹配。 - 表达式由多个部分组成: - 三个正向先行断言 `(?=...)`:用于检查字符串是否满足某些条件,但不消耗字符(即不移动匹配位置)。 - `.`:匹配任意单个字符(除换行符外)。 - `{8,}`:表示前面的模式(这里是 `.`)至少重复 8 次,即字符串长度至少为 8 个字符。 - **简言之**:这个正则表达式要求字符串**必须同时包含至少一个大写字母、至少一个小写字母、至少一个数字,且总长度不少于 8 个字符**。它常用于密码策略中,确保密码强度。 #### 2. **分解每个部分** - `(?=.*[A-Z])`: - `(?= ... )` 是正向先行断言,表示“字符串必须满足括号内的条件”,但不计入匹配位置。 - `.*`:匹配零个或多个任意字符(`.` 表示任意字符,`*` 表示重复零次或多次)。 - `[A-Z]`:匹配一个大写字母(A 到 Z)。 - **含义**:字符串中必须包含至少一个大写字母。例如,在 `"abc123"` 中不满足(没有大写字母),而 `"Abc123"` 满足。 - 类似用法在引用[2]中的“强密码”例子中出现过(如 `/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,15}$/`)[^2]。 - `(?=.*[a-z])`: - 结构同上,但 `[a-z]` 匹配一个小写字母(a 到 z)。 - **含义**:字符串中必须包含至少一个小写字母。例如,`"ABC123"` 不满足(没有小写字母),而 `"Abc123"` 满足。 - `(?=.*[0-9])`: - `[0-9]` 匹配一个数字(09)。 - **含义**:字符串中必须包含至少一个数字。例如,`"Abcdefg"` 不满足(没有数字),而 `"Abc123"` 满足。 - `.{8,}`: - `.` 匹配任意字符。 - `{8,}` 表示前面的 `.` 至少重复 8 次,即字符串长度至少为 8 个字符。 - **含义**:整个字符串的最小长度为 8。例如,`"Abc123"`(长度 6)不满足,而 `"Abc12345"`(长度 8)满足。 #### 3. **组合效果和示例** - **匹配条件总结**: - 至少一个大写字母(A-Z)。 - 至少一个小写字母(a-z)。 - 至少一个数字(0-9)。 - 长度至少 8 个字符(允许更长)。 - 字符类型不限(因为 `.` 匹配任意字符,包括符号如 `!@#$`)。 - **有效匹配示例**: - `"Password123"`:包含大写 P、小写 assword、数字 123,长度 11。 - `"Abc!12345"`:包含大写 A、小写 bc、数字 12345,以及符号 `!`,长度 9- **无效匹配示例**: - `"password"`:缺少大写字母和数字。 - `"PASSWORD123"`:缺少小写字母。 - `"Abc123"`:长度不足 8。 - `"!@#$%^&*"`:缺少字母和数字。 这个表达式确保了密码的基础强度,但未要求特殊字符(如符号),您可以根据需求扩展。类似的正则表达式设计原则在引用[3]中提到,强调“宁简勿繁”,即拆解复杂条件为多个简单断言以提升可读性和效率[^3]。 #### 4. **实际应用** - 在编程中,这个正则表达式可用于表单验证(如密码输入框)。例如,在 JavaScript 中: ```javascript const regex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,}$/; console.log(regex.test("Abc12345")); // 输出 true console.log(regex.test("abc1234")); // 输出 false(缺少大写字母) ``` - 优点:使用先行断言能高效检查多个条件,无需嵌套或复杂逻辑。 - 局限性:它不检查字符类型重复(如多个数字),或特殊字符要求。如果需更严格规则(如包含符号),可追加断言,例如添加 `(?=.*[!@#$%^&*])`。 如果您有更多上下文(如具体代码实现),我可以提供更针对性的帮助。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值