源码学习:smallchat

源码:https://github.com/antirez/smallchat
可用于学习网络通信,源码很小。

考虑先基于第一版进行分析:
https://github.com/antirez/smallchat/blob/Episode-1/smallchat.c

先给出readme文件的翻译:
Smallchat
TLDR: 这只是一个我为几位朋友提供的编程示例。我上传了一个视频到我的YouTube频道,放大了代码,以查看从这个如此简单且故意破碎的示例中可以学到什么。将会有更多的视频和改进,详见本README文件末尾。

现在,让我来讲述完整的故事:

昨天我与我的几位朋友交谈,他们大多是前端开发者,对系统编程有些距离。我们在回忆IRC的旧时光。不可避免地,我说:编写一个非常简单的IRC服务器是每个人都应该做的经历(我向他们展示了我用TCL编写的实现,我很震惊我18年前就写了它:时间过得真快)。像那样的程序中有一些非常有趣的部分。一个进程执行多路复用,获取客户端状态,并在客户端有新数据时尝试快速访问这种状态,等等。

但后来讨论演变,我想,我来展示一个非常简单的C语言示例给你们看。你能写出最小的聊天服务器是什么样的?首先,要真正简化,我们不应该要求任何正式的客户端。即使不太完善,它应该能够与telnet或nc(netcat)一起使用。服务器的主要操作只是接收一些聊天行并将其发送给所有其他客户端,有时被称为扇出操作。但是,这将需要一个适当的readline()函数,然后进行缓冲等等。我们希望它更简单:让我们使用内核缓冲区来欺骗,假装我们每次都从客户端接收到一个完整形式的行(在实践中,这种假设通常成立,所以事情有点能用)。

好吧,通过这些技巧,我们可以实现一个聊天,甚至可以让用户在只有200行代码的情况下设置他们的昵称(当然要删除空格和注释)。由于我将这个小程序作为给我的朋友的示例,我决定也将其推送到Github上。

未来的工作
在接下来的几天里,我将继续修改这个程序以发展它。不同的演变步骤将根据我关于编写系统软件系列的YouTube剧集中的更改进行标记。这是我的计划(可能会改变,但大致上这是我想要涵盖的内容):

  1. 实现读写缓冲。
  2. 避免线性数组,使用字典数据结构来保存客户端状态。
  3. 编写一个适当的客户端:能够处理异步事件的行编辑。
  4. 实现频道。
  5. 从select(2)切换到更高级的API。
  6. 为聊天实现简单的对称加密。

不同的更改将由一个或多个YouTube视频进行介绍。完整的提交历史将保存在这个存储库中。

下面是第一版的源码,后面会与第二版源码进行对比说明:
smallchat.c – 读取客户端输入,发送给所有其他连接的客户端。
数据结构:最小化,且简单。实际核心就一个chatState对象Chat。

