客户端与服务器间的文件互传

基于socket的文件数据互传,将文件中所有的字符一一通过buf进行传递,为了更好地判断文件的 结束,通过添加文件结束标识符“#”,当接收端接收到该字符,即可表示发送端对该文件传输结束,结束端需要对文件进行保存,并继续创建新的文件进行新文件内容的写入


目录

  • 客户端与服务器间的文件互传
    • 代码
    • strcat()、strcmp()、strcpy()函数的用法
    • fgets()、scanf()、gets()函数的用法 及区别
    • 文件流的读出与写入(fwrite、fread)方法
    • socket通信传输下,recv获取后的buf的形式

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>  //IP转换函数
#include <ctype.h>  //toupper函数头文件
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>
#include <netinet/in.h>
#include <pthread.h>  //线程头文件
#include <unistd.h>
#include <fcntl.h>
#include <stdbool.h>#include "wrap.h"  // #define MAXLINE 20   //client.c与server.c中的数据传输的buf的size
#define INET_ADDRSTRLEN 16
#define SERV_PORT 6666
//#define SERV1_PORT 8888
char str1[]="cfile/";
const char str2[]="sfile/";struct s_info{  //定义一个结构体,将客户端的地址与connfd进行捆绑struct sockaddr_in cliaddr;int connfd;// char *filename;
};struct s_info_client{  //定义一个结构体,将客户端的地址与connfd进行捆绑struct sockaddr_in servaddr;int sockfd;
};struct argv_com{ //定义一个结构体来存放输入的参数char *argv1;char *argv2;// char *filename;
};//文件创建函数
int *creatfile(char *filename)
{FILE *fd=fopen(filename,"w+");if(fd!=NULL){printf("%s is created.\n",filename);return (int *)fd ;}else{printf("%s create failed.\n",filename);return NULL;}
}//服务器端写数据——广播
void *server_send(void* server_send)
{struct s_info *ts_back=(struct s_info*)server_send;char buf[MAXLINE];char bufname[MAXLINE];char str[INET_ADDRSTRLEN];int send_fd;send_fd = Socket(AF_INET,SOCK_DGRAM,0);int on=1;setsockopt(send_fd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));//设置套接字允许发送广播数据struct sockaddr_in send_addr;send_addr.sin_family=AF_INET;send_addr.sin_port=htons(SERV_PORT);//广播端口// send_addr.sin_addr.s_addr=inet_addr("ts->back->cliaddr");//广播IP地址send_addr.sin_addr.s_addr= htonl(INADDR_BROADCAST);//广播IP地址,代表255.255.255.255的广播地址,广播消息不会在当前路由器进行转发,作用范围只能在当前局域网。while(1){ memset(bufname,0,sizeof(buf));//fgets(bufname,sizeof(bufname),stdin); //fgets与scanf函数的区别scanf("%s",bufname);printf("bufname:%s\n ",bufname);FILE *serfile_fd=fopen(bufname,"r"); //只允许读数据if(serfile_fd==NULL){printf("server file not found.\n");}else{sendto(send_fd,bufname,MAXLINE,0,(struct sockaddr *)&send_addr,sizeof(send_addr));//获取文件名就广播  int length=0;memset(buf,0,sizeof(buf));//读数据,发给服务器while((length=fread(buf,sizeof(char),sizeof(buf),serfile_fd))>0) //读数据{if(sendto(send_fd,buf,MAXLINE,0,(struct sockaddr *)&send_addr,sizeof(send_addr))<0) //发数据{printf("send file:%s faile\n.",bufname);break;}memset(buf,0,sizeof(buf));}fclose(serfile_fd);//文件结束标志'#'char charflags='#';sendto(send_fd,&charflags,sizeof(char),0,(struct sockaddr *)&send_addr,sizeof(send_addr));printf("%s transfer successed.\n",bufname);} }Close(ts_back->connfd);Close(send_fd);
}//服务器端读数据
void *server_recv(void* server_recv)
{struct s_info *ts=(struct s_info*)server_recv;char buf[MAXLINE];char bufname[MAXLINE];char str[INET_ADDRSTRLEN];int m,n;char charflags='#';bool flags=false;  //文件传输结束标志while(1){//获取需要创建文件的文件名memset(bufname,0,sizeof(bufname));m=recv(ts->connfd,bufname,sizeof(buf),0);if(m==0){//printf("The other side is closed.\n");break;}printf("bufname: %s\n",bufname);FILE *serfile_fd=(FILE *)creatfile(bufname); //获取创建后的文件流描述serfile_fdwhile((n=recv(ts->connfd,buf,sizeof(buf),0))>0){printf("n: %d\n",n);printf("strlrn(buf): %ld\n",strlen(buf));printf("%s",buf);for(int i=0;i<strlen(buf);i++){printf("%c\n",buf[i]);//判断文件是否结束if(charflags==buf[i]){fclose(serfile_fd);printf("%s get successed.\n",bufname);flags=true;break;}//fwrite(buf,sizeof(char),strlen(buf),serfile_fd); //将字符串写入文件流fputc(buf[i],serfile_fd); //将字符一一写入文件流}//文件传输结束if(flags==true){break;}memset(buf,0,sizeof(buf));fflush(serfile_fd);}flags=false; //文件传输标志初始化,进入下一文件读取} Close(ts->connfd);
}//服务器端线程处理函数
void *do_server()
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;char buf[MAXLINE];char str[MAXLINE];int i, n;pthread_t tid_server_recv,tid_server_send;  //定义pthread_t型的变量struct s_info ts[100]; //创建结构体数组,设置线程上限i=0;listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));Listen(listenfd, 20);printf("Accepting connections ...\n");cliaddr_len=sizeof(cliaddr);while(1){connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);ts[i].cliaddr=cliaddr;ts[i].connfd=connfd;// ts[i].filename=argv;pthread_create(&tid_server_recv,NULL,server_recv,(void*)&ts[i]);pthread_detach(tid_server_recv);     pthread_create(&tid_server_send,NULL,server_send,(void*)&ts[i]);pthread_detach(tid_server_send);i++;  //起下一线程}
}//客户端接收数据——广播
void *do_work_client(void *client_recv)
{int m,n;char buf[MAXLINE];char str[INET_ADDRSTRLEN];char bufname[MAXLINE];char charflags='#';bool flags=false;  //文件传输结束标志struct s_info_client *ts=(struct s_info_client *)client_recv;int radio_fd = Socket(AF_INET,SOCK_DGRAM,0);int on = 1;setsockopt(radio_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));//设置端口号可以重用struct sockaddr_in recv_addr;recv_addr.sin_family=AF_INET;recv_addr.sin_port=htons(SERV_PORT);//广播端口recv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//广播地址bind(radio_fd,(struct sockaddr *)&recv_addr,sizeof(recv_addr));//绑定广播地址while(1){  //获取需要创建的文件名memset(bufname,0,sizeof(bufname));m=recvfrom(radio_fd,bufname,MAXLINE,0,NULL,NULL);//阻塞,接收广播数据if(m==0){break;}printf("bufname: %s\n",bufname);FILE *clifile_fd=(FILE *)creatfile(bufname); //获取创建后的文件流描述clifile_fd//不断向文件中写数据while((n=recvfrom(radio_fd,buf,MAXLINE,0,NULL,NULL))>0){printf("n: %d\n",n);printf("strlrn(buf): %ld\n",strlen(buf));printf("%s",buf);for(int i=0;i<strlen(buf);i++){printf("%c\n",buf[i]);//判断文件是否结束if(charflags==buf[i]){fclose(clifile_fd);printf("%s get successed.\n",bufname);flags=true;break;}//fwrite(buf,sizeof(char),strlen(buf),serfile_fd); //将字符串写入文件流fputc(buf[i],clifile_fd); //将字符一一写入文件流}//文件传输结束if(flags==true){break;}memset(buf,0,sizeof(buf));fflush(clifile_fd);}flags=false; //文件传输标志初始化,进入下一文件读取} Close(ts->sockfd);Close(radio_fd);
}void *do_client(void *argv)
{struct sockaddr_in servaddr_client;char buf[MAXLINE];char bufname[MAXLINE];int sockfd;pthread_t tid_client;struct argv_com *Argv=(struct argv_com *)argv;struct s_info_client ts;//creatfile(Argv->filename); //本端创建文件sockfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr_client, sizeof(servaddr_client));servaddr_client.sin_family = AF_INET;inet_pton(AF_INET,"Argv->argv1", &servaddr_client.sin_addr);servaddr_client.sin_port = htons(atoi(Argv->argv2));Connect(sockfd,(struct sockaddr *)&servaddr_client, sizeof(servaddr_client));ts.servaddr=servaddr_client;ts.sockfd=sockfd;pthread_create(&tid_client,NULL,do_work_client,(void*)&ts);  //客户端线程pthread_detach(tid_client);//将客户端向服务器发送要传输数据的文件名while(1){memset(bufname,0,sizeof(buf));// fgets(bufname,sizeof(bufname),stdin); //fgets与scanf函数的区别scanf("%s",bufname);printf("bufname:%s\n ",bufname);FILE *clifile_fd=fopen(bufname,"r"); //只允许读数据if(clifile_fd==NULL){printf("client file not found.\n");}else{Send(sockfd,bufname,MAXLINE,0);int length=0;memset(buf,0,sizeof(buf));//读数据,发给服务器while((length=fread(buf,sizeof(char),sizeof(buf),clifile_fd))>0) //读数据{if(Send(sockfd,buf,sizeof(buf),0)<0) //发数据{printf("send file:%s faile\n.",bufname);break;}memset(buf,0,sizeof(buf));}fclose(clifile_fd);//文件结束标志'#'char charflags='#';Send(sockfd,&charflags,sizeof(char),0); printf("%s transfer successed.\n",bufname);}    }Close(sockfd);
}int main(int argc,char *argv[])
{pthread_t tid_server,tid_client;  //定义pthread_t型的变量if(argc<3){  // 进入服务器端printf("Enter SERVER\n");//创建服务器线程pthread_create(&tid_server,NULL,do_server,NULL);  pthread_detach(tid_server);}else{struct argv_com  ar;ar.argv1=argv[1];ar.argv2=argv[2];// ar.filename=argv[3];//创建客户端线程//printf("argv:%s \n",ar.argv1);pthread_create(&tid_client,NULL,do_client,(void *)&ar);  pthread_detach(tid_client);}pthread_exit(0); //主线程结束,但此处主线程等待其他子线程,待其他主线程结束后,一同退出return 0;  //须在pthread_exit()函数后使用,否则主线程不等待子线程直接退出
}

