用socket实现传输树莓派与主机文件数据

基本通讯建立流程

#详见 《Linux高级程序设计》.第五章 或 《UNIX网络编程》.第四章

简单思路

  1. 性能相比下更好的一方作为服务端
  2. 相互透明。对于双方而言,只存在数据的传入/传出
  3. 通用性。可随时调用该程序传输
  4. 服务端只负责等待连接与接收数据
  5. 工作于同一局域网下(功能简单,只考虑数据传输)
  6. 使用TCP , IPv4

一些细节

  1. accept与recv都可能阻塞程序。
  2. 当recv堵塞时客户端断开连接会使其返回0
  3. TCP发送缓冲区满时,send将堵塞

服务端源码

/**
argv[1]:IP地址
argv[2]:端口号
示范:
# ./filesoc 192.168.1.1 8081
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

int main( int argc , char *argv[] ) {
	
//----------build link----------//
//----------------------------------------------------------------------------------------------------------//
	int mysoc , tosoc , len , sin_size;
	struct sockaddr_in myaddr , toaddr ; 
	char buf[BUFSIZ] ;

//装填sockaddr结构内容
	memset( &myaddr , 0 , sizeof (myaddr) ) ;
	myaddr.sin_family = PF_INET ;
	myaddr.sin_addr.s_addr = inet_addr(argv[1]) ;
	myaddr.sin_port = htons(atoi(argv[2])) ;
	printf( "Server IP %s:%s\n" , argv[1] , argv[2] ) ;

//socket()
	printf("Setting socket.....") ;	
	if ( ( mysoc = socket( PF_INET , SOCK_STREAM , 0 ) ) < 0 )
		return printf("errof\n") ;
	printf("done\n") ;

//bind()
	printf("Setting bind.....") ;
	if( bind( mysoc , (struct sockaddr*)&myaddr , sizeof (struct sockaddr) ) < 0 ) 
		return printf("error\n") ;
	printf("done\n") ;

//listen()与 accept()
	listen( mysoc , 2 ) ;
	printf("default number of links: 2\n") ;
	printf("withing accept....") ;
	sin_size = sizeof (struct sockaddr) ;
	if ( ( tosoc = accept( mysoc , (struct sockaddr*)&toaddr , &sin_size ) ) < 0 ) 
		printf("error\n") ;
	printf("done\nlink to %s\n" , inet_ntoa(toaddr.sin_addr) ) ;

//发送一条欢迎消息
	send( tosoc , "link done\n" , 10 , 0 ) ;
//----------------------------------------------------------------------------------------------------------//
	
//客户端的第一条消息是文件名,服务端依照此消息建立同名文件
	FILE *fp ; 
	len = recv( tosoc , buf , BUFSIZ , 0 ) ;
	buf[len] = '\0' ;
	printf(buf,"\n") ; 
	if ( ( fp = fopen( buf , "ab" ) ) == NULL )
		return printf("open file error\n") ;

//由客户端发送数据,由客户端关闭连接
	printf("creating file.....") ;	
	while ( ( len = recv( tosoc , buf , BUFSIZ , 0 ) ) != 0 )
		fwrite( buf , 1 , len , fp ) ;
	printf("done") ;

	printf("closed.....") ;
	shutdown( tosoc , 2 ) ;
	fclose(fp);
	printf("done\n") ;
	return 0 ;
}

客户端实现

/**
argv[1]:服务器IP地址
argv[2]:服务器程序端口号
argv[3]: 文件名
示范:
# ./tofilesoc 192.168.1.1 8081 new.jpg
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

int main( int argc , char *argv[] ) {
	
	//----------build link----------//
//----------------------------------------------------------------------------------------------------------//

	FILE *fp ;
	int mysoc , len ; 
	struct sockaddr_in toaddr ;
	char buf[BUFSIZ] ;

//装填sockaddr结构
	memset( &toaddr , 0 , sizeof (toaddr) ) ;
	toaddr.sin_family = AF_INET ;
	toaddr.sin_addr.s_addr = inet_addr(argv[1]) ;
	toaddr.sin_port = htons(atoi(argv[2])) ;
	printf( "Server IP %s:%s\n" , argv[1] , argv[2] ) ;

//socket()
	printf("Setting socket.....") ;
	if ( ( mysoc = socket( AF_INET , SOCK_STREAM , 0 ) ) < 0 )
		return printf("errof\n") ;
	printf("done\n") ;

//connect()
	printf("Setting connect.....") ;
	if ( connect( mysoc , (struct sockaddr*)&toaddr ,
						   	sizeof (struct sockaddr) ) < 0 )
		return printf("error\n") ;
	printf("done\n") ;
//----------------------------------------------------------------------------------------------------------//

//接收服务器发来的欢迎消息
	len = recv( mysoc , buf , BUFSIZ , 0 ) ;
	buf[len] = '\0' ;
	printf(buf) ;
	
//打开文件,发送文件名
	if ( ( fp = fopen( argv[3] , "rb" ) ) == NULL )
		return printf("open file error") ;
	send( mysoc , argv[3] , stelen(argv[3]) , 0 ) ;
	
//发送,当理想发送字节长度与实际长度不符,报错退出
	printf("sending.....") ;
	while (!feof(fp)) {
		len = fread( buf , 1 , BUFSIZ , fp ) ;
		if ( len != send( mysoc , buf , BUFSIZ , 0 ) ) {
			printf("error\n") ;
			break ;
		}
	}
	printf("done\n") ;

	printf("closed.....") ;
	shutdown( mysoc , 2 ) ;
	printf("done\n") ;

	return 0 ;
}

总结

优点:

  1. 极简
  2. 通用,可所随时调用

缺点:

  1. 只能工作于极小型网络
    理由:考虑多路由网络,到达指定路由可以由多条路线,此时两次发送的数据到达顺序不一致(因为只要服务端接收到数据,则立马填入文件,但可以简单的给每发送的数据一个序号解决)
  2. 功能不完善,可能引发一系列错误

实例

主机:kali linux gcc version 9.3.0 ( Debian 9.3.0-15 )
树莓派:官方系统 lite版 gcc version 9.3.0 ( Debian 9.3.0-15 )
网络:2.4G无线局域网
文件:图片

服务器等待连接,现在我们去启动客户端
在这里插入图片描述
客户端(树莓派)
在这里插入图片描述
中间似乎由上面奇怪的代码,别担心,那是我的树莓派电压不足的表现。先看看服务端的反映
在这里插入图片描述
工作以及完成 , 看看效果
在这里插入图片描述
图片可以正常观看

来看看图片大小
在这里插入图片描述
2 596 864 字节
源码中设置为每次传输512个字节(BUFSIZ定义为512),效果还是不错的

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值