以下代码是《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可以查看帮助说明