Linux下网络编程(3)文件发送
【摘要】 这篇文章介绍Linux下线程的创建与使用,完成线程的练习,通过线程完成文件发送代码优化,完成目录下所有文件自动发送,利用文件发送理解socket编程。
这篇文章介绍Linux下线程的创建与使用,完成线程的练习,通过线程完成文件发送代码优化,完成目录下所有文件自动发送,利用文件发送理解socket编程。
扩展练习:
1. 实现目录传输(只写一级目录)。 (opendir)
(1) 封装一个函数(发送一个文件)
(2) 发送数据需要封装结构体(分包发送)
2. 实现目录内文件的同步 (公交车站台、地铁里广告机(视频播放机))
(1) 使用一个链表存放目录下所有文件信息的状态
(2) 每隔5秒钟,就遍历当前目录每个文件的信息,与链表里做比较。
任务1:线程的创建示例
(1)创建线程运行并等待退出
#include <stdio.h>
#include <pthread.h>
//线程函数1
void *pthread_func1(void *arg)
{
while(1)
{
printf("线程函数1正在运行.....\n");
sleep(2);
}
}
//线程函数2
void *pthread_func2(void *arg)
{
while(1)
{
printf("线程函数2正在运行.....\n");
sleep(2);
}
}
int main(int argc,char **argv)
{
pthread_t thread_id1;
pthread_t thread_id2;
/*1. 创建线程1*/
if(pthread_create(&thread_id1,NULL,pthread_func1,NULL))
{
printf("线程1创建失败!\n");
return -1;
}
/*2. 创建线程2*/
if(pthread_create(&thread_id2,NULL,pthread_func2,NULL))
{
printf("线程2创建失败!\n");
return -1;
}
/*3. 等待线程结束,释放线程的资源*/
pthread_join(thread_id1,NULL);
pthread_join(thread_id2,NULL);
return 0;
}
//gcc pthread_demo_code.c -lpthread
(2)创建线程并设置分离属性
#include <stdio.h>
#include <pthread.h>
//线程函数1
void *pthread_func1(void *arg)
{
while(1)
{
printf("线程函数1正在运行.....\n");
sleep(2);
}
}
//线程函数2
void *pthread_func2(void *arg)
{
while(1)
{
printf("线程函数2正在运行.....\n");
sleep(2);
}
}
int main(int argc,char **argv)
{
pthread_t thread_id1;
pthread_t thread_id2;
/*1. 创建线程1*/
if(pthread_create(&thread_id1,NULL,pthread_func1,NULL))
{
printf("线程1创建失败!\n");
return -1;
}
/*2. 创建线程2*/
if(pthread_create(&thread_id2,NULL,pthread_func2,NULL))
{
printf("线程2创建失败!\n");
return -1;
}
/*3. 设置分离属性,让线程结束之后自己释放资源*/
pthread_detach(thread_id1);
pthread_detach(thread_id2);
while(1)
{
printf("主函数正在运行.....\n");
sleep(2);
}
return 0;
}
(3)创建多个线程同时运行
#include <stdio.h>
#include <pthread.h>
//线程函数
void *pthread_func(void *arg)
{
int id=*(int*)arg;
int cnt=0;
while(1)
{
printf("线程函数%d运行次数:%d\n",id,cnt++);
sleep(2);
}
}
int main(int argc,char **argv)
{
pthread_t thread_id;
int i;
for(i=0;i<5;i++)
{
/*1. 创建线程*/
if(pthread_create(&thread_id,NULL,pthread_func,(void*)&i))
{
printf("线程%d创建失败!\n",i);
return -1;
}
/*2. 设置分离属性,让线程结束之后自己释放资源*/
pthread_detach(thread_id);
}
while(1)
{
}
return 0;
}
任务2: TCP服务器多线程方式处理客户端数据
可重入函数? 都是私有局部数据。
不可重入函数? 全局变量 静态变量……
基础练习:
1. TCP服务器多线程方式实现数据转发。 类似与QQ群的效果。
2. 多线程方式实现文件发送。类似于飞秋。群发文件。
(1)文件发送服务器端代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
/***************************结构体定义区域*****************************/
#define PROT 8888
#pragma pack(1) //以下结构体以一个字节对齐
struct filedirinfo
{
char name[20];
int file_size;
int count;
};
/**********************************************************************/
/***************************函数声明区域*******************************/
int wait_client_ack(int client_fd);
int send_file(int client_fd,char *absfilename,char *filename);
/**********************************************************************/
/*服务器端创建步骤:
1. 创建网络套接字
2. 绑定端口
3. 设置监听数量
4. 阻塞等待客户端连接
*/
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("./app dirname\n");
exit(-1);
}
struct sockaddr_in server_addr; //存放服务器的IP地址信息
struct sockaddr_in client_addr; //存放客户端的IP地址信息
int socket_fd; //存放服务器网络套接字
int client_fd; //存放客户端网络套接字
socklen_t addrlen; //存放接收的客户端地址长度
/*1. 创建网络套接字 PF_INET:ipv4 、SOCK_STREAM:TCP*/
socket_fd=socket(PF_INET,SOCK_STREAM,0);
if(socket_fd<0)
{
printf("服务器套接字创建失败!\n");
exit(-1);
}
/*2. 绑定端口*/
memset(&server_addr,0,sizeof(struct sockaddr_in)); //初始化结构体内存空间
server_addr.sin_family=PF_INET; //ipv4
server_addr.sin_port=htons(PROT); //大端转小端
server_addr.sin_addr.s_addr=INADDR_ANY; //IP地址赋值
if(bind(socket_fd,(const struct sockaddr *)&server_addr,sizeof(struct sockaddr)))
{
printf("服务器端口绑定失败!\n");
exit(-1);
}
/*3. 设置监听客户端的数量*/
if(listen(socket_fd,10))
{
printf("监听客户端失败!\n");
exit(-1);
}
addrlen=sizeof(struct sockaddr);
/*4. 阻塞等待客户端连接,accept返回客户端的网络套接字*/
client_fd=accept(socket_fd,(struct sockaddr *)&client_addr,&addrlen);
if(client_fd<0)
{
printf("服务器阻塞出现错误!\n");
exit(-1);
}
/*1.打开目录 */
struct dirent *dir_file;
char *abs_addr,*p; //存放绝对路径
int str_len=0;
DIR *dir=opendir(argv[1]);
struct stat FileStatInfo;
if(dir==NULL)
{
printf("目录打开失败!\n");
exit(-1);
}
/*循环读取目录*/
while(dir_file=readdir(dir))
{
// dir_file->d_name + address_dir;
str_len=strlen(argv[1]); //路径的长度
str_len+=strlen(dir_file->d_name); //文件名称的长度
str_len+=1;
/*申请空间*/
p=abs_addr=malloc(str_len);
/*拷贝路径*/
strcpy(abs_addr,argv[1]);
if(strcmp(dir_file->d_name,".")!=0&&strcmp(dir_file->d_name,"..")!=0)
{
/*拼接文件名称*/
strcat(abs_addr,dir_file->d_name);
if(stat(abs_addr,&FileStatInfo)!=0)
{
printf("文件信息获取错误!\n");
exit(-1);
}
if(S_ISREG(FileStatInfo.st_mode)) //判断是否是普通文件
{
send_file(client_fd,abs_addr,dir_file->d_name); //发送文件
printf("正在发送:%s 文件!\n",abs_addr);
}
}
free(p); //释放空间
}
/*关闭目录*/
closedir(dir);
return 0;
}
//发送文件
int send_file(int client_fd,char *absfilename,char *filename)
{
/*编写发送图片的代码*/
char buff[1024]; //存放数据缓冲区
int ack_info; //存放应答数据
int count; //保存读出的字节数
int fd; //文件描述符
struct filedirinfo fileinfo;//存放TCP头结构
struct stat t_stat; //获取文件所有信息的结构体
//1. 打开图片文件
FILE *file=fopen(absfilename,"rb");
if(file==NULL)
{
printf("打开文件失败!\n");
exit(-1);
}
//把文件指针转化为文件描述符
fd=fileno(file);
/* 获取文件大小 */
fstat(fd, &t_stat); //把打开的文件的状态复制到t_stat结构体中
printf("文件的大小: %d 字节\n",t_stat.st_size);
if(filename==NULL)
{
printf("%s文件第%d行出现错误。\n",__FILE__ ,__LINE__);
return -1;
}
strncpy(fileinfo.name,filename,20);
fileinfo.file_size=t_stat.st_size; //文件字节大小
/*向客户端发送头结构*/
write(client_fd,&fileinfo,sizeof(struct filedirinfo));
wait_client_ack(client_fd);//等待应答
while(1)
{
//2. 读出图片内容
count=fread(buff,1,1024,file);
//3. 向客户端发送数据
if(write(client_fd,buff,count)!=count)
{
printf("%s文件第%d行出现错误。\n",__FILE__ ,__LINE__);
return -1; //发送错误
}
printf("server tx:%d\n",count);
wait_client_ack(client_fd);//等待应答
//5. 判断文件是否到结尾
if(feof(file))
{
//5. 关闭文件,结束发送
fclose(file);
break;
}
}
return 0;
}
//等待客户端应答
int wait_client_ack(int client_fd)
{
int count;
int ack_info;
while(1)
{
//接收客户端的应答
count=read(client_fd,&ack_info,4);
//判断应答
if(count>0&&ack_info==8888)
{
return 0;
}else
{
//处理错误信息。
}
}
}
(2)文件发送客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
/***************************结构体定义区域*****************************/
#define PROT 8888
#pragma pack(1) //以下结构体以一个字节对齐
struct filedirinfo
{
char name[20];
int file_size;
int count;
};
/**********************************************************************/
/***************************函数声明区域*******************************/
int send_ack(int client_fd);
int receive_file(int client_fd);
/**********************************************************************/
/*客户端创建步骤:
1. 创建网络套接字
2. 连接服务器
*/
//./app 192.168.1.1
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("参数传递方式:./app 192.168.1.1\n");
exit(-1);
}
int client_fd; //存放客户端网络套接字
struct sockaddr_in server_addr; //存放服务器的IP地址信息
/*1. 创建网络套接字 PF_INET:ipv4 、SOCK_STREAM:TCP*/
client_fd=socket(PF_INET,SOCK_STREAM,0);
if(client_fd<0)
{
printf("客户端套接字创建失败!\n");
exit(-1);
}
/*2. 连接服务器*/
memset(&server_addr,0,sizeof(struct sockaddr_in)); //初始化结构体内存空间
server_addr.sin_family=PF_INET; //ipv4
server_addr.sin_port=htons(PROT); //大端转小端
server_addr.sin_addr.s_addr=inet_addr(argv[1]); //IP地址赋值
if(connect(client_fd,(const struct sockaddr *)&server_addr,sizeof(struct sockaddr)))
{
printf("连接服务器失败!\n");
exit(-1);
}
while(1)receive_file(client_fd); //接收文件
close(client_fd); //关闭套接字
return 0;
}
//向服务器发送应答信号
int send_ack(int client_fd)
{
int ack_info=8888;
//4.给服务器发送应答数据
if(write(client_fd,&ack_info,4)<0)
{
printf("%s文件第%d行出现错误。\n",__FILE__ ,__LINE__);
return -1;
}
return 0;
}
//接收文件
int receive_file(int client_fd)
{
/*编写接收BMP图片代码*/
char buff[1024]; //存放数据缓冲区
int count; //保存读出的字节数
/*接收TCP头指针*/
struct filedirinfo info;
memset(&info,0,sizeof(struct filedirinfo));
while(1)
{
//读出服务器发送过来的数据
count=read(client_fd,(char*)&info,sizeof(struct filedirinfo));
//判断是否收到数据
if(count==sizeof(struct filedirinfo))
{
if(send_ack(client_fd)!=0)return -1; //给服务器发送应答数据
break;
}
}
if(info.name==NULL||info.file_size<=0)
{
printf("%s文件第%d行出现错误。\n",__FILE__ ,__LINE__);
return -1;
}
//1. 创建图片文件
FILE *file=fopen(info.name,"wb");
if(file==NULL)
{
printf("%s 文件创建失败!\n",info.name);
exit(-1);
}
while(1)
{
//读出服务器发送过来的数据
count=read(client_fd,buff,1024);
if(count>0)
{
printf("rx count:%d\n",count);
//向文件中写数据
fwrite(buff,count,1,file);
//给服务器发送应答数据
if(send_ack(client_fd)!=0)return -1; //给服务器发送应答数据
if(count!=1024)
{
//5. 接收完毕关闭文件
fclose(file);
break;
}
}
}
printf("文件总大小:%d\n",info.file_size);
return 0;
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)