linux下socket文件传输问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux下socket文件传输问题相关的知识,希望对你有一定的参考价值。

单个客户端传文件过来没问题,按顺序接收即可,现在出现了这样一个问题,若多个客户端同时发送文件过来,因为文件不可能一次性send到服务端,所以必须用个while循环依次读取文件内容发送,但在服务端如何判断出接收到的文件属于哪个客户端的,然后正确地把这些数据写到文件里。比如A客户端发送文件A.rar用while读取发送,B也类似A发送B.rar,两个同时发,则服务端完全可能会交叉接收到来自不同客户端发送过来的数据,请问该如何区分整理出A的文件盒B的文件。

如果你的客户端在发送文件时,每次都重新connect,再进行数据传输,则你的程序无法解决数据的区分。
如果客户端是一次connect循环发送,后台服务循环接收,则
(1)如果你的服务端只有一个进程(不支持并发),则A和B不会同时运行,只能按顺序接收完A再接收B
(2)如果,每一个新链接上来,你都建立一个新的进程去工作,则不会有问题。
参考技术A 要下班了,时间急,不写代码了先给你一个思路
1 实现最简单的udp socket 模型,实现发送一个字符串。
2 实现一个简单的打开文件,读取文件的例子,如用fgets(),类似的函数有很多,然后再把读取的文件内容忘另一个文件里写(相关函数fopen(),write(),read())。
3 把上面两个函数结合到一起,在客户端实现打开要传送的文件,按一定的大小读取,读取后调用sendto()发送到服务器端。在服务器端创建一个文件,然后调用recvfrom()接受客户端发送过来的数据,向来是创建的那个文件中写。
下面是改好的udp发送文件的例子。

服务器端程序的编译
gcc -o file_server  file_server
客户端程序的编译
gcc -o file_client  file_client.c
服务器程序和客户端程应当分别运行在2台计算机上.
服务器端程序的运行,在一个计算机的终端执行
./file_server
客户端程序的运行,在另一个计算机的终端中执行
./file_client  运行服务器程序的计算机的IP地址
根据提示输入要传输的服务器上的文件,该文件在服务器的运行目录上
在实际编程和测试中,可以用2个终端代替2个计算机,这样就可以在一台计算机上测试网络程序,
服务器端程序的运行,在一个终端执行
./file_server
客户端程序的运行,在另一个终端中执行
./file_client  127.0.0.1
说明: 任何计算机都可以通过127.0.0.1访问自己. 也可以用计算机的实际IP地址代替127.0.0.1
//////////////////////////////////////////////////////////////////////////////////////
// file_server.c  文件传输顺序服务器示例
//////////////////////////////////////////////////////////////////////////////////////
//本文件是服务器的代码
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for printf
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/
#define HELLO_WORLD_SERVER_PORT    6666
#define LENGTH_OF_LISTEN_QUEUE  20
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

int main(int argc, char **argv)

    //设置一个socket地址结构server_addr,代表服务器internet地址, 端口
    struct sockaddr_in server_addr, pcliaddr;
    bzero(&server_addr,sizeof(server_addr)); //把一段内存区的内容全部设置为0
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htons(INADDR_ANY);
    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);

    //创建用于internet的据报套接字(UDPt,用server_socket代表服务器socket

