Linux网络编程 局域网文件共享工具开发

在linux下,开发一个局域网文件共享工具,其实并不局限于局域网,只要是有ip地址和端口号(默认是5168),就能进行文件共享。
功能:

  • 客户端向服务端发送文件
    ./Client IP地址 send 文件名 //Client是编译后的文件名 自行更改
  • 客户端从服务器下载文件
    ./Client IP地址 download 文件名 //注意这里的文件名是服务端的
  • 客户端获取服务端的文件目录
    ./Client IP地址 ls

服务端启动方法
./Server //名字可自行更改

代码:
Client端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

#define PORT 5168
#define MSGLEN 1024

int Connect(char *addrname)
{
    int sockfd;
    struct sockaddr_in sevrAddr;
    struct hostent *host;
    host = gethostbyname(addrname);

    // 声明并初始化一个socket地址结构
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket error");
        exit(-1);
    }
    else
        printf("socket ok\n");
    sevrAddr.sin_family = AF_INET;
    sevrAddr.sin_port = htons(PORT);
    sevrAddr.sin_addr = *((struct in_addr *)host->h_addr);
    bzero(&sevrAddr.sin_zero, 8);

    // 向服务器发起连接,连接成功后client_socket_fd代表了客户端和服务器的一个socket连接
    if (connect(sockfd, (struct sockaddr *)&sevrAddr, sizeof(struct sockaddr)) == -1)
    {
        perror("connect error");
        exit(-1);
    }
    else
        printf("connect ok\n");

    return sockfd;
}
void file_send(char *addrname, char *option, char *filename)
{
    int sockfd;
    FILE *fp;
    char readBuff[MSGLEN];
    char recv_filename[50];
    int len;

    //获取套接字
    sockfd = Connect(addrname);

    //发送send命令到服务端
    if (send(sockfd, option, sizeof(option), 0) < 0)
    {
        perror("send option error");
        exit(-1);
    }
    sleep(1);

    //发送文件名到服务端
    bzero(&recv_filename, sizeof(recv_filename));
    strcpy(recv_filename, filename);
    if (send(sockfd, recv_filename, sizeof(recv_filename), 0) < 0)
    {
        perror("send filename error");
        exit(-1);
    }
    sleep(1);

    fp = fopen(filename, "rb");
    if (fp != NULL)
    {
        printf("sending file");
        bzero(&readBuff, MSGLEN);
        while (1)
        {
            if ((len = fread(readBuff, 1, MSGLEN, fp)) > 0)
            {
                if (send(sockfd, readBuff, len, 0) < 0)
                {
                    perror("send error");
                    exit(-1);
                }
                else
                {
                    printf(".");
                    bzero(&readBuff, MSGLEN);
                }
            }
            else if (len == 0)
            { //等于0表示文件已到末尾
                printf("\nfile send success\n\n\n");
                fclose(fp);
                break;
            }
            else
            {
                perror("read error");
                exit(-1);
            }
        }
    }
    else
    {
        printf("open file failed\n");
    }
    close(sockfd);
}
void file_dowload(char *addrname, char *option, char *filename)
{
    int sockfd;
    int dfp;
    char dowBuff[MSGLEN];
    char recv_filename[50];
    int dow_len;
    char message[50];

    sockfd = Connect(addrname);

    //发送dowload命令到服务端
    if (send(sockfd, option, sizeof(option), 0) < 0)
    {
        perror("dowload option error");
        exit(-1);
    }
    sleep(1);

    //发送想要下载的文件名
    bzero(&recv_filename, sizeof(recv_filename));
    strcpy(recv_filename, filename);
    if (send(sockfd, recv_filename, sizeof(recv_filename), 0) < 0)
    {
        perror("send filename error");
        exit(-1);
    }
    sleep(1);

    if (recv(sockfd, message, sizeof(message), 0) < 0) //收到服务器是否有该文件的信息
    {
        perror("receive message error");
        exit(-1);
    }

    if (strcmp(message, "file no exists") == 0) //服务器没有该文件
    {
        printf("file doesn't exists in the server\n");
        close(sockfd);
        exit(-1);
    }
    else
    {
        //准备接收文件
        dfp = open(filename, O_RDWR | O_CREAT, 777);
        printf("%d\n", dfp);
        int flags = 0;
        bzero(&dowBuff, MSGLEN);
        while ((dow_len = recv(sockfd, dowBuff, MSGLEN, 0)) > 0)
        {
            flags++;
            if (flags == 1)
            {
                printf("dowload file start");
            }
            else
            {
                printf(".");
            }
            if (write(dfp, dowBuff, dow_len))
            {
                bzero(&dowBuff, MSGLEN);
            }
            else
            {
                perror("write error");
                break;
            }
        }
        if (flags == 0)
        {
            perror("dowload file error");
            close(sockfd);
        }
        if (flags > 0)
        {
            printf("\ndowload file success\n");
            close(sockfd);
        }
    }
}
void file_look(char *addrname, char *option)
{
    int sockfd;
    char recv_Buff[MSGLEN];
    int ok;   //通信状态标识
    int sent; //收发字节数

    sockfd = Connect(addrname);//初始化套接字并进行连接

    //发送ls命令到服务端
    if (send(sockfd, option, sizeof(option), 0) < 0)
    {
        perror("send option error");
        exit(-1);
    }
    // sleep(1);

    //接收服务端目录
    recv(sockfd, &ok, sizeof(ok), 0); //接收服务端的状态
    if (ok == -1)
    {
        puts("open path error");
        close(sockfd);
    }
    else
    {
        //   显示服务器目录内容
        printf("the filelist in server:\n");
        bzero(recv_Buff, MSGLEN);
        while (1)
        {
            recv(sockfd, &sent, sizeof(sent), 0);
            if (sent == 0)
            {
                break;
            }
            recv(sockfd, recv_Buff, sent, 0);
            recv_Buff[sent] = 0;
            puts(recv_Buff);
        }
        close(sockfd);
    }
}

