ZRTP交叉编译与移植

news2025/1/20 20:12:43

1 ZRTP源码下载

这里采用的是libzrtp来自于freeswitch:libs/libzrtp。

2 ZRTP交叉编译

zrtp编译比较简单,采用configure进行编译在根目录心中zrtp编译脚本,只需要指定交叉编译工具链和安装地址即可。脚本如下所示:

unset CC CFLAGS LDFLAGS CPPFLAGS CPP LD STRIP
./configure --host=arm-linux-androideabi --prefix=`pwd`/../objects/ 

成果物如下所示include和lib库:
在这里插入图片描述
在这里插入图片描述

3 ZRTP移植

zrtp移植主要对zrtp库进行封装,对外提供初始化和加密解密能力。接口设计如下:

3.1 API设计

typedef void ZrtpEventObserver(int id, BOOL encrypt);

typedef struct zrtp_handle_t zrtp_handle_t;

typedef struct zrtp_handle_t{
	//加密rtp
 	void (*encrypt_rtp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data,unsigned char* out_data,int bytes_in,	int* bytes_out);
	//解密rtp
 	void (*decrypt_rtp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data, unsigned char* out_data, int bytes_in, int* bytes_out);
	//加密rtcp
 	void (*encrypt_rtcp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data,unsigned char* out_data, int bytes_in,int* bytes_out);
	//解密rtcp
 	void (*decrypt_rtcp)(zrtp_handle_t *pthis, int channel, unsigned char* in_data, unsigned char* out_data, int bytes_in, int* bytes_out);
 	
 	BOOL (*is_enabled)(zrtp_handle_t *pthis);

 	void *priv;
}zrtp_handle_t;

//构建zrtp会话
int zrtp_handle_alloc(zrtp_handle_t **ppthis, const WebRtc_Word32 id, ZrtpEventObserver *observer);
//释放zrtp会话
void zrtp_handle_free(zrtp_handle_t *pthis);
//初始化
int zrtp_handle_init();
//反初始化
void zrtp_handle_denit();

3.2 初始化

初始化只需要初始化一次,初始化需要注册发送回调函数,这里协商发送的数据包构造好后最终是有这个接口on_send_packet返回到应用发送。

int zrtp_handle_init()
{
	zrtp_config_defaults(&g_zrtp_config);
	zrtp_randstr2((unsigned char *)g_zrtp_config.client_id, sizeof(zrtp_client_id_t));
//	zrtp_randstr2((unsigned char *)g_zrtp_config.zid, sizeof(zrtp_zid_t));
	g_zrtp_config.lic_mode = ZRTP_LICENSE_MODE_ACTIVE;
	g_zrtp_config.cb.misc_cb.on_send_packet			= on_send_packet;
	g_zrtp_config.cb.event_cb.on_zrtp_protocol_event	= on_zrtp_protocol_event;
	g_zrtp_config.cb.event_cb.on_zrtp_security_event	= on_zrtp_security_event;
	g_zrtp_config.cb.event_cb.on_zrtp_secure			= on_zrtp_secure;
	g_zrtp_config.cb.event_cb.on_zrtp_not_secure		= on_zrtp_not_secure;

	zrtp_status_t s = zrtp_init(&g_zrtp_config, &g_pzrtp_global);
	if (zrtp_status_ok != s) {
		printf("ZRTP init failed, status = %d \n", s);
		return -1;
	}
	
	return 0;
}

void zrtp_handle_denit()
{
	if (NULL != g_pzrtp_global) {
		zrtp_down(g_pzrtp_global);
		g_pzrtp_global = NULL;
	}
}

3.3 会话实例

每一路会话需要实例化一个zrtphandle对象,需要传入一个随机的zrtpid,和观察者。