// 创建数据报套接字(UDP)
    int server_socket = socket(PF_INET,SOCK_DGRAM,0);
    if( server_socket < 0)
   
        printf("Create Socket Failed!");
        exit(1);
   
   
    //把socket和socket地址结构联系起来
    if( bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))
   
        printf("Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
        exit(1);
   
   
    while (1) //服务器端要一直运行
   
        //定义客户端的socket地址结构client_addr
        struct sockaddr_in client_addr;
        socklen_t n = sizeof(client_addr) ;
int length;

        char buffer[BUFFER_SIZE];
        bzero(buffer, BUFFER_SIZE);
        length = recvfrom(new_server_socket,buffer,BUFFER_SIZE,0,&pcliaddr,&n);
        if (length < 0)
       
            printf("Server Recieve Data Failed!\n");
            break;
       
        char file_name[FILE_NAME_MAX_SIZE+1];
        bzero(file_name, FILE_NAME_MAX_SIZE+1);
        strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
//        int fp = open(file_name, O_RDONLY);
//        if( fp < 0 )
        FILE * fp = fopen(file_name,"r");
        if(NULL == fp )
       
            printf("File:\t%s Not Found\n", file_name);
       
        else
       
            bzero(buffer, BUFFER_SIZE);
            int file_block_length = 0;
//            while( (file_block_length = read(fp,buffer,BUFFER_SIZE))>0)
            while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
           
                printf("file_block_length = %d\n",file_block_length);
                //发送buffer中的字符串到new_server_socket,实际是给客户端
                if(send(new_server_socket,buffer,file_block_length,0)<0)
               
                    printf("Send File:\t%s Failed\n", file_name);
                    break;
               
                bzero(buffer, BUFFER_SIZE);
           
//            close(fp);
            fclose(fp);
            printf("File:\t%s Transfer Finished\n",file_name);
       
   


//////////////////////////////////////////////////////////////////////////////////////
// file_client.c  文件传输客户端程序示例
//////////////////////////////////////////////////////////////////////////////////////
//本文件是客户机的代码
#include <netinet/in.h>    // for sockaddr_in
#include <sys/types.h>    // for socket
#include <sys/socket.h>    // for socket
#include <stdio.h>        // for printf
#include <stdlib.h>        // for exit
#include <string.h>        // for bzero
/*
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
*/

#define HELLO_WORLD_SERVER_PORT    6666
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512

int main(int argc, char **argv)

    if (argc != 2)
   
        printf("Usage: ./%s ServerIPAddress\n",argv[0]);
        exit(1);
   

    //设置一个socket地址结构client_addr,代表客户机internet地址, 端口
    struct sockaddr_in client_addr;
    bzero(&client_addr,sizeof(client_addr)); //把一段内存区的内容全部设置为0
    client_addr.sin_family = AF_INET;    //internet协议族
    client_addr.sin_addr.s_addr = htons(INADDR_ANY);//INADDR_ANY表示自动获取本机地址
    client_addr.sin_port = htons(0);    //0表示让系统自动分配一个空闲端口
    //创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket
    int client_socket = socket(AF_INET,SOCK_DGRAM,0);
    if( client_socket < 0)
   
        printf("Create Socket Failed!\n");
        exit(1);
   

    //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
    struct sockaddr_in server_addr;
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    if(inet_aton(argv[1],&server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数
   
        printf("Server IP Address Error!\n");
        exit(1);
   
    server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
    socklen_t server_addr_length = sizeof(server_addr);

    char file_name[FILE_NAME_MAX_SIZE+1];
    bzero(file_name, FILE_NAME_MAX_SIZE+1);
    printf("Please Input File Name On Server:\t");
    scanf("%s", file_name);
   
    char buffer[BUFFER_SIZE];
    bzero(buffer,BUFFER_SIZE);
    strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));
    //向服务器发送buffer中的数据
     socklen_t n = sizeof(server_addr) ;
    sendto(client_socket,buffer,BUFFER_SIZE,0,(struct sockaddr*)&server_addr,n);

//    int fp = open(file_name, O_WRONLY|O_CREAT);
//    if( fp < 0 )
    FILE * fp = fopen(file_name,"w");
    if(NULL == fp )
   
        printf("File:\t%s Can Not Open To Write\n", file_name);
        exit(1);
   
   
    //从服务器接收数据到buffer中
    bzero(buffer,BUFFER_SIZE);
    int length = 0;
    while( length = recv(client_socket,buffer,BUFFER_SIZE,0))
   
        if(length < 0)
       
            printf("Recieve Data From Server %s Failed!\n", argv[1]);
            break;
       
//        int write_length = write(fp, buffer,length);
        int write_length = fwrite(buffer,sizeof(char),length,fp);
        if (write_length<length)
       
            printf("File:\t%s Write Failed\n", file_name);
            break;
       
        bzero(buffer,BUFFER_SIZE);   
   
    printf("Recieve File:\t %s From Server[%s] Finished\n",file_name, argv[1]);
    return 0;

请采纳。
参考技术B 对每个客户端请求,服务端守护进程fork子进程

Linux下搭建简易的HTTP服务器完成图片显示

1. 前言

这篇文章作为Linux下socket(TCP)网络编程的练习,使用C语言代码搭建一个简单的HTTP服务器,完成与浏览器之间的交互,最终在浏览器上显示一张图片;通过这个例子可以巩固socket里多线程使用,也可以方便学习了解HTTP协议。

2. HTTP协议介绍

HTTP协议本身是基于TCP通信协议来传递数据(HTML 文件, 图片文件-也叫超文本传输协议),HTTP协议必须工作在客户端-服务端架构上(本身底层就是TCP),HTTP 默认端口号为 80(浏览器访问默认就是80端口),但是你也可以改为 8080 或者其他端口(可以手动指定端口)。

HTTP协议是无连接的,也就是限制每次连接只处理一个请求;服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

3. HTTP的消息结构

客户端向HTTP服务器发送的请求消息格式包括了4个部分:
请求行(request line)、 请求头部(header)、空行、请求数据

下面这个是浏览器的请求,可以对比上面这张图的格式:

GET / HTTP/1.1
Host: 10.0.0.6
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

HTTP常用的请求是GETPOST

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。
HTTP1.1 新增了五种请求方法: OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。

HTTP服务器向客户端的响应也由四个部分组成,分别是:状态行、消息报头、空行、响应正文。

例如:

"HTTP/1.1 200 OK\\r\\n"
"Content-type:image/jpeg\\r\\n"
"Content-Length:1234\\r\\n"
"\\r\\n"
"...............正文............."

上面列出的报文字段含义:
HTTP/1.0 200 OK: Http/1.0 表示当前协议为 Http。 1.0 是协议的版本。 200 表示成功

Content-type : 告诉浏览器回送的数据类型

Content-Length: 告诉浏览器报文中实体主体的大小,也就是返回的内容长度

上面字段里回复的状态码一般有好几种,分别是:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它 URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误

4. HTTP交互流程

第一次请求是由HTTP客户端(浏览器)发起的,HTTP服务器收到请求后,对请求进行解析,然后完成后续的交互。

如果要在浏览器上显示一张图片,那么交互的流程大致如下:

要让浏览器在界面显示一张图片,还得编写一个HTML代码给浏览器,直接用一个图片标签即可。

当前程序使用的HTML代码比较简单,代码下面贴出来了:

<! DOCTYPE HTML>
<html>
    <head>
        <title>jpg</title>
        <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    </head>

    <body>
        <center><img src="www/123.jpg"   />
        </center>
    </body>
</html>

然后还得准备一张JPG图片,作为资源文件,方便传递给浏览器,本地文件结构如下:

5. 案例代码: 搭建HTTP服务器

下面代码采用多线程形式响应浏览器的请求。

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

/*
函数功能: 服务器向客户端发送响应数据
*/
int HTTP_ServerSendFile(int client_fd,char *buff,char *type,char *file)

    /*1. 打开文件*/
    int fd=open(file,2);
    if(fd<0)return -1;
    /*2. 获取文件大小*/
    struct stat s_buff;
    fstat(fd,&s_buff);
    /*3. 构建响应头部*/
    sprintf(buff,"HTTP/1.1 200 OK\\r\\n"
                "Content-type:%s\\r\\n"
                "Content-Length:%d\\r\\n"
                "\\r\\n",type,s_buff.st_size);
    /*4. 发送响应头*/
    if(write(client_fd,buff,strlen(buff))!=strlen(buff))return -2;
    /*5. 发送消息正文*/
    int cnt;
    while(1)
    
        cnt=read(fd,buff,1024);
        if(write(client_fd,buff,cnt)!=cnt)return -3;
        if(cnt!=1024)break;
    
    return 0;


/*线程工作函数*/
void *thread_work_func(void *argv)

    int client_fd=*(int*)argv;
    free(argv);

    unsigned int cnt;
    unsigned char buff[1024];
    //读取浏览器发送过来的数据
    cnt=read(client_fd,buff,1024);
    buff[cnt]=\\0;
    printf("%s\\n",buff);

    if(strstr(buff,"GET / HTTP/1.1"))
    
        HTTP_ServerSendFile(client_fd,buff,"text/html","www/image_text.html");
    
    else if(strstr(buff,"GET /www/123.jpg HTTP/1.1"))
    
        HTTP_ServerSendFile(client_fd,buff,"image/jpeg","www/888.jpg");
    
    else if(strstr(buff,"GET /favicon.ico HTTP/1.1"))
    
        HTTP_ServerSendFile(client_fd,buff,"image/x-icon","www/1.ico");
    

    close(client_fd);
    //退出线程
    pthread_exit(NULL);


int main(int argc,char **argv)
   
    if(argc!=2)
    
        printf("./app <端口号>\\n");
        return 0;
    

    signal(SIGPIPE,SIG_IGN); //忽略 SIGPIPE 信号--防止服务器异常退出

    int sockfd;
    /*1. 创建socket套接字*/
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    int on = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    /*2. 绑定端口号与IP地址*/
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(atoi(argv[1])); // 端口号0~65535
    addr.sin_addr.s_addr=INADDR_ANY;    //inet_addr("0.0.0.0"); //IP地址
    if(bind(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr))!=0)
    
        printf("服务器:端口号绑定失败.\\n");
    
    /*3. 设置监听的数量,表示服务器同一时间最大能够处理的连接数量*/
    listen(sockfd,20);

    /*4. 等待客户端连接*/
    int *client_fd;
    struct sockaddr_in client_addr;
    socklen_t addrlen;
    pthread_t thread_id;
    while(1)
    
        addrlen=sizeof(struct sockaddr_in);
        client_fd=malloc(sizeof(int));
        *client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&addrlen);
        if(*client_fd<0)
        
            printf("客户端连接失败.\\n");
            return 0;
        
        printf("连接的客户端IP地址:%s\\n",inet_ntoa(client_addr.sin_addr));
        printf("连接的客户端端口号:%d\\n",ntohs(client_addr.sin_port));

        /*创建线程*/
        if(pthread_create(&thread_id,NULL,thread_work_func,client_fd))
        
            printf("线程创建失败.\\n");
            break;
        
        /*设置线程的分离属性*/
        pthread_detach(thread_id);
     
    /*5. 关闭连接*/
    close(sockfd);
    return 0;

6. 最终运行的效果

以上是关于linux下socket文件传输问题的主要内容,如果未能解决你的问题,请参考以下文章

Linux网络编程基础-socket

java socket多文件传输问题

python之socket运用之传输大文件

linux下使用rsync同步

Java学习笔记——Socket实现文件传输

Linux 串口传输文件到Window系统去