Linux socket文件传输2

来源:互联网 发布:和飞行员谈恋爱 知乎 编辑:程序博客网 时间:2024/06/02 04:35

    以下代码是《Linux socket文件传输》的完善版本,主要是添加了命令行参数的设置功能。

1 发送端

/******* 发送端:客户端 sent.c ************/#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/types.h>#include <arpa/inet.h>#include <getopt.h>#include <string.h>#include <stdbool.h>#define MY_NAME "sent"#define CMD_FAILSE  0#define CMD_TURE 1#define CMD_NOFILE  2 // 没有指定要发送的文件static  int portnumber = 8888;static  char sent_file_name[256];static struct hostent *host;/* * 命令行选项  */static struct option options[] = {   {"help",no_argument,        0,  'h' },   {"port",  required_argument, 0,  'p' },    {"file",  required_argument, 0,  'f' },   {"server",  required_argument, 0,  's' },   {0,         0,                 0,  0 }};/* * 命令行选项说明 */static const char *options_descriptions[] = {"Show this help and quit.","Set port number.","Send file name.","Set server ip.",};/* * 功 能:显示程序的帮助信息 */static void usage(void){unsigned int i;printf("Usage:\n\t" MY_NAME " -f <filename> [-p <port>]""\n\nOptions:\n");for (i = 0; options[i].name; i++)printf("\t-%c, --%s\n\t\t\t%s\n",options[i].val, options[i].name,options_descriptions[i]);}/* * 功 能: 解析程序参数 * 参 数: 同main()的参数 * 返 回: true —— 参数非法,或者是--help *   false —— 参数合法,且不是--help */static int cmd_arg_parse( int argc, char **argv ){int opt, option_index = 0;int ret = CMD_NOFILE;while ( ( opt = getopt_long(argc, argv, "p:f:s:h",options, &option_index)) != -1) {   switch ( opt ) {   case 'p':   if (optarg)if( ( portnumber = atoi( optarg )) < 0 ){fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);return CMD_FAILSE;}   break;   case 'f':   if (optarg) {strcpy(sent_file_name, optarg);ret = CMD_TURE;}   break;    case 's':if (optarg) {if( ( host = gethostbyname( optarg ) ) == NULL) { fprintf(stderr, "Gethostname error\n"); return CMD_FAILSE;}}break;    case 'h':  // 显示帮助信息usage();break;   default:   printf("?? getopt returned character code 0%o ??\n", opt);   break;   }   }   if ( optind < argc ) {   printf("non-option ARGV-elements: ");   while ( optind < argc )   printf( "%s ", argv[optind++] );   printf("\n");   }   return ret;}/* * 提示当前的参数设置 */static void hints(void){printf("port number : %d\n", portnumber);printf("file name : %s\n", sent_file_name);}int main(int argc, char *argv[]){int sockfd = -1;int exit_code = 0;struct sockaddr_in server_addr;switch ( cmd_arg_parse(argc, argv) ) {case CMD_NOFILE:fprintf(stderr, "Error: You must specified a file name !\n");case CMD_FAILSE:return -1;}hints();FILE *fp = fopen( sent_file_name, "rb" );if ( fp == NULL) {fprintf(stderr, "Open file error\n");exit_code = 1;goto FINISHED;}/* 客户程序开始建立 sockfd描述符  */if( ( sockfd = socket( AF_INET,SOCK_STREAM, 0 ) ) == -1) {fprintf( stderr, "Socket Error:%s\a\n", strerror(errno) );exit_code = 1;goto FINISHED;}/* 客户程序填充服务端的资料 */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_code = 1;goto FINISHED;}size_t nreads, nwrites;char buffer[1024];while( nreads = fread( buffer, sizeof(char), sizeof( buffer ), fp) ) {if ( ( nwrites = write( sockfd, buffer , nreads) ) != nreads ) {fprintf(stderr, "write error\n");exit_code = 1;goto FINISHED;}}/* 结束通讯     */FINISHED:if ( fp != NULL )fclose( fp );if ( sockfd != -1)close( sockfd );exit( exit_code );}

    说明:sent --help可以查看帮助说明

2 接收端

/******* 文件接收端:服务器(recv.c) ************/#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/types.h>#include <arpa/inet.h>#include <sys/socket.h>#include <netinet/tcp.h>#include <stdbool.h>#include <getopt.h>#include <string.h>#define MY_NAME "recv"static int portnumber = 8888;static char save_file_name[256] = "out.txt";/* * 命令行选项  */static struct option options[] = {   {"help",no_argument,        0,  'h' },   {"port",  required_argument, 0,  'p' },    {"f",  required_argument, 0,  'f' },   {0,         0,                 0,  0 }};/* * 命令行选项说明 */static const char *options_descriptions[] = {"Show this help and quit.","Set port number.","Set output file name.",};/* * 功 能:显示程序的帮助信息 */static void usage(void){unsigned int i;printf("Usage:\n\t" MY_NAME " [-f <filename>] [-p <port>]""\n\nOptions:\n");for (i = 0; options[i].name; i++)printf("\t-%c, --%s\n\t\t\t%s\n",options[i].val, options[i].name,options_descriptions[i]);}/* * 功 能: 解析程序参数 * 参 数: 同main()的参数 * 返 回: true —— 参数非法,或者是--help *   false —— 参数合法,且不是--help */static bool cmd_arg_parse( int argc, char **argv ){int opt, option_index = 0;;int ret = true;while ( (opt = getopt_long(argc, argv, "p:f:h",options, &option_index)) != -1) {   switch ( opt ) {   case 'p':   if (optarg)if( ( portnumber = atoi( optarg )) < 0 ){fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);return false;}   break;   case 'f':   if (optarg)strcpy(save_file_name, optarg);   break;   case 'h':  // 显示帮助信息usage();ret = false;break;   default:   printf("?? getopt returned character code 0%o ??\n", opt);   ret = false;   break;   }   }   if ( optind < argc ) {   printf("non-option ARGV-elements: ");   while ( optind < argc )   printf( "%s ", argv[optind++] );   printf("\n");   ret = false;   }   return ret;}/* * 提示当前的参数设置 */static void hints(void){printf("port number : %d\n", portnumber);printf("file name : %s\n", save_file_name);}int main(int argc, char *argv[]){int sockfd = -1;int exit_code = 0;struct sockaddr_in server_addr;struct sockaddr_in client_addr;// 命令行参数分析if ( !cmd_arg_parse(argc, argv) )return -1;// 提示hints();/* 服务器端开始建立socket描述符 */if( ( sockfd = socket( AF_INET, SOCK_STREAM, 0) ) == -1 ) {fprintf(stderr,"Socket error:%s\n\a",strerror(errno));exit_code = 1;goto FINISHED;}/* 服务器端填充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_code = 1;goto FINISHED;}/* 监听sockfd描述符  */if( listen(sockfd, 5) == -1) {fprintf( stderr, "Listen error:%s\n\a", strerror( errno ) );exit_code = 1;goto FINISHED;}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_code = 1;goto FINISHED;}printf("Server get connection from %s\n",inet_ntoa( client_addr.sin_addr ) );    bool tcp_established = true;FILE *save_fp = fopen(save_file_name, "w");if ( save_fp == NULL) {exit_code = 1;goto FINISHED;}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_code = 1;goto FINISHED;}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 );/* 循环下一个     */  }FINISHED:if ( sockfd != -1)close( sockfd );exit( exit_code );}
    说明:recv --help可以查看帮助说明


0 0
原创粉丝点击