日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

IPMsg飞鸽传书网络协议解析手记

發布時間:2024/1/1 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IPMsg飞鸽传书网络协议解析手记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

相信很多人都使用過飛鴿傳書,這個小工具在局域網傳輸數據高效而便捷,自己在大二的時候就想看看飛鴿傳書的源碼,但那時候自己的水平有限,這幾天有機會重寫飛鴿傳書,也對IPMSG的網絡協議做了深入的研究,這里也要感謝IPMSG的作者公開源代碼。

?? 首先需要明確IPMSG的主要功能,IPMSG可以局域網通信、傳輸文件、傳輸文件夾,可以通過添加局域網外IP來實現網外的聊天與文件傳輸功能。我們先分析下IPMSG的聊天功能,IPMSG通過UDP協議實現聊天,當一個IPMSG的客戶端運行開始,首先它向整個局域網廣播上線報文,局域網內的其他IPMSG客戶端收到上線報文后,回復該報文,回復報文中包含了該客戶端的IP PORT 用戶名 機器名。這樣在上線客戶端通過廣播發送上線報文后,局域網內的其他所有IPMSG客戶端都發送一個回復報文,這樣,所有IPMSG的客戶端都更新自己的在線用戶列表。這樣IPMSG的上線就算結束了,接下來,如果有客戶端發送消息,而消息是通過UDP來完成的,客戶端通過查詢自己用戶鏈表獲取其他用戶的網絡地址信息,發送消息給其他用戶。總結一下:

ipmsg可以用于收發消息和文件(夾)

使用UDP協議收發消息使用TCP協議收發文件(夾)

默認使用2425端口做數據傳輸(TCP/UDP)

包含以下功能

用戶上下線識別

消息收發

文件傳輸文件夾傳輸

IPMSG的報文格式:版本號:包編號:發送者姓名:發送者主機名:命令字:附加信息

整個報文通過字符串的形式發送,IPMSG的版本號為1,而包編號必須是不重復的數字,這里可以是用比較簡潔的方式,就是通過linux的庫函數timer來完成,time 函數返回從1970 年1 月1 日0 點以來的秒數.所以每個運行timer()的結果都是不一樣的,可以放心使用。報文中的命令字是指明這個報文是消息、上線通告、傳輸文件、傳輸文件夾還是其他的東西,附加信息在不同的命令字下是不一樣的,如果命令字是消息,那么附加信息就是消息內容,如果命令字是傳輸文件,那么附加信息就是文件的信息了,我們來看一下命令字,這是IPMSG最為重要的內容。

/*????@(#)Copyright (C) H.Shirouzu 1996-1998 ipmsg.h????Ver1.34 */

#ifndef IPMSG_H
#define IPMSG_H

/* IP Messenger Communication Protocol version 1.0 define */
/* macro */
#define GET_MODE(command)????(command & 0x000000ffUL)
#define GET_OPT(command)????(command & 0xffffff00UL)

/* header */
#define IPMSG_VERSION????????????0x0001
#define IPMSG_DEFAULT_PORT????????0x0979

/* command */
#define IPMSG_NOOPERATION????????0x00000000UL

#define IPMSG_BR_ENTRY????????????0x00000001UL
#define IPMSG_BR_EXIT????????????0x00000002UL
#define IPMSG_ANSENTRY????????????0x00000003UL
#define IPMSG_BR_ABSENCE????????0x00000004UL

#define IPMSG_BR_ISGETLIST????????0x00000010UL
#define IPMSG_OKGETLIST????????????0x00000011UL
#define IPMSG_GETLIST????????????0x00000012UL
#define IPMSG_ANSLIST????????????0x00000013UL
#define IPMSG_FILE_MTIME????????0x00000014UL
#define IPMSG_FILE_CREATETIME????????0x00000016UL
#define IPMSG_BR_ISGETLIST2????????0x00000018UL

#define IPMSG_SENDMSG????????????0x00000020UL
#define IPMSG_RECVMSG????????????0x00000021UL
#define IPMSG_READMSG????????????0x00000030UL
#define IPMSG_DELMSG????????????0x00000031UL

/* option for all command */
#define IPMSG_ABSENCEOPT????????0x00000100UL
#define IPMSG_SERVEROPT????????????0x00000200UL
#define IPMSG_DIALUPOPT????????????0x00010000UL
#define IPMSG_FILEATTACHOPT????????0x00200000UL

/* file types for fileattach command */
#define IPMSG_FILE_REGULAR????????0x00000001UL
#define IPMSG_FILE_DIR????????????0x00000002UL
#define IPMSG_LISTGET_TIMER????????????0x0104
#define IPMSG_LISTGETRETRY_TIMER????0x0105

#define HS_TOOLS????????"HSTools"
#define IP_MSG????????????"IPMsg"
#define NO_NAME????????????"no_name"
#define URL_STR????????????"://"
#define MAILTO_STR????????"mailto:"
#endif ????????/* IPMSG_H */