client创建两个文件1.txtt、2.txt,server创建两个文件a.txt、b.txt ,测试后运行结果:

strcat()、strcmp()、strcpy()函数的用法

char *strcat(char *dest,const char *src);
char *strncat(char *dest, const char *src, size_t n);

strcat与strncat都是字符串连接函数,功能上稍有区别:
strcat可以把src字符串的全部内容复制到dest字符串后面(此时dst的值发生改变);
strncat 把一个字符串指定长度复制到另一个字符串后面,若超过指定长度就复制整个字符串

int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);

strcmp与strncmp都是用来比较字符串(或比较数数组、字符常量)的,区别在于是否比较指定长度的字符串e

若s1==s2则返回0;

若s1>s2则返回正数;

若s1<s2则返回负数。

即:两个字符串子自左向右逐个字符比较(按ASCII值比较大小),直到出现不同的字符或者遇到’\0‘为止。

strncmp函数是比较指定的size个字符,若s1和s2的前size个字符相同,则返回0

注:针对单个字符的比较,也可以直接用“=/=” 即if(‘A’==‘B’)。

char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

dest是指目标字符串指针,src是指源字符串指针。dest与src所指的区域不可重叠,strcpy以’\0‘作为结束标志,dest必须有足够的空间来存放src所包含的字符串(包含结束符NULL),成功执行后返回目标数组指针dest。