int zrtp_handle_alloc(zrtp_handle_t **ppthis, const WebRtc_Word32 id, ZrtpEventObserver *observer)
{
    if(!ppthis)
        return -1;      

	zrtp_handle_t *phl = (zrtp_handle_t *)malloc(sizeof(zrtp_handle_t));	
	if(!phl)
	{
		return -1;
	}

	priv_t *ppriv = (priv_t *)malloc(sizeof(priv_t));	
	if(!ppriv)
	{
	    free(phl);
		return -1;
	}

	memset(phl, 0, sizeof(zrtp_handle_t));
	memset(ppriv, 0, sizeof(priv_t));
	
    phl->priv = ppriv;   

	ppriv->__zrtp_session = NULL;
	ppriv->__zrtp_audio = NULL;
	ppriv->__lSSRC = 0;
	ppriv->__rSSRC = 0;
	ppriv->__stream_seq = 0;
	ppriv->__stream_timestamp = 0;
	ppriv->__is_first_start_stream = 0;
	ppriv->__is_need_send_hello = 0;
	ppriv->__is_zrtp_enable = 0;
	ppriv->__obverserptr = observer;
	ppriv->id = id;
	  
	ppriv->__buffer_out_data = (char *)malloc(IP_PACKET_SIZE);
    phl->audio_encrypt_rtp = encrypt;
    phl->audio_decrypt_rtp = decrypt;
    phl->audio_encrypt_rtcp = encrypt_rtcp;
    phl->audio_decrypt_rtcp = decrypt_rtcp;

    *ppthis = phl;
    
    return 0;
}

void zrtp_handle_free(zrtp_handle_t *pthis) 
{
	if(!pthis)
		return;
	
	DisableZsrtp(pthis);
	
	priv_t *ppriv = pthis->priv;
	
	if (NULL != ppriv->__buffer_out_data) {
		free(ppriv->__buffer_out_data);
		ppriv->__buffer_out_data = NULL;
	}

	if (pthis->priv){
		free(pthis->priv);
		pthis->priv = NULL;
	}	
	free(pthis);
	pthis = NULL;
}

3.4 加解密实现

加解密会用首先会进入到协商流程zrtp_stream_start完成协商,之后再进入加解密流程zrtp_process_rtp。

int encrypt(zrtp_handle_t *pthis,
		int channel,
		unsigned char* in_data,
		unsigned char* out_data,
		int bytes_in,
		int* bytes_out) {

	
    priv_t *ppriv = pthis->priv;
    
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        return 0;
    }

	if(ppriv->__is_need_send_hello){
	   
		zrtp_stream_start(ppriv->__zrtp_audio, ppriv->__lSSRC);
		   
		ppriv->__is_need_send_hello = RL_FALSE;
	}
   
	if(!ppriv->__buffer_out_data_consumed){
		++ppriv->__buffer_out_data_retry;
		
		memcpy(out_data, ppriv->__buffer_out_data, ppriv->__buffer_out_data_bytes);
		*bytes_out = ppriv->__buffer_out_data_bytes;
		ppriv->__buffer_out_data_consumed = RL_TRUE;
		return 0;
	}

	if (-1 == ppriv->__voice_encrypt_status && ppriv->__buffer_out_data_retry == ZRTP_COUNT_THRESHOLD_DEFAULT){
		// LinKy: Rollback to unecrypt, this operation will close zrtp session!
		
		UpdateEncryptStatus(pthis, RL_FALSE);
		//return 0;
	}

	if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE) {
		// Not ready, use original data	
		
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return 1;
	}

	char packet[IP_PACKET_SIZE];
	ZRTP_UNALIGNED(zrtp_rtp_hdr_t) *rtp_hdr = (zrtp_rtp_hdr_t*)packet;
	
	/* Fill RTP Header according to the specification */
	zrtp_memset(rtp_hdr, 0, sizeof(zrtp_rtp_hdr_t));
	rtp_hdr->version = 2;						/* Current RTP version 2 */
	rtp_hdr->pt = 0;							/* PCMU padding type */
	rtp_hdr->ssrc = zrtp_hton32(ppriv->__lSSRC);		/* Use stream Identifier as it's SSRC */
	if (ppriv->__stream_seq >= 0xFFFF) {
		ppriv->__stream_seq = 0;
	}
	
	rtp_hdr->seq = libzrtp_swap16(ppriv->__stream_seq++);
	rtp_hdr->ts = zrtp_hton32(ppriv->__stream_timestamp);
	ppriv->__stream_timestamp += 20;					// LinKy: Assume the interval is 20ms
	zrtp_memcpy(packet + sizeof(zrtp_rtp_hdr_t), in_data, bytes_in);
	unsigned int size = sizeof(zrtp_rtp_hdr_t) + bytes_in;

	zrtp_status_t s = zrtp_process_rtp(ppriv->__zrtp_audio, packet, &size);
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			memcpy(out_data, packet, size);
			*bytes_out = size;
			return 2;
		}
 		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec

			// LinKy: Shall we rollback to unencrypt here?
			return 3;
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
			return 4;
		}
 		break;
	}
	return 0;
}

