linux如何利用套接字传输一个txt文件,Linux socket文件传输

1 简介

在《Linux socket编程案例》中,实现了服务器/客户端之间的字符串传输,本文要将其改造为实现文件传输。

角色:服务器--文件接收者

客户端--文件发送者

2 主要流程

文件发送端(client):fopen(打开本地文件)->fread(读取本地文件内容)->write(通过socket将本地文件内容发送到服务器)

文件接收端(server):read(通过socket读取client发送过来的内容)->fopen(创建一个本地文件)->fwrite(将通过socket接收的内容写入文件)

3 主要难题

文件传输一般需要协议(例如ftp)。由于这里基于TCP实现文件传输,发送端/接收端之间需要协调一致,例如,何时开始传输,何时结束传输。

开始传输:如果发送端与接收端连接上,就随时可以传输内容,一般以发送端为主导;

结束传输:a) 接收端主动结束;b)  发送端主动结束。

文件的开始传输比较简单,这里重点分析结束传输。

3.1 接收端主动结束

接收端主动结束接受文件,有多种情况:a)当接受不到新数据时,结束文件接收;当接收到特定的字符时,停止接收。

对于第a)种情况,如果文件传输过程中出现中断,或者网络状况不好时,数据之间出现时间间隔,将会导致错误的结束文件传输。

3.2 发送端主动结束

3.2.1 发送结束符

文件发送端发送完数据后,发送一个结束符告诉接收端,通知其结束文件接收。

3.2.2 断开连接

文件发送端发送完数据后,直接断开与接收端(服务器)的连接。接受端要负责检测连接是否已经断开,如果是,则停止接受文件。本文将重点分析此方法。下面,先给出发送端代码:

/******* 发送端:客户端sent.c ************/

#include #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[])

{

int sockfd;

char buffer[1024];

struct sockaddr_in server_addr;

struct hostent *host;

int portnumber;

FILE *fp = fopen( "./sent.c", "rb" );

if ( fp == NULL) {

fprintf(stderr, "Open file error\n");

exit( 1 );

}

if( argc != 3) {

fprintf( stderr, "Usage:%s hostname portnumber\a\n", argv[0] );

exit(1);

}

if( ( host = gethostbyname( argv[1] ) ) == NULL) {

fprintf(stderr,"Gethostname error\n");

exit(1);

}

if( ( portnumber = atoi( argv[2] ) )<0) {

fprintf( stderr, "Usage:%s hostname portnumber\a\n", argv[0] );

exit(1);

}

/* 客户程序开始建立 sockfd描述符 */

if( ( sockfd = socket( AF_INET,SOCK_STREAM, 0 ) ) == -1) {

fprintf( stderr, "Socket Error:%s\a\n", strerror(errno) );

exit(1);

}

/* 客户程序填充服务端的资料 */

bzero( &server_addr, sizeof( server_addr ) );

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons( portnumber );

server_addr.sin_addr = *( ( struct in_addr * )host->h_addr );

/* 客户程序发起连接请求 */

if( connect( sockfd, ( struct sockaddr * )( &server_addr ), sizeof( struct sockaddr ) ) ==-1 ) {

fprintf(stderr, "Connect Error:%s\a\n", strerror(errno));

exit(1);

}

size_t nreads, nwrites;

while( nreads = fread( buffer, sizeof(char), sizeof( buffer ), fp) ) {

if ( ( nwrites = write( sockfd, buffer , nreads) ) != nreads ) {

fprintf(stderr, "write error\n");

fclose( fp );

close( sockfd );

exit( 1 );

}

}

/* 结束通讯 */

fclose( fp );

close( sockfd );

exit(0);

}    说明:上述代码将本地文件sent.c读取并通过socket发送。

4 网络连接状况检测

根据第3章的描述,为了实现由文件发送端断开连接而达到结束文件传输的目的,需要接收端能够检测当前的网络状况(是否已经断开)。那么,怎样检测呢[11]? 参考资料[9]说明了如果不检测是否已经断开就继续发数据,将会导致程挂掉。参考资料[10]总结了判断客户端socket断开连接的方法,本文选择其方法二,接收端完整代码如下:

/******* 文件接收端:服务器(recv.c) ************/

#include #include #include #include #include #include #include #include #include #include #include #include // struct tcp_info类定义

#include // bool类型[12]

int main(int argc, char *argv[])

{

int sockfd;

struct sockaddr_in server_addr;

struct sockaddr_in client_addr;

int portnumber;

if( argc != 2 ) {

fprintf(stderr,"Usage:%s portnumber\a\n", argv[0]);

exit(1);

}

if( ( portnumber = atoi( argv [1] )) < 0 ){

fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);

exit(1);

}

/* 服务器端开始建立socket描述符 */

if( ( sockfd = socket( AF_INET, SOCK_STREAM, 0) ) == -1 ) {

fprintf(stderr,"Socket error:%s\n\a",strerror(errno));

exit(1);

}

/* 服务器端填充sockaddr结构 */

bzero( &server_addr, sizeof(struct sockaddr_in) );

server_addr.sin_family = AF_INET;

server_addr.sin_addr.s_addr = htonl( INADDR_ANY );

server_addr.sin_port = htons( portnumber );

/* 捆绑sockfd描述符 */

if( bind( sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr))==-1) {

fprintf( stderr, "Bind error:%s\n\a", strerror( errno ) );

exit(1);

}

/* 监听sockfd描述符 */

if( listen(sockfd,5) == -1) {

fprintf( stderr, "Listen error:%s\n\a", strerror( errno ) );

exit(1);

}

while( 1 )

{

/* 服务器阻塞,直到客户程序建立连接 */

int sin_size = sizeof(struct sockaddr_in);

int new_fd = accept( sockfd, (struct sockaddr *)(&client_addr), &sin_size );

if( new_fd == -1 ) {

fprintf(stderr,"Accept error:%s\n\a", strerror( errno ) );

exit( 1 );

}

printf("Server get connection from %s\n",

inet_ntoa( client_addr.sin_addr ) );

bool tcp_established = true;

FILE *save_fp = fopen("out.txt", "w");

while( tcp_established ) {

struct tcp_info info;

int len = sizeof(info);

getsockopt( new_fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len );

if( info.tcpi_state == TCP_ESTABLISHED ) { // TCP连接还没有中断,可以读数据

char buffer[1024];

ssize_t length = read( new_fd, buffer, sizeof(buffer) );

if( length == -1 ) {

fprintf(stderr, "Read Error:%s\n", strerror( errno ));

exit(1);

}

else if ( length > 0){

fwrite(buffer, sizeof(char), length, save_fp);

}

}

else {

tcp_established = false;

fclose( save_fp );

printf("received finished !\n");

}

}

/* 这个通讯已经结束 */

close( new_fd );

/* 循环下一个 */

}

close( sockfd );

exit(0);

}

说明:上述代码将接受到的文件内容保存于out.txt文件中。

5 代码完善

更加完善的代码,见《Linux socket文件传输2》

参考资料

[1]套接字传输文件的试验

[2]套接字实现文件传输

[3]怎么利用套接字传输大文件

[4]应用Socket套接字技术实现文件远程传输的方式分析

[5]CFile类循环读取大文件及套接字传输文件

[6]Linux网络编程:UDP实现可靠的文件传输

[7]Linux网络编程之socket文件传输示例

[8]Linux下的socket文件传输

[9]linux socket客户端被断开的后果和处理方法

[10]服务器中判断客户端socket断开连接的方法

[11]linux socket怎么检测断开

[12]关于linux下C语言编译器gcc不认识bool型的问题

[13]getsockopt的TCP层实现剖析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值