strcpy是一种不安全的写法,常用strncpy来替代,其表示将src前n个字符复制到字符串dest,但strncpy不会向dest追加结束标记’\0’,为了避免该问题,strncpy的标准用法(手动添加\0)如下:

strncpy(dest,src,sizeof(dest)-1);

dest[sizeof[dest)-1]=‘\0’;

len=strlen(dest);

针对字符串的复制,也可以使用snprintf()

int snprintf ( char * str, size_t size, const char * format, … );

str——目标字符串;

size——拷贝的字符数;

format——格式化成字符串

若format的字符串长度小于size,则会全部复制到str中;如果格式化后的字符串长度大于等于 size,超过 size 的部分会被截断,只将其中的 (size-1) 个字符复制到 str 中,并给其后添加一个字符串结束符 \0,返回值为欲写入的字符串长度。

返回值:

1、如果格式化后的字符串长度小于 size,则会把字符串全部复制到 str 中,并给其后添加一个字符串结束符 \0;

2、如果格式化后的字符串长度大于等于 size,超过 size 的部分会被截断,只将其中的 (size-1) 个字符复制到 str 中,并给其后添加一个字符串结束符 \0,返回值为欲写入的字符串长度

例:

#include <stdio.h> int main()
{ char buffer[50]; char* s = "runoobcom"; // 读取字符串并存储在 buffer 中int j = snprintf(buffer, 6, "%s\n", s);  //返回值j为欲写入的字符串长度// 输出 buffer及字符数printf("string:\n%s\ncharacter count = %d\n", buffer, j);  return 0;
} /*结果输出:
string:
runoo
character count = 10*/

