#931 信创是什么

2023-11-30

https://www.cnxclm.com/read-736-1.html

信创,全名:信息技术应用创新,是政府的一个计划,目的是推广国产 IT 技术应用,最终实现 IT 领域自主可控。
这个计划是什么时候开始的我不清楚,确实是自从美国开始对中国展开贸易战之后,这个词才广为人知,也就是近几年的事情。

目前主要是党政军相关项目国产化,以后可能会推广到国企与事业单位。

信创项目的规模不断扩大,自 2019 年起大幅加速。
从 2011 年到 2018 年,信创项目的政府合同规模达到 1.99 亿人民币(合 2852 万美元);
2020 年跃升至 279.2 亿人民币(合 40 亿美元),
2021 年初至 2022 年 11 月为 184.5 亿人民币(合 26.4 亿美元)。
预计 2025 年,这一领域估值将达 2 万亿元(1250 亿美元)。

内容

  • 基础设施:CPU、芯片、服务器、交换机、路由器
  • 基础软件:操作系统、数据库、大数据、中间件、云平台(如 IaaS、PaaS 和低代码平台)
  • 应用软件:办公软件、通用软件、行业软件等
  • 信息安全

信创工委会

https://itaic.org.cn/

2016 年,信创工委会(信息技术应用创新工作委员会)成立。

包括很多知名国产厂商都在信创委员会中,比如:

  • 龙芯(CPU)
  • 浪潮(服务器)
  • 深信服
  • 华为
  • 麒麟软件(操作系统)
  • WPS
  • 奇虎 360
  • 奇安信(360 企业安全)

具体哪些产品符合信创要求,似乎也没有看到一个公开的清单。

项目

  • 操作系统

  • 麒麟 Kylin

  • 统信 UOS
  • 红旗
  • 华为 EularOS

  • 数据库

  • 武汉达梦

  • 其他

  • 东方通

#929 Wi-Fi 6 还没用上,Wi-Fi 7 又来了

2023-11-27
标准 名称 年份 频率(GHz) 理论最大速率 备注
IEEE 802.11 Wi-Fi 0 1997 2.4 2 Mbps
IEEE 802.11b Wi-Fi 1 1999 2.4 11 Mbps
IEEE 802.11a Wi-Fi 2 1999 5 54 Mbps 正交频分复用(OFDM)
IEEE 802.11g Wi-Fi 3 2003 2.4 54 Mbps 正交频分复用(OFDM),兼容 802.11b
IEEE 802.11i 草案 WPA 2003 -- --
IEEE 802.11i-2004 WPA 2 2004 -- --
IEEE 802.11n Wi-Fi 4 2009 2.4/5 600 Mbps 最多 4 个 MIMO
IEEE 802.11ac Wi-Fi 5 2014 5 最多 8 个 MIMO;下行 MU-MIMO (2016 Wave2)
IEEE 802.11i-2018 WPA 3 2018 -- --
IEEE 802.11ax Wi-Fi 6 2019 2.4/5 9.6 Gbps 上下行 MU-MIMO
Wi‑Fi 6E 2020 6
IEEE 802.11be Wi-Fi 7 2024 2.4/5/6 23 Gbps
  • 802.11a 其实早于 802.11b 提出,只是因为一些非技术原因,推广滞后
  • 2000 年,Wi-Fi 这个词被提出,无线以太网兼容性联盟(WECA)更名为 Wi-Fi 联盟(Wi-Fi Alliance,缩写 WFA)
  • Wi-Fi 一词,国内通常读作 歪fai,有一些人引经据典提出过不同看法引发争议
  • Wi-Fi 0、1、2、3 不是官方认定的名称
  • WEP:有线等效加密(Wired Equivalent Privacy),又称无线加密协议(Wireless Encryption Protocol)
  • WPA:Wi-Fi Protected Access (Wi-Fi 访问保护)

现状

市面上大部分路由器都是支持 Wi-Fi 1 - 5,常常可以在广告上看到 IEEE 802.11a/b/g/n/ac 字样。
旗舰型,贵一点的(至少 300+),支持 Wi-Fi 6。

我没有做过这方面研究和对比,只能说理论上:

  1. Wi-Fi 6 速率上限有了提升,网络质量也有优化,同时能耗降低(TWT),能够同时支持更多终端连接(OFDMA),同时支持更多并发数据传输(MU-MIMO)。
  2. Wi-Fi 6E 引入了 6GHz 频段,能支持更高的速度和更低的延迟。

目前我家的路由器,手机,其他设备大多还不支持 Wi-Fi 6。
我家的有线网络用的 5 类线,所以我家理论上最高网速也就 100Mbps(12MB/s)。