int decrypt(zrtp_handle_t *pthis,
	    int channel,
	    unsigned char* in_data,
	    unsigned char* out_data,
	    int bytes_in,
	    int* bytes_out) {
	unsigned int size = bytes_in;
	
	
	priv_t *ppriv = pthis->priv;
	
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        
        return 0;
    }

	
	if(ppriv->__is_need_send_hello){
		zrtp_stream_start(ppriv->__zrtp_audio, ppriv->__lSSRC);
		
		ppriv->__is_need_send_hello = RL_FALSE;
	}

	
	//if zrtp success,ppriv->__buffer_in_data_retry Approximately equals 18;
	if (-1 == ppriv->__voice_encrypt_status && ppriv->__buffer_in_data_retry >= ZRTP_COUNT_THRESHOLD_DEFAULT){
	//if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE){
		// Not ready, use original data
		
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return 1;
	}
	else if(ppriv->__voice_encrypt_status == 0)
	{
		
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return 1;
	}
	
	ppriv->__buffer_in_data_retry++;
	
	char packet[IP_PACKET_SIZE];
	memcpy(packet, in_data, bytes_in);
	zrtp_status_t s = zrtp_process_srtp(ppriv->__zrtp_audio, packet, &size);

	
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			
			char *body = packet + sizeof(zrtp_rtp_hdr_t);
			memcpy(out_data, body, size - sizeof(zrtp_rtp_hdr_t));
			*bytes_out = size - sizeof(zrtp_rtp_hdr_t);
			return 2;
		}
		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec

			// LinKy: Yep, we drop it, use original data to play!
			
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
			return 3;
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//
			// LinKy: Be careful! This may cause noise if data is encrypted actually!
			
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
			return 4;
		}
		break;
	}
	return 0;
}

void encrypt_rtcp(zrtp_handle_t *pthis,
    int channel,
    unsigned char* in_data,
    unsigned char* out_data,
    int bytes_in,
    int* bytes_out) {
        
    priv_t *ppriv = pthis->priv;
	unsigned int size = bytes_in;
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        return;
    }

	if(ppriv->__is_need_send_hello){
		zrtp_stream_start(ppriv->__zrtp_audio, ppriv->__lSSRC);
		ppriv->__is_need_send_hello = RL_FALSE;
	}
	
	if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE) {
		// Not ready, use original data
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return;
	}
	
	char packet[IP_PACKET_SIZE];
	ZRTP_UNALIGNED(zrtp_rtcp_hdr_t) *rtcp_hdr = (zrtp_rtcp_hdr_t*)packet;
	
	/* Fill RTCP Header according to the specification */
	rtcp_hdr->rc = 0;
	rtcp_hdr->version = 2;
	rtcp_hdr->ssrc = zrtp_hton32(ppriv->__lSSRC);

	
	/* Get RTP body from PGP words lists. Put RTCP marker at the beginning */
	zrtp_memcpy(packet + sizeof(zrtp_rtcp_hdr_t), "RTCP", 4);		
	zrtp_memcpy(packet + sizeof(zrtp_rtcp_hdr_t) + 4, in_data, bytes_in);
	size = sizeof(zrtp_rtcp_hdr_t) + bytes_in + 4;
	/* RTCP packets sould be 32 byes aligned */
	size += (size % 4) ? (4 - size % 4) : 0;
	
	zrtp_status_t s = zrtp_process_rtcp(ppriv->__zrtp_audio, packet, &size);
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			memcpy(out_data, packet, size);
			*bytes_out = size;
		}
		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec
			
			// LinKy: Shall we rollback to unencrypt here?
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
		}
		break;
	}
}