fgets()、scanf()、gets()函数的用法 及区别

scanf()输入了空格就表示字符串结束,空格后的字符作为下一个输入;gets()函数将接收输入的整个字符串直到遇到换行符为止;

fgets()函数既可以从文件中读入数据,也可以从屏幕中输入一字符串:

fgets(str,n,fp);

读入n-1(最多读入n-1)个字符到str为起始地址的字符串中,第n个字符存放换行符(✔)

文件流的读出与写入(fwrite、fread)方法

#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

返回值:读或写的记录数,成功时返回的记录数等于nmemb,出错或读到文件末尾时返回的记录
数小于nmemb,也可能返回0。

fread和fwrite用于读写记录,这里的记录是指一串固定长度的字节,比如一个int、一个结构体或者一个定长数组。参数size指出一条记录的长度,而nmemb指出要读或写多少条记录,这些记录在ptr所指的内存空间中连续存放,共占size * nmemb个字节,fread从文件stream中读出size * nmemb个字节保存到ptr中,而fwrite把ptr中的size * nmemb个字节写到文件stream中

socket通信传输下,recv获取后的buf的形式

buf是一个字符数组,根据获取的数据填满每一个buf[i],其中也包括换行符。

sizeof的用法

sizeof()获取数据在内存中所占用的存储空间,以字节为单位进行计数。

int a=10;
int arr=[1,2,3];
char str[]="hello";
int len_a = sizeof(a);
int len_arr = sizeof(arr);
int len_str = sizeof(str)
printf("len_a=%d,len_arr=%d,len_str=%d\n",len_a,len_arr,len_str)
// len_a=4,len_arr=12;len_str=6(双引号""的末尾自动补上'\0'也算一位)

例如:

int arr[]={1,2,3};
for(int i=0;i<(sizeof(arr)/sizeof(int));i++){printf("%d,",arr[i]);
}

此外:

//使用new关键字,在堆区开辟一个int数组
int* arr = new int[5]{1,2,3,4,5};
//这里并不是计算数组arr所占用的内存空间大小,而是计算指针所占内存大小,32位系统指针占4字节,64位系统指针占8字节
cout << sizeof(arr) << endl;
//解指针,因为arr指针指向的时数组的首元素,所以实际计算的是int类型的数据所占用内存空间,int类型占4字节
cout << sizeof(*arr) << endl; //计算arr指针变量所指的数组元素中第一个元素所占内存的大小

