自写项目——哈夫曼编译码及客户端服务端交互实现

1.项目要求

哈夫曼编译码

题目要求:编写一个用哈夫曼实现的编码压缩器,语言不限!

实现英文字符编译码的功能;实现译码的压缩解压功能;要有较美观的图形用户界面;从客户端发送一段字符给服务器端,服务器端能成功的返回这段字符经过哈夫曼编译码之后的结果!

编写步骤:

①编写较美观的图形用户界面;

②得到客户端所发送的字符信息,并进行统计,每个字符 的频度作为哈夫曼树叶子结点的权值,并构建哈夫曼树;

③进行哈夫曼树的编码和译码;

④自行选取5~7个二进制码来进行压缩;

⑤将压缩好的字符发送到服务器端,并接受;

⑥解压----译码-----编码-----还原成原来的字符!

2.项目实现

直接上代码(其中有详细注释,ubuntu 16.04 编译通过!):

clien.cpp

#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#define BUFFER_SIZE 1024

#define MaxValue 10000//权值最大值
#define MaxBit 55//最大编码位数
#define MaxN 55//最大结点个数 

//准备工作
int sockfd;//
char* IP = "127.0.0.1";//该地址请写测试服务器的IP,本机IP,回送地
short PORT = 10222;//端口号
typedef struct sockaddr SA;//类型转换
char message[100];
char demessge[100];

void init()
{
    printf("客户端开始启动\n");
    sockfd = socket(AF_INET,SOCK_STREAM,0);//启动socket
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = inet_addr(IP);
    if(connect(sockfd,(SA*)&addr,sizeof(addr))==-1){//向服务器请求建立链接
        perror("无法连接到服务器");
        printf("客户端启动失败\n");
        exit(-1);
    }
    printf("客户端启动成功\n");
}
//开始通信

void code(int * count)
{
    int t;
    printf("\nplease input message:");
    scanf("%s",message);
    send(sockfd,message,sizeof(message),0);// 把文件大小数据发送
    recv(sockfd,count,sizeof(int),0);//得到编码后的结果大小
    printf("\nthe result:\n");
    for(int i=0;i<*count;i++)
    {
        recv(sockfd,&t,sizeof(int),0);//一个一个接受,并输出
        if(t == 2)
            printf(" ");
        else
            printf("%d",t);
    }
    getchar();
    getchar();
}

void decode()
{
    char result[100];
    printf("\nplease input the message you want tanslate:");
    scanf("%s",demessge);//得到要译的码
    send(sockfd,demessge,sizeof(demessge),0);//发送到服务端
    recv(sockfd,result,sizeof(result),0);//从服务端接受到译码后的结果
    printf("\nthe result:\n");
    printf("%s",result);
    getchar();
    getchar();
}

void start()
{
    int count=0;
    char result[100];
    int choice=1;
    int judge;
   //菜单
    do{
        judge=1;
        printf("\n\t********************************************\n");
        printf("\t**            1.字符压缩                    **\n");
        printf("\t**            2.字符解压                    **\n");
        printf("\t**            0.退出                        **\n");
        printf("\t********************************************\n");
        printf("\t请选择:");
        scanf("%d",&choice);

        if(choice == 1||choice == 0||choice == 2)
            send(sockfd,&choice,sizeof(int),0);//发送用户选择信息
        else
            continue;

        recv(sockfd,&judge,sizeof(int),0);//接受判断标准,判断其是否为错误

        if(judge==1) {//如果错误,提示先用1选项建树
            printf("there is no haffe tree! \nplease set up the tree first!\n");
            continue;
        }

        switch(choice)
        {
            case 1 :
                code(&count);//编码
                break;
            case 2 :
                decode();//译码
                break;
            default:
                break;
        }
    }while(choice != 0);

    close(sockfd);//关闭socket
}

void sig_close(int i)
{
    //关闭客户端的描述符
    close(sockfd);
    exit(0);
}

int main()
{
    int type;
    signal(SIGINT,sig_close);//关闭CTRL+C
    init();//启动并连接服务器
    start();//开始与服务器进行通信。
    return 0;
}

server.cpp

#include <stdio.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#define MaxValue 10000//权值最大值
#define MaxBit 55//最大编码位数
#define MaxN 55//最大结点个数 

char message[100];
//准备工作

int sockfd;//
char* IP = "127.0.0.1";//服务器IP
short PORT = 10222;//端口号
typedef struct sockaddr SA;//类型转换

/*====================================
=1.构造哈夫曼树
=2.构造哈夫曼编码
=3.构造哈夫曼解码
====================================*/

//哈夫曼树的结点结构
typedef struct
{
	char wei;
	int weight;//权值
	int flag;//标记
	int parent;// 双亲结点下一标
	int leftChild;//左孩子下标
	int rightChild;//右孩子下标
 } HaffNode;

 //哈夫曼编码结构