void decrypt_rtcp(zrtp_handle_t *pthis,
	    int channel,
	    unsigned char* in_data,
	    unsigned char* out_data,
	    int bytes_in,
	    int* bytes_out) {
	        
	priv_t *ppriv = pthis->priv;
	unsigned int size = bytes_in;
    if (!ppriv->__is_zrtp_enable 
			|| NULL == in_data || NULL == out_data
        	|| 0 > bytes_in || NULL == bytes_out) {
        // invalid
        return;
    }

	if(ppriv->__is_first_start_stream){
		zrtp_stream_start(ppriv->__zrtp_audio, channel);
		ppriv->__is_first_start_stream = RL_FALSE;
	}

	if (ppriv->__zrtp_audio->state != ZRTP_STATE_SECURE) {
		// Not ready, use original data
		memcpy(out_data, in_data, bytes_in);
		*bytes_out = bytes_in;
		return;
	}
	
	char packet[IP_PACKET_SIZE];
	memcpy(packet,in_data,bytes_in);
	zrtp_status_t s = zrtp_process_srtcp(ppriv->__zrtp_audio, packet, &size);
	switch (s) {
	case zrtp_status_ok: {
			//
			// Packet was successfully decrypted. Dont forget that packet
			// size was changed during decryption. New size now in size 
			//
			char *body = packet + sizeof(zrtp_rtp_hdr_t);
			memcpy(out_data, body, size - sizeof(zrtp_rtp_hdr_t));
			*bytes_out = size - sizeof(zrtp_rtp_hdr_t);
		}
		break;
	case zrtp_status_drop: {
			//
			// This is a protocol ZRTP packet or masked RTP media.
			// In either case the packet must be dropped to protect your 
			// private data and media codec

			// LinKy: Yep, we drop it, use original data to play!
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
		}
		break;
	case zrtp_status_fail: {
			//
			// This is some kind of error - see logs for more information.
			// Don't put such packet to the network. It is not secure.
			//

			// LinKy: Be careful! This may cause noise if data is encrypted actually!
			memcpy(out_data, in_data, bytes_in);
			*bytes_out = bytes_in;
		}
		break;
	}
}

4 ZRTP抓包分析

一个完整的ZRTP协商流程,首先是hello进双方加密方式的交换,之后会进行一个收到的应答,然后是commit消息确认HMAC秘钥,DHPart消息交换公钥,Confirm消息确认签名,最后是ConfACK应答。下面是wareshark抓包显示的交互流程。
在这里插入图片描述

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

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

相关文章

文心一言 VS 讯飞星火 VS chatgpt (107)-- 算法导论10.1 5题

五、用go语言,栈插入和删除元素只能在同一端进行,队列的插入操作和删除操作分别在两端进行,与它们不同的,有一种双端队列(deque),其插入和删除操作都可以在两端进行。写出4个时间均为 O(1)的过程,分别实现在…

Python之字符串分割替换移除

Python之字符串分割替换移除 分割 split(sepNone, maxsplit-1) -> list of strings 从左至右sep 指定分割字符串,缺省的情况下空白字符串作为分隔符maxsplit 指定分割的次数,-1 表示遍历整个字符串立即返回列表 rsplit(sepNone, maxsplit-1) -> …

【熬夜爆肝版】JAVA基础入门专栏——1.JAVA开发入门

JAVA开发入门 1、Java概述1)起源2)特点3)应用领域 2、JDK1)定义2)作用3)组成4)JDK版本与兼容性5)JDK的安装与配置6)JDK的发行版 3、系统环境变量1)定义2&…

【Java项目推荐之黑马头条】你的登录鉴权业务是怎么实现的?

前言 在学习Java的路上还是遇到了很多不错的好项目的,今天分享给大家,希望能对大家面试有所帮助! 后续会继续推荐其他好的项目,这次推荐的是B站开源的视频黑马头条项目,来吧学会它一起去虐面试官!&#x…

【C语言初阶】初识C语言

目录 一、什么是C语言 二、第一个C语言程序 三、数据类型 类型的使用: 四、变量、常量 4.1 定义变量的方法 4.2 变量的命名 4.3 变量的分类 4.4 变量的使用 4.5 变量的作用域和生命周期 4.5.1 作用域 4.5.2 生命周期 4.6 常量 五、字符串转义字符注释 …

【计算机组成 课程笔记】7.2 DRAM和SRAM

课程链接: 计算机组成_北京大学_中国大学MOOC(慕课) 7 - 2 - 702-DRAM和SRAM(13-22--)_哔哩哔哩_bilibili 从【计算机组成 课程笔记】7.1 存储层次结构概况_Elaine_Bao的博客-CSDN博客中,我们了解到:SRAM比较快&#x…

x64内核实验5-API进0环

x64内核实验5-API进0环 今天开始我们来分析系统api进0环的过程 系统调用3环部分的过程分析 先写一个应用程序然后我们调用一个readprocessmemory的系统api #include <Windows.h> #include <stdio.h>DWORD32 testVal 111;int main() {HANDLE h GetCurrentProc…

DatenLord前沿技术分享 No.36

达坦科技专注于打造新一代开源跨云存储平台DatenLord&#xff0c;通过软硬件深度融合的方式打通云云壁垒&#xff0c;致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题&#xff0c;以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。在本周…

ChatGPT已进化到会看图和说话了,上教程