#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. 客户名,默认为0
};
/* 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.最大的 'clients' 槽位被占用的索引。struct client *clients[MAX_CLIENTS]; // Clients are set in the corresponding slot of their socket descriptor.客户端数组,用于存储连接到服务器的客户端信息。每个客户端通过其套接字描述符在数组中找到对应的位置。
};
struct chatState *Chat; // Initialized at startup.指向 struct chatState 结构的指针,用于表示整个聊天程序的全局状态。在程序启动时会初始化该结构。

api:最基本的socket
我调整一下学习顺序,从低到高,按被调用顺序看

先看内存申请。其实只是为了在内存不足时,先输出错误信息,并正常退出返回1,避免崩溃

/* 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;
}

初始化全局的唯一的数据结构对象,创建tcp服务器

/* Create a TCP socket lisetning 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;
}
/* 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); //创建tcp服务器if (Chat->serversock == -1) {perror("Creating listening socket");exit(1);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/169497.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

激活函数与其导数:神经网络中的关键元素

激活函数是神经网络中的重要组成部分&#xff0c;有力地推动了深度学习的发展。然而&#xff0c;仅仅了解和选择激活函数是不够的&#xff0c;我们还需要理解激活函数的导数。本文将详细介绍激活函数的概念、作用及其导数的重要性&#xff0c;并探究导数对神经网络训练的影响。…

【室内定位系统源码】UWB超宽带定位技术的特点和应用前景

uwb人员、物品定位系统源码&#xff0c;智慧工厂人员安全管理定位&#xff0c;高精度定位系统源码 UWB超宽带定位技术概念&#xff1a; 超宽带无线通信技术&#xff08;UWB&#xff09;是一种无载波通信技术&#xff0c;UWB不使用载波&#xff0c;而是使用短的能量脉冲序…

Presto+Alluxio数据平台实战

数新网络&#xff0c;让每个人享受数据的价值https://xie.infoq.cn/link?targethttps%3A%2F%2Fwww.datacyber.com%2F 一、Presto & Alluxio简介 Presto Presto是由Facebook开发的开源大数据分布式高性能 SQL查询引擎。 起初&#xff0c;Facebook使用Hive来进行交互式查询…

AI创作工具:Claude2注册保姆级教程

最近软件打算多接入几个AI写作平台&#xff0c;包括讯飞星火&#xff0c;百度文心&#xff0c;Claude2&#xff0c;这样就能给用户提供更多的写作选择 经过半天的调研&#xff0c;讯飞星火&#xff0c;百度文心一言&#xff0c;接入都比较简单&#xff0c;毕竟是国内的。 在调…

关于pytorch以及相关包的安装教程

一.查看自己电脑的配置 首先查看自己电脑的cuda的版本&#xff0c;WinR,敲入cmd打开终端 输入nvidia-smi&#xff0c;查看自己电脑的显卡等配置 这里要说明一下关于这个CUDA,它具有向后兼容性&#xff0c;这意味着支持较低版本的 CUDA 的应用程序通常也可以在较高版本的 CUD…

Jmeter接口自动化测试操作流程

在企业使用jmeter开展实际的接口自动化测试工具&#xff0c;建议按如下操作流程&#xff0c; 可以使整个接口测试过程更规范&#xff0c;更有效。 接口自动化的流程&#xff1a; 1、获取到接口文档&#xff1a;swagger、word、excel ... 2、熟悉接口文档然后设计测试用例&am…

播放器开发(二):了解FFmpeg与SDL常用对象和函数

学习课题&#xff1a;逐步构建开发播放器【QT5 FFmpeg6 SDL2】 前言 这一篇内容就是简单的了解一遍一些常用的函数名称和作用&#xff0c;混个眼熟。 能看源码的就去看源码&#xff01;&#xff01;&#xff01; 能看源码的就去看源码&#xff01;&#xff01;&#xff01; …

C#每天复习一个重要小知识day5:枚举与switch是天生一对

因为枚举一般用来表示条件和类型等等&#xff0c;所以它一般用条件分支来表现。所以枚举与switch是天生一对&#xff0c;因为很方便。&#xff08;用if语句也可&#xff0c;但是没switch方便&#xff09; 简单的举例&#xff1a; namespace 精细练习 {enum E_Player {Main,Ot…

前端大厂(腾讯、字节跳动、阿里......)校招面试真题解析,让你面试轻松无压力!

前言 校招很重要&#xff0c;应届生的身份很珍贵&#xff01;在校招的时候与我们竞争的大部分都是没有工作经验的学生&#xff0c;而且校招企业对学生的包容度高&#xff0c;一般对企业来说&#xff0c;社招更看重实际工作经验&#xff0c;而校招更愿意“培养人”&#xff0c;校…

node fs模板及蓝桥案例实战

文章目录 介绍文件写入writeFile 异步写入writeFileSync 同步写入appendFile / appendFileSync 追加写入createWriteStream 流式写入 文件读取readFile 异步读取readFileSync 同步读取createReadStream 流式读取 文件移动与重命名文件删除文件夹操作mkdir / mkdirSync 创建文件…

python操作redis

操作单redis 需要安装redis模块&#xff1a;pip install redis demo&#xff1a; #!/usr/bin/env python3 # coding utf-8import redis import threadingdef a():conn redis.Redis(host"192.168.1.66", port6379, password"123456", db6,# decode_res…

数据库表结构导出成Excel或Word格式

前言 该工具主要用于导出excel、word&#xff0c;方便快速编写《数据库设计文档》&#xff0c;同时可以快速查看表的结构和相关信息。 本博客仅作记录&#xff0c;最新源码已经支持多种数据库多种格式导出&#xff0c;有兴趣的可移步源码作者地址&#xff1a;https://gitee.co…

模运算Mod

模运算是一种数学运算&#xff0c;通常用于计算机编程和数论中。它表示的是两个整数相除后的余数。在编程中&#xff0c;模运算符&#xff08;%&#xff09;用于计算一个数除以另一个数的余数。在数论中&#xff0c;模运算可以用于解决一些与整数除法有关的问题&#xff0c;如最…

RK3568驱动指南|第八篇 设备树插件-第73章 设备树插件使用实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

二十三种设计模式全面解析-深入探讨状态模式的高级应用技术:释放对象行为的无限可能

在软件开发中&#xff0c;状态管理是一个常见的挑战。当对象的行为随着内部状态的变化而变化时&#xff0c;有效地管理对象的状态和相应的行为变得至关重要。在这方面&#xff0c;状态模式提供了一种优雅而灵活的解决方案。它允许对象在运行时根据内部状态的改变而改变其行为&a…

【Redis】前言--redis产生的背景以及过程

一.介绍 为什么会出现Redis这个中间件&#xff0c;从原始的磁盘存储到Redis中间又发生了哪些事&#xff0c;下面进入正题 二.发展史 2.1 磁盘存储 最早的时候都是以磁盘进行数据存储&#xff0c;每个磁盘都有一个磁道。每个磁道有很多扇区&#xff0c;一个扇区接近512Byte。…

MYSQL基础之【创建数据表,删除数据表】

文章目录 前言MySQL 创建数据表通过命令提示符创建表使用PHP脚本创建数据表 MySQL 删除数据表在命令提示窗口中删除数据表使用PHP脚本删除数据表 后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主…

三菱PLC编码器转速测量功能块(梯形图和ST代码)

编码器转速测量功能块算法公式详细讲解请参考下面文章链接: SMART PLC编码器转速测量功能块(高速计数器配置+梯形图)-CSDN博客文章浏览阅读427次。里工业控制张力控制无处不在,也衍生出很多张力控制专用控制器,磁粉制动器等,本篇博客主要讨论PLC的张力控制相关应用和算法,…

制作docker镜像

1. 什么是Docker镜像 Docker镜像是Docker容器的模板&#xff0c;是Docker容器运行的基础。 2. 制作docker镜像的方式与流程 2.1 基于Dockerfile制作镜像 2.1.1 Dockerfile的基本语法 # 指定基础镜像 FROM <image> # 维护者信息 MAINTAINER # 指定镜像标签 LABEL <…

Android frameworks 开发总结之十(lock screen message Battery Last full charge)

1.設置lock screen message後不显示 XXXt設備設置lock screen message後&#xff0c;發現鎖頻界面不顯示內容&#xff0c;像時間日期都不顯示。只在右上角顯示一個鎖圖標&#xff0c;需要向下滑動一下才能顯示出來。布局文件位置: frameworks/base/packages/SystemUI/res-keygu…