typedef struct
{
 	int bit[MaxN];//存放哈夫曼编码的数组
 	int start;//编码的起始下标
 	int weight;//字符权值
 }Code;
typedef struct
{
	char ch;
	int  weight;
}STR;

int sear_weight(char ch[],int weight[],char ch2[])
{
	 STR str[27]={{' ',183},{'a',64},{'b',13},{'c',22},{'d',32},{'e',103},
	             {'f',21},{'g',15},{'h',47},{'i',57},{'j',1},{'k',5},
				 {'l',32},{'m',20},{'n',57},{'o',63},{'p',15},{'q',1},
				 {'r',48},{'s',51},{'t',80},{'u',23},{'v',8},{'w',18},
				 {'x',1},{'y',16},{'z',1}};
	int i,k=0;

	for(i=0;ch[i]!='\0';i++)
	{
		if(ch[i]==' ')
		{
		    ch2[i]=' ';
		    weight[i]=183;
		}
		else if(ch[i]>=65&&ch[i]<=90)
		{
			k=ch[i];
			ch2[i]=ch[i];
			weight[i]=str[k-64].weight;
		}
		else if(ch[i]>=97&&ch[i]<=122)
		{
			k=ch[i];
			ch2[i]=ch[i];
			weight[i]=str[k-96].weight;
		}
		else
		{
			return 1;
		}
	}
}

  // 建立叶节点个数为n,权值数组为weight的哈夫曼树
void HaffTree(int weight[],int n,HaffNode haffTree[],char ch2[])
{
  	int i,j,m1,m2,x1,x2;

  	//初始化哈夫曼树,n个叶结点的二叉树共有2n-1个结点
	for(i=0; i<2*n-1; i++)
	{
	  	if(i<n)
		{
		  haffTree[i].wei=ch2[i];
		  haffTree[i].weight = weight[i];//叶节点
		}
	  	else haffTree[i].weight = 0;//非叶结点

	  	haffTree[i].parent      = -1;
	  	haffTree[i].flag        = 0;//是否加入二叉树
		haffTree[i].leftChild   = -1;
		haffTree[i].rightChild  = -1;
	}

	//构造哈夫曼树haffTree的n-1个非叶结点
	for(i=0; i<n-1; i++)
	{
		m1=m2=MaxValue;
		x1=x2=0;
		for(j=0; j<n+i; j++)
		{
			if(haffTree[j].weight<=m1 && haffTree[j].flag==0)
			{
			  	m2=m1;
			  	x2=x1;
			  	m1=haffTree[j].weight;
			  	x1=j;
			}
			else if(haffTree[j].weight<=m2 && haffTree[j].flag==0)
			{
				m2=haffTree[i].weight;
				x2=j;
			}
	    }
		//将找出的两棵权值最小的子树合并为一棵子树
		haffTree[x1].parent=n+i;
		haffTree[x2].parent=n+i;
		haffTree[x1].flag=1;
		haffTree[x2].flag=1;
		haffTree[n+i].weight=haffTree[x1].weight+haffTree[x2].weight;
		haffTree[n+i].leftChild=x1;
		haffTree[n+i].rightChild=x2;
    }
}  

//由n个结点的哈夫曼树->构造哈夫曼编码
void HaffCode(HaffNode haffTree[],int n,Code haffCode[])
{
	Code *cd = (Code *)malloc(sizeof(Code));
	int i,j,child,parent;

	//求n个叶结点的哈夫曼编码
	for(i=0; i<n; i++)
	{
	    cd->start = n-1;//权值越小,编码越长
		cd->weight=haffTree[i].weight;
		child=i;//从小到大依次编码
		parent=haffTree[child].parent;
		//刨根问底->从叶结点沿路直到根结点
		while(parent !=-1)//-1根结点
		{
			if(haffTree[parent].leftChild==child)
				cd->bit[cd->start]=0;
			else
				cd->bit[cd->start]=1;

			cd->start--;//从后往前依次填码
			child=parent;
			parent=haffTree[child].parent;
		}
		//保存每个叶结点的信息
		for(j=cd->start+1; j<n; j++)
			haffCode[i].bit[j]=cd->bit[j];//编码
		haffCode[i].start=cd->start+1;//编码起始位
		haffCode[i].weight=cd->weight;//权值 

	}
}

void haffyima(HaffNode haffTree[],int n,char demessage[],int weight[],char message[])
{
	int i,j,k=0;
	HaffNode haffTree1;
    int len=strlen(demessage);
	for(i=0;i<len;i++)
	{
		haffTree1=haffTree[2*n-2];

		for(;i<len;i++)
		{
			if(demessage[i]=='1')
			{
				haffTree1=haffTree[haffTree1.rightChild];
			}
			else
				haffTree1=haffTree[haffTree1.leftChild];
            if(haffTree1.rightChild == -1 && haffTree1.leftChild == -1)
                break;
		}
		for(j=0;j<n;j++)
		{
		    if(haffTree1.weight==weight[j])
		    {
                message[k++]=haffTree1.wei;
		        break;
            }
		}
	}
}   