Wi-Fi 7

  1. 支持最大 320MHz 带宽(Wi-Fi 6 是 160MHz)
    新的带宽模式:连续 240MHz、非连续 160+80MHz、连续 320 MHz、非连续 160+160MHz
    主要作用是提升 QoS(减少延迟,降低丢包率)
  2. 支持 Multi-RU 机制
  3. 引入更高阶的 4096-QAM 调制技术(Wi-Fi 6 是 1024-QAM)
  4. 引入 Multi-Link 多链路机制
  5. 支持多 AP 间的协同调度
    网络漫游问题

我不太懂。
场景应该是一些工业和商业场景。家用的话,可能只有土豪的游戏设备(VR 游戏?)才能用得上,或者发烧友自己搭建的家庭网络设施。
想必和 Wi-Fi 6 / 5G 一样,对绝大多数普通人来说,只是一个新的营销噱头。

参考资料与拓展阅读

Wi-Fi(发音: /ˈwaɪfaɪ/),又称“无线网络”,是 Wi-Fi 联盟的商标,一个基于 IEEE 802.11 标准的无线局域网技术。“Wi-Fi”常写作“WiFi”或“Wifi”,但是这些写法并没有被 Wi-Fi 联盟认可。

1999 年,几家富有远见的公司联合起来组成了一个全球性非营利性协会——无线以太网兼容性联盟(Wireless Ethernet Compatibility Alliance, WECA),其目标是使用一种新的无线网络技术,无论品牌如何,都能带来最佳的用户体验。在 2000 年,该小组采用术语“Wi-Fi”作为其技术工作的专有名称,并宣布了正式名称:Wi-Fi Alliance。

第七代 WiFi 无线网络,速度可高达 30Gbps,是 WiFi 6 最高 9.6Gbps 速率的三倍之多。相比于 Wi-Fi 6,WiFi 7 将引入 CMU-MIMO 技术最多可支持 16 条数据流,8 车道变 16 车道,妥妥的星际高速公路,其次 WiFi 7 除传统的 2.4GHz 和 5GHz 两个频段,还将新增支持 6 GHz 频段,并且三个频段能同时工作。
2021 年 12 月,联发科宣布 2022 年初将推出 WiFi 7 网络,此前数据显示其网速是 Wi-Fi 6 的 3 倍多。在 2022 年世界移动通信大会(MWC 2022)上,中兴推出 WiFi 7 标准的产品。

#926 开源编程字体

2023-11-14

已经有的开源字体:

厂商 字体 协议 网址
Adobe Source Code Pro OFL https://adobe-fonts.github.io/source-code-pro/ / Repo
Google Noto OFL https://fonts.google.com/noto / Repo
GitHub Monaspace OFL https://monaspace.githubnext.com/ / Repo
JetBrain Mono OFL https://www.jetbrains.com/zh-cn/lp/mono/ / Repo

Adobe 的 Source Code Pro 补上 CJK 文字就成为了我们熟悉的思源黑体系列。

Google Noto 其实就是思源黑体的基础上,再补充其他语言,试图给所有语言文字一个统一的展示效果。
Noto 这个名字来自 No More Tofu。

#923 转载:如何表达

2023-11-08

很多时候,我们表达的目的,不是为了让自己说的开心,而是让听的人清楚。
那如何表达,才能让对方更容易理解,更快抓住重点。
今天我们我们重发一篇文章《如何清晰表达观点?》来聊聊表达这件事,希望对你有所启发。
以下是这篇重发正文。

#922 Redis 创始人 antirez 提供的一个 C 语言示例:聊天服务器

2023-11-04

代码地址:https://github.com/antirez/smallchat
视频讲解:https://www.youtube.com/watch?v=eT02gzeLmF0

作者给做前端开发的朋友写的一个网络编程示例,只有 200 多行纯 C 代码,没有第三方依赖,使用了 select 多路复用实现命令行聊天。
可以用来复习一下 C 编程。

简化部分注释后:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/select.h>

/* ============================ 数据结构 ================================= */

#define MAX_CLIENTS 1000 // This is actually the higher file descriptor.
#define SERVER_PORT 7711

/* This structure represents a connected client. There is very little
 * info about it: the socket descriptor and the nick name, if set, otherwise
 * the first byte of the nickname is set to 0 if not set.
 * The client can set its nickname with /nick <nickname> command. */
struct client {
    int fd;     // Client socket.
    char *nick; // Nickname of the client.
};

/* This global structure encasulates the global state of the chat. */
struct chatState {
    int serversock;     // Listening server socket.
    int numclients;     // Number of connected clients right now.
    int maxclient;      // The greatest 'clients' slot populated.
    struct client *clients[MAX_CLIENTS]; // Clients are set in the corresponding
                                         // slot of their socket descriptor.
};