socket网络编程——基于socket通信实现对客户端与服务器间的文件互传相关推荐

  1. linux socket编程web服务器实现报文解析,[Socket][网络编程]程序范例:Linux下连接WEB服务器...

    昨天试着在Ubuntu 下用C语言写了一个客户端,用来向WEB服务器上交请求并取回响应报文. 同时,这也是一个通用的基于IPv4的客户端程序例程. [cpp] /* socket test on li ...

  2. Socket实现文件互传(一)

    最近一直在做比赛的一个项目,实现客户端和PC端的文件互传,其实一开始在看到这个题目的时候,完全不知道怎么去实现,感觉一脸懵逼,后来在查阅了资料以及相关书籍后了解到可以用Socket来进行通信,通过IO ...

  3. socket网络编程 java_Java Web 基础(一) 基于TCP的Socket网络编程

    一.Socket简单介绍 Socket通信作为Java网络通讯的基础内容,集中了异常.I/O流模式等众多知识点.学习Socket通信,既能够了解真正的网络通讯原理,也能够增强对I/O流模式的理解. 1 ...

  4. 基于Linux的socket网络编程项目——游侠手机商城

    基于Linux的socket网络编程项目--游侠手机商城 一.项目说明 二.项目使用的技术 三.客户端搭建 四.服务器端搭建 一.项目说明 本项目是一个仿真手机商城类系统,基本功能: 登录界面功能:用 ...

  5. Linux C++服务器项目——网络编程1 (socket通信,服务端,客户端)

    牛客 C++高并发服务器开发 参考笔记 1.MAC地址 2 IP地址 2.1 简介 2.2 IP地址编址方式 2.3 子网掩码 3 端口 3.1 简介 3.2 端口类型 4 网络模型 4.1 OSI七 ...

  6. Python Socket网络编程(二)局域网内和局域网与广域网的持续通信

    目录 前言 IP地址 简介 公有IP 私有IP 局域网之间网络通信 前提 功能描述 源码 运行结果 局域网与广域网网络通信 前提 源码 结语 前言 本系列博客是笔者学习Python Socket的过程 ...

  7. C# Socket网络编程入门(服务器与客户端通信,客户端与客户端通信)

    WebSocket全双工通讯链接,用于前台和后台自由发送信息 一.效果展示: 效果描述: 1.服务器充当管理员,给所有人发送信息,除服务器以外其他人都能接受到. 2.其他用户发送信息除自己以外其他用户 ...

  8. Linux C++/Java/Web/OC Socket网络编程

    一,Linux C++ Socket网络编程 1.什么是TCP/IP.UDP? TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制 ...

  9. linux网络编程、socket编程

    进程间通信: 特点:依赖于内核,造成缺陷--无法实现多机通信. 网络: 地址:由IP地址(IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物 ...

最新文章

  1. vector iterator not incrementable 的问题
  2. lvs主从服务器转发风暴(广播风暴、大流量)
  3. 多线程小抄集(新编三)
  4. MAD huashi
  5. 腾讯2016春招安全岗笔试题解析
  6. SoapUI笔记-使用SoapUI测试WebService服务端
  7. python自动填日志_Selenium3+python自动化012+日志logging基本用法、高级用法
  8. LeetCode 71. Simplify Path
  9. Node.js 11.14.0 发布,服务器端的 JavaScript 运行环境
  10. 菜鸟版JAVA设计模式-从抽象与实现说桥接模式
  11. PHPWAMP自定义添加PHP版本教程,支持无限添加PHP和Mysql版本
  12. 销售服务器账务处理,​销售货物和提供技术服务怎么做账
  13. molecule html5 游戏,精选超炫html5网站收集
  14. 云服务到底是什么东西?
  15. 基于扰动观测器的直流电机调速系统,(售出不退慎拍!) 有计算公式,仿真模型
  16. uniapp、uniCloud实现微信公众号自动查询淘宝京东优惠券制作过程
  17. @Cacheable使用详解
  18. idea 2020.1 连接MySQL数据库的两种方法
  19. tomcat国内下载地址
  20. python 前端框架比较_浅谈五大Python Web框架

热门文章

  1. Unity中启动外部文件
  2. 国内外网络公开课书签搬运
  3. android 平板root,【Android】免root即能修改Android ID,實現手機平板共用一個Line不互踢...
  4. JavaScript中的Date日期、String字符串、Array数组、Math提供对数据的数学计算
  5. 哪里可以测试XRD(D8 Advance X射线衍射仪)
  6. 本学期计算机课总结,计算机学期课程PPT总结
  7. 什么是全景漫游,VR全景漫游系统功能有哪些?
  8. Pulsar 和 Kafka 基准测试报告(完整版)
  9. java上传图片限制大小_java怎么限制上传图片的大小
  10. 黄帝81难经1-10难