//初始化服务器的网络,创建socket
void init()
{
    printf("服务器开始启动..\n");
    sockfd = socket(AF_INET,SOCK_STREAM,0);//启动socket
    if(sockfd == -1){
        perror("创建socket失败");
        printf("服务器启动失败\n");
        exit(-1);
    }
    //准备网络通信地址
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = inet_addr(IP);
    if(bind(sockfd,(SA*)&addr,sizeof(addr))==-1){//绑定服务器
        perror("绑定失败");
        printf("服务器启动失败\n");
        exit(-1);
    }
    printf("成功绑定\n");

    //设置监听
    if(listen(sockfd,10)==-1){
        perror("设置监听失败");
        printf("服务器启动失败\n");
        exit(-1);
    }
    printf("设置监听成功\n");
    printf("初始化服务器成功\n");
    //等待客户端链接,放到另一个函数中
}

void* service_thread(void* p)
{

	int fd = *(int*)p;          //拿到标记客户端的sockfd
	int count=0;
    int i,j,n;
    int weight[100]={0};
    char ch[100];
    char ch2[100];
    int cht;
    char demessage[100];
    int choice;
    int flag=0;
    int judge;

	HaffNode *myHaffTree=NULL;
	Code     *myHaffCode=NULL;
    while(1)// 死循环,等待客户端信息
    {
        recv(fd,&choice,sizeof(int),0);//得到客户端选择信息

        if(flag == 0 && choice == 2){//如果其还没建立haff树,则返回错误,提醒其建立haff树
        	judge=1;
        	send(fd,&judge,sizeof(int),0);//返回错误标志
            continue;
        }else{
        	judge=0;
        	send(fd,&judge,sizeof(int),0);
        }

        if(choice == 1)//如果是编码,建立haff树,并编码
        {
            count = 0 ;
        	flag=1;
            recv(fd,ch,sizeof(ch),0);
            n=strlen(ch);//输入编码长度
            sear_weight(ch,weight,ch2);//计算权重

	        if(n>MaxN)
	    	{
		   		printf("n越界!!!\n");
	    		return 0;
    		}

            //建树
	  		myHaffTree=(HaffNode *)malloc(sizeof(HaffNode)*(2*n-1));
	        myHaffCode=(Code *)malloc(sizeof(Code)*n);

	    	HaffTree(weight,n,myHaffTree,ch2);
	    	HaffCode(myHaffTree,n,myHaffCode);

	    	for(i=0; i<n; i++)
	    	{
	        	for(j = myHaffCode[i].start; j<n; j++)
                	count++;
            	count++;
	    	}
	    	send(fd,&count,sizeof(int),0);//返回其编码后的长度  

	    	for(i=0; i<n; i++)//一个字符一个字符发送
	    	{
	        	for(j = myHaffCode[i].start; j<n; j++)
            	{
	    	    	send(fd,&(myHaffCode[i].bit[j]),sizeof(int),0);
            	}
            	cht=2;
            	send(fd,&cht,sizeof(int),0);
	    	}
    	}

    	else if(choice == 2)//如果为译码
    	{
        	recv(fd,demessage,sizeof(demessage),0);//得到要译码的信息

        	haffyima(myHaffTree,n,demessage,weight,message);//进行译码    

        	send(fd,message,sizeof(message),0);//把译码信息返回
   	 	}
   	 	else{
   	 		break;
   	 	}
   	}
    close(fd);//关闭socket
}
//等待客户端的连接,启动服务器的服务

void service()
{
    printf("服务器开始服务\n");
    while(1)//一直循环,等待客户端链接
    {
        struct sockaddr_in fromaddr;
        socklen_t len = sizeof(fromaddr);
        int fd = accept(sockfd,(SA*)&fromaddr,&len);
        if(fd == -1)
        {
            printf("客户端链接出错\n");
            continue;//继续循环,处理连接
        }
        //如果客户端成功连接上
        pthread_t pid;
        pthread_create(&pid,0,service_thread,&fd);
    }
}

void signal_close(int signal_num){
//关闭服务器前 关闭服务器的socket
    close(sockfd);
    printf("服务器已经关闭\n");
    exit(1);
}

int main()
{
    signal(SIGINT,signal_close);//退出CTRL+C
    init();//初始化服务器
    service();//服务器开始运行
    return 0;
}

时间: 2024-10-13 13:43:48

自写项目——哈夫曼编译码及客户端服务端交互实现的相关文章