struct chatState *Chat; // Initialized at startup.

/* ======================== 网络方法 ========================== */

/* Create a TCP socket listening to 'port' ready to accept connections. */
int createTCPServer(int port) {
    int s, yes = 1;
    struct sockaddr_in sa;

    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1;
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); // Best effort.

    memset(&sa,0,sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1 ||
        listen(s, 511) == -1)
    {
        close(s);
        return -1;
    }
    return s;
}

/* Set the specified socket in non-blocking mode, with no delay flag. */
int socketSetNonBlockNoDelay(int fd) {
    int flags, yes = 1;

    /* Set the socket nonblocking.
     * Note that fcntl(2) for F_GETFL and F_SETFL can't be
     * interrupted by a signal. */
    if ((flags = fcntl(fd, F_GETFL)) == -1) return -1;
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) return -1;

    /* This is best-effort. No need to check for errors. */
    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
    return 0;
}

/* If the listening socket signaled there is a new connection ready to
 * be accepted, we accept(2) it and return -1 on error or the new client
 * socket on success. */
int acceptClient(int server_socket) {
    int s;

    while(1) {
        struct sockaddr_in sa;
        socklen_t slen = sizeof(sa);
        s = accept(server_socket,(struct sockaddr*)&sa,&slen);
        if (s == -1) {
            if (errno == EINTR)
                continue; /* Try again. */
            else
                return -1;
        }
        break;
    }
    return s;
}

/* We also define an allocator that always crashes on out of memory: you
 * will discover that in most programs designed to run for a long time, that
 * are not libraries, trying to recover from out of memory is often futile
 * and at the same time makes the whole program terrible. */
void *chatMalloc(size_t size) {
    void *ptr = malloc(size);
    if (ptr == NULL) {
        perror("Out of memory");
        exit(1);
    }
    return ptr;
}

/* Also aborting realloc(). */
void *chatRealloc(void *ptr, size_t size) {
    ptr = realloc(ptr,size);
    if (ptr == NULL) {
        perror("Out of memory");
        exit(1);
    }
    return ptr;
}

/* ====================== 聊天服务实现 ======================== */

/* Create a new client bound to 'fd'. This is called when a new client
 * connects. As a side effect updates the global Chat state. */
struct client *createClient(int fd) {
    char nick[32]; // Used to create an initial nick for the user.
    int nicklen = snprintf(nick,sizeof(nick),"user:%d",fd);
    struct client *c = chatMalloc(sizeof(*c));
    socketSetNonBlockNoDelay(fd); // Pretend this will not fail.
    c->fd = fd;
    c->nick = chatMalloc(nicklen+1);
    memcpy(c->nick,nick,nicklen);
    assert(Chat->clients[c->fd] == NULL); // This should be available.
    Chat->clients[c->fd] = c;
    /* We need to update the max client set if needed. */
    if (c->fd > Chat->maxclient) Chat->maxclient = c->fd;
    Chat->numclients++;
    return c;
}

/* Free a client, associated resources, and unbind it from the global
 * state in Chat. */
void freeClient(struct client *c) {
    free(c->nick);
    close(c->fd);
    Chat->clients[c->fd] = NULL;
    Chat->numclients--;
    if (Chat->maxclient == c->fd) {
        /* Ooops, this was the max client set. Let's find what is
         * the new highest slot used. */
        int j;
        for (j = Chat->maxclient-1; j >= 0; j--) {
            if (Chat->clients[j] != NULL) Chat->maxclient = j;
            break;
        }
        if (j == -1) Chat->maxclient = -1; // We no longer have clients.
    }
    free(c);
}

/* Allocate and init the global stuff. */
void initChat(void) {
    Chat = chatMalloc(sizeof(*Chat));
    memset(Chat,0,sizeof(*Chat));
    /* No clients at startup, of course. */
    Chat->maxclient = -1;
    Chat->numclients = 0;

    /* Create our listening socket, bound to the given port. This
     * is where our clients will connect. */
    Chat->serversock = createTCPServer(SERVER_PORT);
    if (Chat->serversock == -1) {
        perror("Creating listening socket");
        exit(1);
    }
}

/* Send the specified string to all connected clients but the one
 * having as socket descriptor 'excluded'. If you want to send something
 * to every client just set excluded to an impossible socket: -1. */
void sendMsgToAllClientsBut(int excluded, char *s, size_t len) {
    for (int j = 0; j <= Chat->maxclient; j++) {
        if (Chat->clients[j] == NULL ||
            Chat->clients[j]->fd == excluded) continue;

        /* Important: we don't do ANY BUFFERING. We just use the kernel
         * socket buffers. If the content does not fit, we don't care.
         * This is needed in order to keep this program simple. */
        write(Chat->clients[j]->fd,s,len);
    }
}