HI&#xff0c;同学们&#xff0c;我是赤辰&#xff0c;本期是第14篇AI工具类教程&#xff0c;文章底部准备了粉丝福利&#xff0c;看完后可领取&#xff01; ChatGPT又又又升级&#xff01;这次是支持语音聊天和图像问答。 这意味着用户现在除了键盘文本输入外&#xff0c;还可…

【JavaEE】多线程进阶(一)饿汉模式和懒汉模式

多线程进阶&#xff08;一&#xff09; 文章目录 多线程进阶&#xff08;一&#xff09;单例模式饿汉模式懒汉模式 本篇主要引入多线程进阶的单例模式&#xff0c;为后面的大冰山做铺垫 代码案例介绍 单例模式 非常经典的设计模式 啥是设计模式 设计模式好比象棋中的 “棋谱”…

三一充填泵:煤矿矸石无害化充填,煤炭绿色高效开采的破局利器

富煤贫油少气是我国的能源禀赋特征&#xff0c;决定了我国以煤炭为主的能源结构&#xff0c;煤炭为国民经济发展提供了重要的基础。煤炭开采过程会对土地、地下水、空气等环境造成较大的污染&#xff0c;但大宗固废煤矸石无害化充填的技术手段可以有效改善这样的情况&#xff0…

LabVIEW使用机器学习分类模型探索基于技能课程的学习

LabVIEW使用机器学习分类模型探索基于技能课程的学习 教育中的学习评估对教育工作者来说是一项繁琐的工作&#xff0c;但评估的好处是显着的。由于其开放性和复杂性&#xff0c;使用传统的评估方法为学生提供及时的支持一直具有挑战性。在Covid-19大流行期间突然转向在线学习&…

总结二:linux面经

文章目录 1、 Linux中查看进程运行状态的指令、查看内存使用情况的指令、tar解压文件的参数。2、文件权限怎么修改&#xff1f;3、说说常用的Linux命令&#xff1f;4、说说如何以root权限运行某个程序&#xff1f;5、 说说软链接和硬链接的区别&#xff1f;6、说说静态库和动态…

QT聊天室阶段性记录(完善中:注册功能,数据库存储)

server.h #ifndef SERVERDEMO_H #define SERVERDEMO_H#include <QObject> #include <QTcpServer> #include <QMap> #include <QSqlDatabase> //数据库管理类 #include <QSqlQuery> //执行sql语句的类 #include <QSqlRecord> //数据库…

最全MacBook选购指南 | 看完你就知道怎么买

最全MacBook选购指南 | 看完你就知道怎么买 作为MacBook的老用户大大小小的型号也都用了不少 那这么多台MacBook到底怎么选呢&#x1f4a1; . ☑️M1和Intel的MacBook有什么差别呢&#xff1f; 下半年苹果发布的两款MacBook都是苹果自研的芯片M1。在此之前苹果一直用的都是Inte…

解决报错:模块“react-redux“没有导出的成员“TypedUseSelectorHook”

在react整合typescript,redux时&#xff0c;写hook.ts时报这个错&#xff1a;模块"react-redux"没有导出的成员“TypedUseSelectorHook” 现象如下&#xff1a; 原因&#xff1a;react-redux版本太低&#xff0c;至少要升级到7.2.3以后才能包含TypedUseSelectorHook…

Clion中使用C/C++开发stm32程序

前言 从刚开始学习阶段&#xff0c;一直是用的keil5开发stm32程序&#xff0c;自从看到稚晖君推荐的CLion开发嵌入式程序后&#xff0c;这次尝试在CLion上开发stm32程序。 1、配置CLion用于STM32开发的环境 这里我就不详细写了&#xff0c;没必要重新写&#xff0c;网上教程很多…

【Java项目推荐之黑马头条】自媒体文章实现异步上下架(使用Kafka中间件实现)

自媒体文章上下架功能完成 需求分析 流程说明 接口定义 说明接口路径/api/v1/news/down_or_up请求方式POST参数DTO响应结果ResponseResult DTO Data public class WmNewsDto {private Integer id;/*** 是否上架 0 下架 1 上架*/private Short enable;}ResponseResult 自媒…

哈希/散列--哈希表[思想到结构][==完结版==]

文章目录 1.何为哈希?1.1百度搜索1.2自身理解1.3哈希方法/散列方法1.4哈希冲突/哈希碰撞1.5如何解决?哈希函数的设计 2.闭散列和开散列2.1闭散列/开放定址法2.2开散列/链地址法/开链法1.概念2.容量问题3.字符串问题4.开散列性能测试5.开散列与闭散列比较 3.代码实现[配备详细…