基于python的二元霍夫曼编码译码详细设计

一.设计题目 对一幅BMP格式的灰度图像(个人证件照片)进行二元霍夫曼编码和译码 二.算法设计 (1)二元霍夫曼编码: ①:图像灰度处理: 利用python的PIL自带的灰度图像转换函数,首先将彩色图片转为灰度的bmp图像,此时每个像素点可以用单个像素点来表示. ②:二元霍夫曼编码: 程序流程图: 详细设计: 统计像素点频率,首先通过python自带的PIL库的图像像素点读取函数read()获取灰度图像的所有像素点,通过循环遍历每个像素点,将每个出现的像素点值以及其次数以键值对的形式放入到pyt

哈夫曼编码译码系统(c/c++)

哈夫曼编码译码系统的实现,主要包含三部分: 1.创建哈夫曼树 2.编码函数 3.译码函数 编写代码时为了方便,在这里混用了c++的输入输出流.主体用c语言实现. 下面时代码部分: 1.头文件,以及储存结构: #include<stdio.h> #include<iostream> using namespace std; #define MAX 2000 typedef char ElemType; typedef struct{ ElemType data; int w; int

大一写的哈夫曼编码程序(今天贴上)

/* 霍夫曼编码 哈夫曼树(Huffman Tree),又叫最优二叉树,指的是对于一组具有确定权值的叶子结点的具有最小带权路径长度的二叉树 1.数据移动时无符号不同于有符号,右移要默认为0 2.文件操作以字节为最小单位.凑足写入,最后多一个字节写上次剩余多少 3.区分叶子节点与内部节点,8个字符的最长路径为7 无符号类型的应用,位操作在有符号时候会带符号操作,当系统使用文本方式打开文件后,会对回车做相应处理.文件读取不正确 移位操作时候注意左右不同.当最后一个单元不能完全保存时,最后用一个来存储

数据结构课程设计-哈夫曼编码译码

//******************************************** //程序功能:哈夫曼编码及译码 // //日期:2014年11月18 // //******************************************** #include<stdio.h> #include<stdlib.h> #include<string.h> #include <windows.h> #define MAX 128 //叶子节点

【Matlab编程】哈夫曼编码的Matlab实现

在前年暑假的时候,用C实现了哈夫曼编译码的功能,见文章<哈夫曼树及编译码>.不过在通信仿真中,经常要使用到Matlab编程,所以为了方便起见,这里用Matlab实现的哈夫曼编码的功能.至于哈夫曼编译码的基本原理,我们可以参考之前的文章<哈夫曼树及编译码>,里面有详细的说明及图解过程.下面直接给出具体的Matlab实现的哈夫曼编码函数,由于程序中注释还算比较详细,在此就不予与说明: function [ h,e ] = Huffman_code( p ) %p为概率分布,此函数功能是

哈夫曼编译系统

1 /* 2 哈夫曼编码C语言实现 3 */ 4 // n个数字,对应n个权值 5 // n作为主输入参数,确定需要编码的数量 6 // 输入n个权值,进行加权求解 7 // 求解最小生成树 8 // 9 // 涉及数据成员: 10 // weight 权值 11 // parent 父节点 12 // 13 #include<stdio.h> 14 #include<stdlib.h> 15 #include<string> 16 #define TREELEN 10

C++哈夫曼树编码和译码的实现

一.背景介绍: 给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近. 二.实现步骤: 1.构造一棵哈夫曼树 2.根据创建好的哈夫曼树创建一张哈夫曼编码表 3.输入一串哈夫曼序列,输出原始字符 三.设计思想: 1.首先要构造一棵哈夫曼树,哈夫曼树的结点结构包括权值,双亲,左右孩子:假如由n个字符来构造一棵哈夫曼树,则共有结点2n-1个:在构造前,先初始化

哈夫曼编码(最优前缀码)

作为哈夫曼树的一个重要应用,我们来介绍哈夫曼编码.在我的上一篇博文<树之哈夫曼树>中已经介绍了建立哈夫曼树的过程,而由哈夫曼树求得的编码为最优前缀码.每个叶子表示的字符的编码,就是从根到叶子的路径上的标号依次相连所形成的编码,显然这就是该字符的最优前缀码.所谓前缀码是指,对字符集进行编码时,要求字符集中任一字符的编码都不是其它字符的编码的前缀,比如常见的等长编码就是前缀码.所谓最优前缀码是指,平均码长或文件总长最小的前缀编码称为最优的前缀码(这里的平均码长相当于码长的期望值). 我们知道,变长

赫夫曼编译码器实验报告

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #define MAX 100 #define MAXVALUE 10000 typedef struct{ char ch; int weight,flag; int parent,lchild,rchild; }HTNode; typedef struct{ char ch; int bit[MAX]; int