void menu(char *addrname, char *option, char *filename)
{
    if (strcmp(option, "send") == 0)
    {
        file_send(addrname, option, filename);
    }
    else if (strcmp(option, "download") == 0)
    {
        file_dowload(addrname, option, filename);
    }
    else if (strcmp(option, "ls") == 0)
    {
        file_look(addrname, option);
    }
    else
    {
        printf("option error\n");
    }
}

int main(int argc, char **argv)
{
    char addrname[32];
    char option[20];
    char filename[50];

    if (argc == 4)
    {
        strcpy(addrname, argv[1]);
        strcpy(option, argv[2]);
        strcpy(filename, argv[3]);
        menu(addrname, option, filename);
    }
    else if (argc == 3)
    {
        strcpy(addrname, argv[1]);
        strcpy(option, argv[2]);
        menu(addrname, option, NULL);
    }
    else
    {
        printf("Usage: ./client [hostname] [option] [filename] or ./client [hostname] [ls]");
        exit(-1);
    }

    return 0;
}

Server端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>

#define PORT 5168
#define MSGLEN 1024

int main(int argc, char **argv)
{
    int severFd, clientFd; //服务端套接字,客户端套接字
    int fp, flags;         //文件描述符,文件读取标志
    socklen_t addrlen;     //int
    addrlen = sizeof(struct sockaddr);
    struct sockaddr_in severAddr, clientAddr; //socket地址结构
    char recvBuff[MSGLEN];                    //文件接收缓冲区
    char sendBuff[MSGLEN];                    //文件发送缓冲区
    char listBuff[MSGLEN];                    //文件列表发送缓冲区
    char recv_filename[50];                   //文件名接收缓冲区
    char filename[50];                        //文件名
    char option[20];                          //命令接收缓冲区
    char t_message[50] = "file exists";       //文件存在
    char f_message[50] = "file no exists";    //文件不存在
    int recv_len;                             //文件接收长度
    int send_len;                             //文件发送长度
    int list_len;                             //文件列表发送长度

    // 声明并初始化一个服务器端的socket地址结构
    if ((severFd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("sockst error");                         //perror()用来将上一个函数发生错误的原因输出到标准设备 ,错误原因依照全局变量errno 的值来决定要输出的字符串
        exit(-1);
    }
    severAddr.sin_family = AF_INET;                     //地址家族,AF_INET代表TCP/IP
    severAddr.sin_port = htons(PORT);                   //端口,将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian) 参数:16位无符号整数 返回值:TCP / IP网络字节顺序.
    severAddr.sin_addr.s_addr = htonl(INADDR_ANY);      //IP地址,htonl()将一个32位数从主机字节顺序转换成网络字节顺序
    bzero(&severAddr.sin_zero, 8);                      //结构的其余的部分须置 0

    // 绑定sseverFD和socket地址结构
    //参数1:用socket()函数创建的文件描述符
    //参数2:指向一个结构为sockaddr参数的指针
    //参数3:结构的长度
    if (bind(severFd, (struct sockaddr *)&severAddr, sizeof(struct sockaddr)) == -1)
    {
        perror("bind error");
        exit(-1);
    }

    // socket监听
    // 参数1:需要进入监听状态的套接字
    // 参数2:请求队列的最大长度
    // 当请求队列满时,就不再接收新的请求,对于 Linux,客户端会收到 ECONNREFUSED 错误
    if (listen(severFd, 10) == -1)
    {
        perror("listen error");
        exit(-1);
    }

    while (1)
    {
        // 接受连接请求,返回一个新的socket(描述符),这个新socket用于同连接的客户端通信
        // accept函数会把连接到的客户端信息写到client_addr中 ,accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来
        // 参数1:服务器端套接字
        // 参数2:sockaddr_in 结构体变量
        if ((clientFd = accept(severFd, (struct sockaddr *)&clientAddr, &addrlen)) == -1)
        {
            perror("accept error");
            exit(-1);
        }
        printf("connect ip:%s  port: %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));

        //接收客户端命令 
        //bzero() 会将内存块(字符串)的前n个字节清零
        bzero(&option, sizeof(option));
        
        //recv先等待s的发送缓冲区的数据被协议传送完毕,recv函数仅仅是copy数据,真正的接收数据是协议来完成的
        if (recv(clientFd, option, sizeof(option), 0) < 0)
        {
            perror("receive option error");
            break;
        }

        //执行对应操作
        if (strcmp(option, "send") == 0) //客户端想要发文件
        {
            //接收客户端发来的文件名
            bzero(&recv_filename, sizeof(recv_filename));
            if (recv(clientFd, recv_filename, sizeof(recv_filename), 0) < 0)
            {
                perror("receive filename error");
                break;
            }
            strcpy(filename, recv_filename);

            //循环接收数据
            bzero(&recvBuff, MSGLEN);
            fp = open(filename, O_RDWR | O_CREAT, 777); //创建文件
            flags = 0;
            while ((recv_len = recv(clientFd, recvBuff, MSGLEN, 0)) > 0)
            {
                flags++;
                if (flags == 1)
                {
                    printf("receive file start");
                }
                else
                {
                    printf(".");
                }
                if (write(fp, recvBuff, recv_len))
                {
                    bzero(&recvBuff, MSGLEN);
                }
                else
                {
                    perror("write error");
                    break;
                }
            }
            if (flags == 0) //未接收到文件数据
            {
                perror("receive file error");
            }
            if (flags > 0)
            {
                printf("\nreceive file success\n\n");
            }
            close(clientFd);
        }
        else if (strcmp(option, "download") == 0) //客户端想要下载文件
        {
            //接收客户端发来的文件名
            bzero(&recv_filename, sizeof(recv_filename));
            if (recv(clientFd, recv_filename, sizeof(recv_filename), 0) < 0)
            {
                perror("receive filename error");
                break;
            }
            bzero(&filename, sizeof(filename));
            strcpy(filename, recv_filename);
            FILE *sfp;
            sfp = fopen(filename, "rb");
            if (sfp != NULL)
            {
                //send仅仅是把buf中的数据copy到套接字sockfd的发送缓冲区的剩余空间里
                if (send(clientFd, t_message, sizeof(t_message), 0) < 0)
                {
                    perror("send t_message error");
                    exit(1);
                }
                sleep(1);

                printf("sending file");
                bzero(&sendBuff, MSGLEN);
                while (1)
                {
                    if ((send_len = fread(sendBuff, 1, MSGLEN, sfp)) > 0)
                    {
                        if (send(clientFd, sendBuff, send_len, 0) < 0)
                        {
                            perror("send error");
                            exit(-1);
                        }
                        else
                        {
                            printf("...");
                            bzero(&sendBuff, MSGLEN);
                        }
                    }
                    else if (send_len == 0) //等于0表示文件已到末尾
                    {
                        printf("\nfile send success\n\n");
                        break;
                    }
                    else
                    {
                        perror("read error");
                        exit(-1);
                    }
                }
                fclose(sfp);
            }
            else //文件不存在
            {
                printf("no such file\n\n");
                if (send(clientFd, f_message, sizeof(f_message), 0) < 0)    //发送文件不存在的信息
                {
                    perror("send f_message error");
                    exit(-1);
                }
                sleep(1);
            }
            close(clientFd);
            sleep(1);
        }
        else if (strcmp(option, "ls") == 0)
        {
            int ok;   //收发标识
            int sent; //收发字节数
            DIR *dir;
            struct dirent *ptr;
            char data_send[MSGLEN];
            char path[5] = ".";
            if ((dir = opendir(path)) == NULL)
            {
                ok = -1;
                send(clientFd, &ok, sizeof(ok), 0); //打开路径失败则返回ok=-1
                close(clientFd);
            }
            else
            {
                ok = 1;
                send(clientFd, &ok, sizeof(ok), 0); //打开路径成功则返回ok=1

                // 逐行内容读取并发送
                while (1)
                {
                    ptr = readdir(dir);
                    if (ptr == NULL)
                        sent = 0;
                    else
                        sent = strlen(ptr->d_name);
                    send(clientFd, &sent, sizeof(sent), 0);
                    if (sent == 0)
                        break;
                    strcpy(data_send, ptr->d_name);
                    send(clientFd, data_send, sent, 0);
                }
                closedir(dir); //关闭文
                printf("send filelist success\n\n");
                close(clientFd);
            }
        }
    }
    close(severFd);
    return 0;
}

获取文件目录结果
在这里插入图片描述
代码有详细的注释,这里就不多做解释了
有一点就是,这个逻辑是:执行一次命令客户端就结束退出(因为后期跟SpringBoot结合,所以这个逻辑是这样)。如果看懂了代码可以自己修改逻辑,不过我这还有另外一个版本,在客户端给了一个菜单选项,你可以自行决定是否退出或者继续共享文件。放在下载了,感兴趣可以看看。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值