/* The main() function implements the main chat logic:
 * 1. Accept new clients connections if any.
 * 2. Check if any client sent us some new message.
 * 3. Send the message to all the other clients. */
int main(void) {
    initChat();

    while(1) {
        fd_set readfds;
        struct timeval tv;
        int retval;

        FD_ZERO(&readfds);
        /* When we want to be notified by select() that there is
         * activity? If the listening socket has pending clients to accept
         * or if any other client wrote anything. */
        FD_SET(Chat->serversock, &readfds);

        for (int j = 0; j <= Chat->maxclient; j++) {
            if (Chat->clients[j]) FD_SET(j, &readfds);
        }

        /* Set a timeout for select(), see later why this may be useful
         * in the future (not now). */
        tv.tv_sec = 1; // 1 sec timeout
        tv.tv_usec = 0;

        /* Select wants as first argument the maximum file descriptor
         * in use plus one. It can be either one of our clients or the
         * server socket itself. */
        int maxfd = Chat->maxclient;
        if (maxfd < Chat->serversock) maxfd = Chat->serversock;
        retval = select(maxfd+1, &readfds, NULL, NULL, &tv);
        if (retval == -1) {
            perror("select() error");
            exit(1);
        } else if (retval) {

            /* If the listening socket is "readable", it actually means
             * there are new clients connections pending to accept. */
            if (FD_ISSET(Chat->serversock, &readfds)) {
                int fd = acceptClient(Chat->serversock);
                struct client *c = createClient(fd);
                /* Send a welcome message. */
                char *welcome_msg =
                    "Welcome to Simple Chat! "
                    "Use /nick <nick> to set your nick.\n";
                write(c->fd,welcome_msg,strlen(welcome_msg));
                printf("Connected client fd=%d\n", fd);
            }

            /* Here for each connected client, check if there are pending
             * data the client sent us. */
            char readbuf[256];
            for (int j = 0; j <= Chat->maxclient; j++) {
                if (Chat->clients[j] == NULL) continue;
                if (FD_ISSET(j, &readfds)) {
                    /* Here we just hope that there is a well formed
                     * message waiting for us. But it is entirely possible
                     * that we read just half a message. In a normal program
                     * that is not designed to be that simple, we should try
                     * to buffer reads until the end-of-the-line is reached. */
                    int nread = read(j,readbuf,sizeof(readbuf)-1);

                    if (nread <= 0) {
                        /* Error or short read means that the socket
                         * was closed. */
                        printf("Disconnected client fd=%d, nick=%s\n",
                            j, Chat->clients[j]->nick);
                        freeClient(Chat->clients[j]);
                    } else {
                        /* The client sent us a message. We need to
                         * relay this message to all the other clients
                         * in the chat. */
                        struct client *c = Chat->clients[j];
                        readbuf[nread] = 0;

                        /* If the user message starts with "/", we
                         * process it as a client command. So far
                         * only the /nick <newnick> command is implemented. */
                        if (readbuf[0] == '/') {
                            /* Remove any trailing newline. */
                            char *p;
                            p = strchr(readbuf,'\r'); if (p) *p = 0;
                            p = strchr(readbuf,'\n'); if (p) *p = 0;
                            /* Check for an argument of the command, after
                             * the space. */
                            char *arg = strchr(readbuf,' ');
                            if (arg) {
                                *arg = 0; /* Terminate command name. */
                                arg++; /* Argument is 1 byte after the space. */
                            }

                            if (!strcmp(readbuf,"/nick") && arg) {
                                free(c->nick);
                                int nicklen = strlen(arg);
                                c->nick = chatMalloc(nicklen+1);
                                memcpy(c->nick,arg,nicklen+1);
                            } else {
                                /* Unsupported command. Send an error. */
                                char *errmsg = "Unsupported command\n";
                                write(c->fd,errmsg,strlen(errmsg));
                            }
                        } else {
                            /* Create a message to send everybody (and show
                             * on the server console) in the form:
                             *   nick> some message. */
                            char msg[256];
                            int msglen = snprintf(msg, sizeof(msg),
                                "%s> %s", c->nick, readbuf);
                            if (msglen >= (int)sizeof(msg))
                                msglen = sizeof(msg)-1;
                            printf("%s",msg);

                            /* Send it to all the other clients. */
                            sendMsgToAllClientsBut(j,msg,msglen);
                        }
                    }
                }
            }
        } else {
            /* Timeout occurred. We don't do anything right now, but in
             * general this section can be used to wakeup periodically
             * even if there is no clients activity. */
        }
    }
    return 0;
}