? 報文中的命令字是一個32位無符號整數,包含命令(最低字節)和選項(高三字節)兩部分
常用基本命令(帶有BR標識的為廣播命令),下邊是一些重要的命令字。
IPMSG_NOOPERATION?不進行任何操作
IPMSG_BR_ENTRY?用戶上線
IPMSG_BR_EXIT?用戶退出
IPMSG_ANSENTRY?通報在線
IPMSG_SENDMSG?發送消息
IPMSG_RECVMSG?通報收到消息
IPMSG_GETFILEDATA?請求通過TCP傳輸文件
IPMSG_RELEASEFILES?停止接收文件
IPMSG_GETDIRFILES?請求傳輸文件夾
在IPMSG上線時,首先發送的是IPMSG_NOOPERATION,默認是不做任何處理,然后上線通告報文IPMSG_BR_ENTRY?。
?
用戶列表通過鏈表來實現,看看結構體:

typedef struct use_date
{??
??char use_name[USE_NAME_LEN];?? //用戶名
??char host_name[HOST_NAME_LEN];? //機器名
??int id;???????????????//節點ID。
??long int host_ip;???? //存儲IP信息,避免重復添加
??struct sockaddr_in inet;? //存儲網絡信息
??struct use_data *next;
}IPMSG_USE;

每次IPMSG在收到上線通告報文后,都要查找相同ip的節點是否已經存在,只要和結構體成員host_ip比較就可以了,這樣整個用戶列表當中的成員是不會重復的。報文的發送主要依靠下邊的函數實現,這里推薦下邊的這種寫法,特別是對與命令比較多的情況下,使用下邊的好處就在與結構非常的清晰。

?

?

mode: 命令? msg: 附加信息 struct sockaddr *p:網絡信息? fd:網絡套接字描述符?

int msg_send(const int mode,const char *msg,const struct sockaddr *p,int fd)
{
????int udp_fd=fd;
????int broadcast_en=1;
????char msg_buf[SND_BUF_LEN];
????char *use="test",*group="sunplusapp";
????socklen_t broadcast_len=sizeof(broadcast_en);
????long int msg_id=time((time_t *)NULL);
????struct sockaddr_in udp_addr;
????struct sockaddr client;
????bzero(msg_buf,SND_BUF_LEN);
????bzero(&udp_addr,sizeof(struct sockaddr_in));
????udp_addr.sin_family=AF_INET;
????udp_addr.sin_port=htons(IPMSG_UDP_PORT);
????inet_pton(AF_INET,BR_IP,&udp_addr.sin_addr.s_addr);

//下邊的if 與else if :對于上線通告 下線等使用廣播地址,其他的則否
????if( (p==NULL)&&(mode!=IPMSG_NOOPERATION)&&(mode!=IPMSG_BR_ENTRY)&&(mode!=IPMSG_BR_EXIT))
????{
????????printf("p is NULL,only mode = IPMSG_NOOPERATICNA IPMSG_BR_ENTRY IPMSG_EXIT is allowed p=NULL /n");
????????return 1;
????}
????else if( (p!=NULL)&&(mode!=IPMSG_NOOPERATION)&&(mode!=IPMSG_BR_ENTRY)&&(mode!=IPMSG_BR_EXIT))
????????client=*p;

//打開廣播
????if( setsockopt(udp_fd,SOL_SOCKET,SO_BROADCAST,&broadcast_en,broadcast_len)<0 )
????{
????????perror("setsockopt error");
????????exit(1);
????}
????switch (mode)
????{
????????case IPMSG_NOOPERATION:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,NULL);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,(struct sockaddr *)&udp_addr,sizeof(struct sockaddr));
????????break;
????????case IPMSG_BR_ENTRY:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,(struct sockaddr *)&udp_addr,sizeof(struct sockaddr));
????????break;
????????case IPMSG_BR_EXIT:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,(struct sockaddr *)&udp_addr,sizeof(struct sockaddr));
????????break;
????????case IPMSG_ANSENTRY:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????case IPMSG_SENDMSG:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????case IPMSG_SENDMSG_OPT:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????case IPMSG_RECVMSG:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????case IPMSG_GETFILEDATA:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????case IPMSG_RELEASEFILES:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????case IPMSG_GETDIRFILES:
????????sprintf(msg_buf,"1:%d:%s:%s:%d:%s",msg_id,use,group,mode,msg);
????????sendto(udp_fd,msg_buf,strlen(msg_buf),0,&client,sizeof(struct sockaddr));
????????break;
????????default:
????????printf("no match mode !/n");
????????break;
????}
????broadcast_en=0;

//? 關掉廣播
????if( setsockopt(udp_fd,SOL_SOCKET,SO_BROADCAST,&broadcast_en,broadcast_len)<0 )
????{
????????perror("setsockopt error");
????????exit(1);
????}
????printf("msg send ok ! /n");
????return 0;
}????

通過上邊的報文就可以實現消息的傳遞,可以發起文件、文件夾的傳輸,傳輸文件時,首先需要通過UDP報文聯絡,在UDP報文聯絡好之后,隨即發起TCP文件傳輸,文件傳輸是不帶格式的。IPMSG的一個難點就是文件夾的傳輸。今天就寫這里,而且也做到這里。

總結

以上是生活随笔為你收集整理的IPMsg飞鸽传书网络协议解析手记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。