本文主要为”飞鸽“的文件发送部分
文章目录
前言
ipmsg全称:IP Messenger,中文名为“飞鸽传书”,是一款用C语言写的局域网聊天和文件传输工具。它是一个小巧方便的即时通信软件,它适合用于局域网内甚至广域网间进行实时通信和文档共享。特别是在局域网内传送文件/文件夹的效率很高。
它具有很多优点,如数据通讯不需要建立服务器、直接在两台电脑间通信和数据传输,支持文件及文件目录的传输,安全快捷以及小巧方便等优异特点,因此很多公司都采用它作为部门、公司内部的IM即时通信工具。
实训项目为在linux下用C语言写一个可以实现“飞鸽”功能的程序
一、实现过程及对比
1.1 实现过程
发送文件:UDP讲请求发送文件的关键字发到指定ip的用户上,对方选择同意接收,主动发起TCP连接请求,进行连接。
数据打包:读取发送文件的信息,存到特定字符串里进行发送。
设置发送端缓冲区和接收端缓冲区,发送文件时,把待发送的文件放入缓冲区。接收到的字符串放入接收端缓冲区,读取后建立一同名文件,将字符写入文件。
同时将文件信息存入信息表,可单独输出。
1.2 与例程对比
给出例程文件是由链表连接存储,实现方式较为复杂,代码量较大,因此舍去。
二、程序解析
2.1 发送文件
假设输入有三种情况
情况一:输入 发送文件 选项,输入收件人,输入文件路径和文件名
情况二:输入 发送文件和收件人,输入文件路径和文件名
情况三:输入 发送文件、收件人、文件路径和文件名
2.1.1 输入情况一
//选择收件人
user_list();
printf("please select a user:");
scanf("%d",&uid);
getchar();
2.1.2 输入情况二
//选择文件
printf("input filename to %s[%s]:",usr->usr_name, usr->usr_ip);
fflush(stdout);
fgets(filename,sizeof(filename), stdin);
filename[strlen(filename)-1]='\0';
2.1.3 输入情况三
//读取文件名
strcpy(filename,argv[2]);
2.1.4 stat函数
int stat(const char *path, struct stat *buf);
stat函数自带结构体
struct stat {
dev_t st_dev; /* 文件的设备编号 */
ino_t st_ino; /* 索引结点编号 */
mode_t st_mode; /* 文件类型和权限*/
nlink_t st_nlink; /*硬链接数 */
uid_t st_uid; /*用户ID*/
gid_t st_gid; /* 组ID*/
dev_t st_rdev; /* 设备类型(若此文件为设备文件,则为设备编号*/
off_t st_size; /* 文件大小*/
blksize_t st_blksize; /*文件系统的I/O缓冲区大小*/
blkcnt_t st_blocks; /* 块数 */
time_t st_atime; /* 访问时间 */
time_t st_mtime; /* 修改时间 */
time_t st_ctime; /* 更改时间 */
};
2.1.5 数据报打包
sprintf(buf, "1:%d:%s:%s:%d:%c",t,user(),host(),IPMSG_SENDMSG|IPMSG_SENDCHECKOPT|IPMSG_FILEATTACHOPT,0);
sprintf(buf+strlen(buf)+1,"0:%s:%x:%x",filename,fstat.st_size,fstat.st_ctime); //打包文件信息
sendto(sockfd, buf, strlen(buf)+strlen(buf+strlen(buf)+1), 0, (struct sockaddr*)&addr, sizeof(addr));
2.2 接收文件
2.2.1 TCP连接部分
//TCP connection
if((sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)
{
perror("recv file socket");
exit(1);
}
if(connect(sockfd, (struct sockaddr*)&(file.addr), sizeof(struct sockaddr))!=0)
{
perror("connect");
exit(1);
}
2.2.2 收写文件
if((fd=open(file.name,O_RDWR|O_CREAT,0666))<0) //以只读或者追写的方式打开一个权限为666的同名文件
{
perror("open");
exit(1);
}
while(filesize!=0) //filesize为接收文件的大小
{
int len = recv(sockfd, buf,sizeof(buf),0); //len为成功接收数据大小
write(fd, buf, len); //把buf中len长度的数据写入文件描述符fd所指的文档
filesize -= len;
}
2.3 添加文件
temp[i++] = strtok(fileopt,":"); //按照:分割
while((temp[i++] = strtok(NULL,":"))!=NULL);
sscanf(temp[2], "%x", &(file.size)); //从temp[2]中读出16进制数放到字符串里
sprintf(hex, "%x", atoi(packno));
strcpy(file.packno, hex); //包编号
strcpy(file.fino, temp[0]); //传进来的信息
strcpy(file.name, temp[1]);
file.addr = addr;
2.4 文件传输关键字
if(atoi(temp[4])&IPMSG_FILEATTACHOPT) //传送文件选项
{
printf("\rrecv file!\n");
printf("MY_IPMSG>>");
fflush(stdout);
add_file(temp[1], fileopt, addr);
}
2.5 发送文件线程
if((cfd=accept(tcp_fd, (struct sockaddr*)&addr, &addrlen))<0)
{
perror("accpet");
exit(1);
}
recv(cfd, buf, sizeof(buf),0);
if((fd=open(sfile.name, O_RDONLY))<0)
{
perror("open sendfile");
exit(1);
}
while(sfile.size!=0)
{
int len = read(fd, buf, sizeof(buf));
send(cfd, buf, len,0);
sfile.size -= len;
}
2.6 当键入”ls“时展现当前文件目录下文件
if(strcmp(temp[0], "ls")==0)
{
pid_t pid=0;
if((pid=fork())==0)
{
execlp("ls","ls",NULL);
}
waitpid(pid,NULL,WUNTRACED);
}
总结
UI界面较为简陋,结果实现也有一定问题,期待后续改进。