• 服务器客户端 之 文件下载


    约定协议:

      客户端发送请求:char buf[256]=文件名

      服务端回复:  int len=文件长度 + 文件信息

            若文件不存在返回 "-1" + "file not found\n"

            若文件读取错误返回 "-2" + "server error\n"

            若文件为目录     "-3" + "dirtory error\n"

    -----------TCP协议 客户端流程--------------------

    创建socket

          根据协议族,协议类型,协议编号,向操作系统申请一个socket 文件描述符;

          int socket(int domain, int type, int protocol);

    连接socket

          把申请的 socket 描述符 和 (服务器的ip类型,服务器的ip地址,服务器的端口号)连接起来

          connect(int socket_fd, const struct sockaddr *addr, socklen_t addr_len);

    通话

          while(1)

          {

            通过socket 文件描述符 给 服务器发请求

            write(socket, request, strlen(request));

            分析服务器的回应,保存数据

            readline(socket, request, request_maxlen);

          }

    ---------------TCP协议 服务器端流程----------------------------

    创建socket

          根据协议族,协议类型,协议编号,向操作系统申请一个socket 文件描述符;

          int server_sockfd = socket(int domain, int type, int protocol);

    绑定IP端口号到socket

          把本地的ip类型,ip地址,端口号 绑定到 socket上

          int bind(int server_sockfd, const struct sockaddr *addr, socklen_t addr_len);

    监听socket

          监听socket上是否有连接过来,并指定最大连接数(本质是把本地端口号,ip地址和。。。)

          int listen(int server_sockfd, int backlog);

    接受连接    

          不断接受连接,每接收一个连接,就有一对socket(C/S),记录来自客户端连接的ip地址,端口号,并对每个连接请求作出回应,

          struct sockaddr_in client_addr;

          while(1)

          {

              int addr_len = sizeof(client_addr);

              int client_sockfd = acctpt(int server_sockfd, const struct sockaddr *client_addr, &addr_len);

              readline(client_sockfd, buf, buf_maxsize);

              write(client_sockfd, “回应”, maxsize);

          }

    ---------------------------

    /* server.c */
    #include <stdio.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include "wrap.h"
    
    static void my_send(int client_fd, int err, char *msg)
    {
        int size = htonl(err);
        char buf[256];
        memcpy(buf, &size, sizeof(int));
        memcpy(buf+sizeof(int), msg, sizeof(buf)-sizeof(int));
        Writen(client_fd, buf, sizeof(buf));
    }
    
    void server_dialog(int connect_fd)
    {
        int n;                
        char buf[256];        // 文件名最大长度
        int remains;          // 要传送的数据大小
        int fd;               // 本地文件描述符
         
        n = Readn(connect_fd, buf, 256); // 获取客户端请求-文件名
        printf("filename: %s", buf);
    
        if(access(buf, F_OK) == 0){
            remains = file_size(buf);
        }else{
            my_send(connect_fd, -1, "file not found\n");
            return;
        }
    
        printf("filesize: %u\n",remains);
        if(remains < 0){
            my_send(connect_fd, -3, "dir error\n");
            return;
        }
    
        remains = htonl(remains);
        if((fd = open(buf, O_RDONLY)) < 0){
            my_send(connect_fd, -2, "server error\n");
            return;
        }
        
        memcpy(buf, &remains, sizeof(int)); // 告诉客户端要接收的数据长度
        Writen(connect_fd, buf, sizeof(int));
    
        while((n = read(fd, buf, sizeof(buf))) > 0)
    	{   
    		write(connect_fd, buf, n);
    		printf("send %d bytes\n", n);
    	}
    
        close(connect_fd);
    }
    
    int server(char *ip, int port)
    {
        // step1: create socket
        int listenfd = Socket(AF_INET, SOCK_STREAM, 0);
     
        // step2: bind port
        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr)); 
        server_addr.sin_family = AF_INET;  // 协议族
        inet_pton(AF_INET, ip, &server_addr.sin_addr); // ip地址
        server_addr.sin_port = htons(port); // 端口号
        Bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
     
        // step3: listen 
        Listen(listenfd, 20);
     
        // step4: accept connect socket
        socklen_t client_addr_len;
        struct sockaddr_in client_addr;
        int connect_fd;
        while(1)
        {
            client_addr_len = sizeof(struct sockaddr_in);
            connect_fd = Accept(listenfd, (struct sockaddr *)&client_addr, &client_addr_len);
            server_dialog(connect_fd);
        }
        return 0;
    }
     
    int main(int argc, char **argv) 
    {
        if(argc != 3){
            fprintf(stderr, "%s <ip> <port>\n", argv[0]);
            exit(1);
        }
        char *ip = argv[1];
        int port = strtol(argv[2], NULL, 10);
        return server(ip, port);
    }
    

      

    /* client.c */
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include "wrap.h"
    
    void error_exit(int server_fd, int err)
    {
        char buf[256];
        read(server_fd, buf, sizeof(buf));
        fprintf(stderr, "%s\n",buf);
        exit(-1);
    }
    
    void client_dialog(int sockfd, char *file, char *dir)
    {
        int n;             // 每次实际接收到的数据
        char buf[256];     // 数据缓存区
        int remains;       // 要接收的数据大小
        int fd;            // 保存到本地的文件的描述符
    
        if(access(dir, F_OK) < 0){
            mkdir(dir, 0755);
        }
    
        sprintf(buf, "%s/%s", dir, file);       // 要保存的文件路径
        fd = open(buf, O_WRONLY|O_CREAT|O_TRUNC, 0755);
        if(fd < 0)
        {
            perror("open");
            exit(1);
        }
        strcpy(buf,file);
        Writen(sockfd, buf, 256);
    
        Readn(sockfd, buf, sizeof(int)); // 读取文件长度 
        
        memcpy(&remains, buf, sizeof(int));
        remains = ntohl(remains);
        printf("------ filesize = %d\n", remains);
        if(remains<0)
            error_exit(sockfd, remains);
    
        while(remains > 0)
        {
            if(remains < sizeof(buf))
                n = Readn(sockfd, buf, remains);
            else
                n = Readn(sockfd, buf, sizeof(buf));
    
            printf("client remains = %d, n = %d\n", remains,n);
    
            if(n==0) // 对方关闭了
                break;
            remains -= Writen(fd, buf, n);
        }
        close(sockfd);
    }
    
    int download(char *ip, int port, char *file, char *dir)
    {
        int sockfd, n, fd; 
        sockfd = Socket(AF_INET, SOCK_STREAM, 0); 
    
        struct sockaddr_in servaddr;
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        inet_pton(AF_INET, ip, &servaddr.sin_addr);
        servaddr.sin_port = htons(port);
     
        Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    
            client_dialog(sockfd, file, dir);
        return 0;
    }
    
    
    int help_exit(char *exename)
    {
    	printf("%s  <ip> <port> <path> <dir>\n", exename);
    	printf( 
    		   "if [dir] is empty, it downs <file> to current directory.\n"
               "Example:\n"
               "%s 192.168.7.203 8080 /001.jpg .\n"
               "%s 192.168.7.203 8080 /002.jpg ./pic/\n", exename, exename);
    	exit(0);
    }
    
    
    int main(int argc, char *argv[])
    {
    	if(argc != 5)
    	{
    		help_exit(argv[0]);
    	}
    
        return download(argv[1], // ip
                atoi(argv[2]),  // port
                argv[3],  // file
                argv[4]  // destination dirctory
                );
    
    }
    
    /* wrap.c */
    #include "wrap.h"
    
    void perr_exit(const char *s)
    {
    	perror(s);
    	exit(1);
    }
    
    char *getip(const char *domain)
    {	
    	struct hostent *phost = gethostbyname(domain);
    	if(phost == NULL)	
    		perr_exit("gethostbyname");
    	else
    	{
    		static char buf[32];
    		char **net_addr = phost->h_addr_list;	
    		inet_ntop(phost->h_addrtype, net_addr[0], buf, sizeof(buf));
    		return buf;
    	}
    }
     
    char *chomp(char *s) // 删除字符串后面的\n
    {
        char *save = s;
        while(*s){
            if(*s == '\n'){
                *s = '\0';
                return save;
            }
            s++;
        }
        return save;
    }
    ssize_t file_size(char *name) 
    {
        int fd = open(name, O_APPEND);
        if(fd < 0)
        {
            perror("open src file");
            exit(-1);
        }
        off_t off = lseek(fd, 0, SEEK_END); 
        close(fd);
        return (int)off;
    }
    
    int Socket(int family, int type, int protocol)
    {
        // ipv4 对应的族为AF_INET, TCP协议的类型为SOCK_STREAM, 协议protocal一般为0
        int n;
        if((n = socket(family, type, protocol)) < 0)
            perr_exit("socket error");
        return n; // 返回的socket文件描述符
    }
    
    void Bind(int sockfd, const struct sockaddr *sa, socklen_t salen)
    {
        if(bind(sockfd, sa, salen) < 0)
            perr_exit("bind error");
    }
    void Connect(int sockfd, struct sockaddr *sa, socklen_t salen)
    {
    	if((connect(sockfd, sa, salen)) < 0)
    		perr_exit("connect error");
    }
    
    void Listen(int sockfd, int backlog) // 监听sockfd, 最大连接数为backlog
    {
        if(listen(sockfd, backlog) < 0)
            perr_exit("listen error");
    }
    
    int Accept(int sockfd, struct sockaddr *sa, socklen_t *salenptr)
    {
        int n;
    again:
        if( (n = accept(sockfd, sa, salenptr)) < 0)
        {
            if( errno == ECONNABORTED  // 软件引起的连接中止 ; 服务器端拒绝连接ECONNREFUSED
                || errno == EINTR)     // 被信号打断
                goto again;
            else
                perr_exit("accept error");
        }
        return n; 
    
    }
     
    ssize_t Readn(int fd, void *buf, size_t n)
    {
    	size_t gain;
    	ssize_t remains;
    	char *curr;
    
    	remains = n;
    	curr = buf;
    	
    	while(remains > 0)
    	{
    		if((gain = read(fd, curr, remains)) < 0)
    		{
    			if(errno == EINTR)// interrupted by signal	
    				gain = 0;
    			else
    				return -1;
    		}
    		else if(gain == 0) // the other side has been closed
    		{
    			return n - remains;
    		}
    			
    		remains -= gain;	
    		curr += gain;
    	}
    
    	return n;
    }
    
    ssize_t Writen(int fd, void *buf, size_t n)
    {
    	size_t send;
    	ssize_t remains;
    	char *curr;
    	
    	remains = n;
    	curr = buf;
    
    	while(remains > 0)
    	{
    		if( (send = write(fd, curr, remains)) < 0 )
    		{
    			if(send == EINTR)
    				send = 0;
    			else
    				return -1;
    		}
    		remains -= send;
    		curr += send;
    	}
    
    	return n;	
    }
    
    
    static ssize_t my_read(int fd, char *ptr)
    {
    	static int read_cnt;
    	static char *read_ptr; 
    	static char read_buf[128];
    
    	if (read_cnt <= 0) {
    	again:
    		if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
    			if (errno == EINTR)
    				goto again;
    			return -1; 
    		} else if (read_cnt == 0) // the other side has been closed
    			return 0; 
            printf("readline: received %d \n", read_cnt);
    		read_ptr = read_buf; // 第1次读入时read_cnt == 100
    	}
    
    	read_cnt--;         // 第2次以后调用时 直接执行下面语句 100次,使得read_cnt<0;
    	*ptr = *read_ptr++; 
    	return 1;
    }
    
    ssize_t Readline(int fd, void *vptr, size_t maxlen) // 返回行的长度, 功能类似fgets函数
    {
    	ssize_t n, rc;
    	char    c, *ptr;
    
    	ptr = vptr;
    	for (n = 1; n < maxlen; n++) {
    		if ( (rc = my_read(fd, &c)) == 1) { 
    			*ptr++ = c;  // 每次只读1个字符
    			if (c  == '\n') // 整行
    				break;
    		} else if (rc == 0) { // the other side has been closed
    			*ptr = 0;
    			return n - 1;   // 当前已读的数据长度
    		} else
    			return -1;     // error
    	}
    	*ptr  = 0;
    	return n;
    }
    
    
     
    
    /* wrap.h */
    #ifndef _WRAP_H
    #define _WRAP_H
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    void perr_exit(const char *s);
    char *chomp(char *s);
    char *getip(const char *domain);
    int Socket(int family, int type, int protocol);
    void Bind(int sockfd, const struct sockaddr *sa, socklen_t salen);
    void Connect(int sockfd, struct sockaddr *sa, socklen_t salen);
    int Accept(int sockfd, struct sockaddr *sa, socklen_t *salenptr);
    ssize_t Readn(int fd, void *buf, size_t n);
    ssize_t Writen(int fd, void *buf, size_t n);
    static ssize_t my_read(int fd, char *ptr);
    ssize_t Readline(int fd, void *vptr, size_t maxlen);
    #endif 
     
    
    
    # Makefile
    all:
    	gcc wrap.c client.c -o client
    	gcc wrap.c server.c -o server
    clean:
    	-rm server client *.~ *.out
     
    
    
    readme.txt
    直接make
    
    启动一个shell
    
    ./server 《本机ip》 《8080》
    比如:./server 192.168.144.193 8080
    
    
    ./client 《本机ip》  《8080》 《本地文件》 《aaa》
    比如:./client 192.168.144.193 8080 ./readme.txt ./aaa
     
    
  • 相关阅读:
    Docker系列三:Docker容器管理
    Docker系列一:Docker基本概念及指令介绍
    MySQL数据库“十宗罪”(十大经典错误案例)
    用数据驱动渠道推广(下:数据篇)
    用数据驱动渠道推广(上:工具篇)
    日留存、周留存、月留存,究竟怎样才能让更多的用户留下来?
    MySQL中的联合索引学习教程
    VC++NMAKE
    一元三次方程求根公式
    一元四次方程求根公式
  • 原文地址:https://www.cnblogs.com/mathzzz/p/2681084.html
Copyright © 2020-2023  润新知