分佈式文件系統 fastdfs 源碼分析 之 文件上傳流程分析

fastdfs是一個輕量級的分佈式文件系統,主要由 tracker server, storage server 以及client組成,這裏主要涉及兩點 :
1)客戶端上傳文件流程和協議分析
2)實現一個簡單的文件上傳函數

一: 文件上傳的基本流程

這裏寫圖片描述

fastdfs中上傳一個文件,主要涉及以下幾個步驟:

1)上傳連接請求,客戶端會向tracker server發出上傳文件的請求
2)tracker收到請求後,返回storage server的ip和端口
3)客戶端連接storage,並且上傳文件
4)文件上傳完成後,storage返回路徑信息

以下具體分析文件上傳過程中的協議和各種操作

fastdfs協議頭部:

typedef struct
    {
        char pkg_len[FDFS_PROTO_PKG_LEN_SIZE];  //body length, not including header(8個字節)
        char cmd;    //command code     TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE
        char status; //status code for response
    } TrackerHeader;

fastdfs協議的頭部是由10個字節大小的結構體構成,
發送:發送數據時,先發送TrackerHeader到服務器,隨後發送具體的數據
接受:接受數據時,先接受sizeof(TrackerHeader)大小的報文頭部,隨後接受pkg_len長度的報文體

status: 發送的時候設置爲0
cmd: 命令
pkg_len:一個int64_t的整型,除去TrackerHeader長度的報文長度

二: 客戶端向tracker server發送獲取storage地址請求
#define TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE 101

//            協議頭
//            pkg_len | cmd     | status
//            8 bytes | 1 bytes | 1 bytes
//向tracker server請求storage server cmd
#define TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE     101

    TrackerHeader header;//協議頭部
    memset(&header, 0, sizeof(TrackerHeader));
    header.cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;

    //向tracker server 請求 storage,tcpsenddata 返回非0,表示發送成功
    if(tcpsenddata(sockfd, &header, sizeof(TrackerHeader), 10, &count) != 0)
    {
        fprintf(stderr, "tcpsenddata error: %s\n", strerror(errno));
        return 1;
    }
    else//請求發送成功,等待tracker回覆
    {
        //接收頭部,頭部是一個TrackerHeader類型,10個字節
        TrackerHeader resp;
        if((ret_code = tcprecvdata(sockfd, &resp, sizeof(TrackerHeader), 10, &count)) != 0) 
        {
            fprintf(stderr, "tcprecvdata error: %s\n", strerror(ret_code));
            return 1;
        }
        //開始接收報文體
        //int64_t read_int64(const char *buff)
        //{
        //      unsigned char *p;
        //      p = (unsigned char *)buff;
        //      return  (((int64_t)(*p)) << 56) | \
        //      (((int64_t)(*(p+1))) << 48) |  \
        //      (((int64_t)(*(p+2))) << 40) |  \
        //      (((int64_t)(*(p+3))) << 32) |  \
        //      (((int64_t)(*(p+4))) << 24) |  \
        //      (((int64_t)(*(p+5))) << 16) |  \
        //      (((int64_t)(*(p+6))) << 8) | \
        //      ((int64_t)(*(p+7)));
        //}
        int size = read_int64(resp.pkg_len);//獲取報體長度
        char *buf = (char*)calloc(size + 1, sizeof(char));

        if((ret_code = tcprecvdata(sockfd, buf, size, 10, &count) != 0)) 
        {
            fprintf(stderr, "tcprecvdata error: %s\n", strerror(ret_code));
            return 1;
        }

        //  報文體
        //  group_name  |ip         |port       |storage_index
        //  16 bytes    |16 bytes   |8 bytes    |
        //#define TRACKER_QUERY_STORAGE_STORE_BODY_LEN 40
        if(count != TRACKER_QUERY_STORAGE_STORE_BODY_LEN)
        {
            fprintf(stderr, "invalid message");
            return 1;
        }
        //group name    
        //#define FDFS_GROUP_NAME_MAX_LEN  16
        char group_name[FDFS_GROUP_NAME_MAX_LEN + 1]  = {0};
        memcpy(group_name, buf, FDFS_GROUP_NAME_MAX_LEN);
        group_name[FDFS_GROUP_NAME_MAX_LEN] = '\0';
        //ip: port
        //#define IP_ADDRESS_SIZE 16 
        //port:8 bytes
        char ip[IP_ADDRESS_SIZE + 1] = {0};
        memcpy(ip, buf + FDFS_GROUP_NAME_MAX_LEN, IP_ADDRESS_SIZE - 1);
        char szPort[8] = {0};
        memcpy(szPort, buf + FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1, 8);
        ip[IP_ADDRESS_SIZE] = '\0';
        int port = read_int64(szPort);
        //storage index;
        char *storage_index = buf + FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE;

三:以上步驟完成後,獲取storage的ip 和 port後,就可以上傳文件了

在官方的客戶端中,文件操作有upload,download, append,delete等,這裏只涉及upload
上傳文件中,官方給出了三種方式
1)通過buffer上傳,即將文件讀取進內存,然後在發送
2)使用sendfile,sendfile是Linux提供的一個庫函數
3)通過回調函數的方式

這裏主要涉及的是第一種,通過buffer上傳的方式

文件上傳協議:

//文件上傳協議頭部
    10 bytes        | 1 bytes        | 8 bytes    | 6 bytes        |
    TrackerHeader   | storage_index  | 文件長度    | 文件名或者全爲0)  |
//storage_index 是客戶端向tracker server申請storage index時候返回的結果
//文件名 如果不爲空,那麼取前6位,或者可以全部設置爲0



//上傳完成 storage回覆客戶端協議
10 bytes      | 16 bytes    | TrackerHeader.pkg_len - 16bytes 
TrackerHeader | groupname   | remote file name
void uploadfile(int sockfd, const char *filepath, char *storage_index) 
{
    char out_buf[512];
    TrackerHeader *pHeader;
    char *p = out_buf;
    char *buf = NULL;

    //TrackerHeader 10 bytes
    //文件上傳協議頭部
    //10 bytes        | 1 bytes        | 8 bytes    | 6 bytes        |
    //TrackerHeader   | storage_index  | 文件長度    | 文件名或者全爲0)  |
    pHeader = (TrackerHeader*)out_buf;
    p += sizeof(TrackerHeader);

    //storage index  1 bytes
    *p++ = *storage_index;
    //filesize 8bytes
    long int filesize = 0;
    int ret = 0;
    //讀取文件到buf,並且返回文件長度 filesize
    if((ret = getfilebuf(&buf, &filesize, filepath) != 0))
    {
        fprintf(stderr, "getfilebuf failed: %s\n", strerror(ret));
        return;
    }
    //void write_int64(int64_t n, char *buff)
    //{
    //  unsigned char *p;
    //  p = (unsigned char *)buff;
    //  *p++ = (n >> 56) & 0xFF;
    //  *p++ = (n >> 48) & 0xFF;
    //  *p++ = (n >> 40) & 0xFF;
    //  *p++ = (n >> 32) & 0xFF;
    //  *p++ = (n >> 24) & 0xFF;
    //  *p++ = (n >> 16) & 0xFF;
    //  *p++ = (n >> 8) & 0xFF;
    //  *p++ = n & 0xFF;
    //}
    write_int64(filesize, p);
    //#define FDFS_PROTO_PKG_LEN_SIZE 8
    p += FDFS_PROTO_PKG_LEN_SIZE;

    //ext_name
    //#define FDFS_FILE_EXT_NAME_MAX_LEN 6
    memset(p, 0, FDFS_FILE_EXT_NAME_MAX_LEN);
    p += FDFS_FILE_EXT_NAME_MAX_LEN;

    //set TrackerHeader
    write_int64(p - out_buf + filesize - sizeof(TrackerHeader), pHeader->pkg_len);
    //#define STORAGE_PROTO_CMD_UPLOAD_FILE 11
    pHeader->cmd  = STORAGE_PROTO_CMD_UPLOAD_FILE;
    pHeader->status = 0;

    //發送報文頭部
    int count;
    int ret_code = 0;
    if((ret_code = tcpsenddata(sockfd, out_buf, p - out_buf, 10, &count) != 0)) {
        fprintf(stderr, "tcpsenddata failed: %s\n", strerror(errno));
        return;
    }
    //發送報文體,具體文件數據
    if((ret_code = tcpsenddata(sockfd, buf, filesize, 10, &count)) != 0) {
        fprintf(stderr, "tcpsenddata body failed: %s\n", strerror(errno));
        return;
    }

    //接收storage server回覆
    //上傳完成 storage回覆客戶端協議
    //10 bytes      | 16 bytes    | TrackerHeader.pkg_len - 16bytes 
    //TrackerHeader | groupname   | remote file name
    TrackerHeader resp;
    if((ret_code = tcprecvdata(sockfd, &resp, sizeof(TrackerHeader), 1000, &count)) != 0) {
        fprintf(stderr, "tcprecvdata failed: %s\n", strerror(ret_code));
        return;
    }   
    if(count != sizeof(TrackerHeader)) {
        fprintf(stderr, "invalid header");
        return;
    }

    int64_t bodylen = read_int64(resp.pkg_len);
    //接收報文體
    char *in_buf = (char*)calloc(bodylen + 1, sizeof(char));
    if((ret_code = tcprecvdata(sockfd, in_buf, bodylen, 10, &count)) != 0) 
    {
        fprintf(stderr, "read body failed: %s\n", strerror(ret_code));
        return;
    }

    //groupname
    //#define FDFS_GROUP_NAME_MAX_LEN  16
    char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
    memcpy(group_name, in_buf, FDFS_GROUP_NAME_MAX_LEN);
    group_name[FDFS_GROUP_NAME_MAX_LEN] = '\0';

    //remote filename
    char remote_filename[bodylen - FDFS_GROUP_NAME_MAX_LEN + 1];
    memcpy(remote_filename, in_buf + FDFS_GROUP_NAME_MAX_LEN, bodylen - FDFS_GROUP_NAME_MAX_LEN + 1);

    cout << "groupname: " << group_name << endl;
    cout << "remote_filename: " << remote_filename << endl;

    char httpaddr[128] = {0};
    sprintf(httpaddr, "http://106.75.129.177:8080/%s/%s", group_name, remote_filename);
    cout << "httpaddr: " <<  httpaddr << endl;//http地址
}

以下附上完整代碼, ubuntu14位, 編譯器 g++,測試已通過

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using namespace std;
#define FDFS_GROUP_NAME_MAX_LEN     16
#define FDFS_PROTO_PKG_LEN_SIZE     8
#define IP_ADDRESS_SIZE         16
//cmd
#define TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE     101
#define STORAGE_PROTO_CMD_UPLOAD_FILE       11

#define TRACKER_QUERY_STORAGE_STORE_BODY_LEN    (FDFS_GROUP_NAME_MAX_LEN \
    + IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE + 1)
#define FDFS_FILE_EXT_NAME_MAX_LEN 6
typedef struct {
    char pkg_len[FDFS_PROTO_PKG_LEN_SIZE];
    char cmd;
    char status;
}TrackerHeader;

//set socketfd nonblocking
int setnonblocking(int sockfd);
int tcprecvdata(int sockfd, void *data, const int size,\
    const int timeout_ms, int *count);
int tcpsenddata(int sockfd, void *data, const int size,\
const int timeout_ms, int *count);

int64_t read_int64(const char* buf);
void write_int64(int64_t n, char* buf);

void uploadfile(int sockfd, const char *filepath, char *storage_index);
int getfilebuf(char **buf, long int *filesize, const char* filepath);
//apply storage address from tracker server
int main() {
    const char *ip = "127.0.0.1";
    uint16_t port = 22122;
    int ret_code = 0;
    int sockfd = -1;
    int count = 0;
    //connect tracker server
    if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) 
    {
        fprintf(stderr, "socket errnor: %s\n", strerror(errno));
        return 1;
    }
    if((ret_code = setnonblocking(sockfd)) != 0) 
    {
        fprintf(stderr, "setnonblocking error: %s\n", strerror(ret_code));
        return 1;
    }
    struct sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr(ip);
    addr.sin_port = htons(port);
    addr.sin_family = AF_INET;

    socklen_t len = sizeof(struct sockaddr);
    if(connect(sockfd, (struct sockaddr*)&addr, len) < 0) 
    {
        fprintf(stderr, "connect error: %s\n", strerror(errno));
        return 1;
    }

    TrackerHeader header;
    memset(&header, 0, sizeof(TrackerHeader));
    header.cmd = TRACKER_PROTO_CMD_SERVICE_QUERY_STORE_WITHOUT_GROUP_ONE;

    if(tcpsenddata(sockfd, &header, sizeof(TrackerHeader), 10, &count) != 0)
    {
        fprintf(stderr, "tcpsenddata error: %s\n", strerror(errno));
        return 1;
    }
    else
    {
        //recv header
        TrackerHeader resp;
        if((ret_code = tcprecvdata(sockfd, &resp, sizeof(TrackerHeader), 10, &count)) != 0) 
        {
            fprintf(stderr, "tcprecvdata error: %s\n", strerror(ret_code));
            return 1;
        }

        cout << "recv header: " << count << endl;

        //read body;
        int size = read_int64(resp.pkg_len);
        char *buf = (char*)calloc(size + 1, sizeof(char));

        if((ret_code = tcprecvdata(sockfd, buf, size, 10, &count) != 0)) 
        {
            fprintf(stderr, "tcprecvdata error: %s\n", strerror(ret_code));
            return 1;
        }

        //body
        //  group_name  |ip     |port       |storage_index
        //  16bytes     |16bytes    |8bytes     |
        cout << "read body: " << count << endl;
        if(count != TRACKER_QUERY_STORAGE_STORE_BODY_LEN)
        {
            fprintf(stderr, "invalid message");
            return 1;
        }
        //group name    
        char group_name[FDFS_GROUP_NAME_MAX_LEN + 1]  = {0};
        memcpy(group_name, buf, FDFS_GROUP_NAME_MAX_LEN);
        group_name[FDFS_GROUP_NAME_MAX_LEN] = '\0';
        cout << "group name: " << group_name << endl;
        //ip: port
        char ip[IP_ADDRESS_SIZE + 1] = {0};
        memcpy(ip, buf + FDFS_GROUP_NAME_MAX_LEN, IP_ADDRESS_SIZE - 1);
        char szPort[8] = {0};
        memcpy(szPort, buf + FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1, 8);
        ip[IP_ADDRESS_SIZE] = '\0';
        int port = read_int64(szPort);
        cout << "address: " << ip << ":" << port << endl;
        //storage index;
        char *storage_index = buf + FDFS_GROUP_NAME_MAX_LEN + IP_ADDRESS_SIZE - 1 + FDFS_PROTO_PKG_LEN_SIZE;
        cout << "storage_index: " << storage_index << endl;
        free(buf);

        //connect storage server
        sockaddr_in st_addr;
        st_addr.sin_addr.s_addr = inet_addr(ip);
        st_addr.sin_family = AF_INET;
        st_addr.sin_port = htons(port);

        int storage_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(storage_fd < 0) {
            fprintf(stderr, "socket failed: %s\n", strerror(errno));
            return 1;
        }
        socklen_t len2 = sizeof(sockaddr_in);
        if(connect(storage_fd, (struct sockaddr*)&st_addr, len2) < 0) {
            fprintf(stderr, "connect failed: %s\n", strerror(errno));
            return 1;
        }

        uploadfile(storage_fd, "1.jpg", storage_index);
    }
    return 0;
}

int getfilebuf(char **buf, long int *filesize, const char *filepath) 
{
    int ret_code = 0;
    FILE *fp = fopen(filepath, "rb+");
    if(fp == NULL)
    {
        ret_code = errno;
        return ret_code;
    }
    //get filesize;
    fseek(fp, 0, SEEK_END);
    *filesize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    cout << "get filesize: " <<*filesize << endl;
    //malloc buf
    *buf = (char*)calloc(*filesize + 1, sizeof(char));
    if(*buf == NULL) {
        ret_code = errno;
        return ret_code;
    }

    int read_bytes = 0;
    int left_bytes = *filesize;
    char *p = *buf;
    while(left_bytes > 0) {
        read_bytes = fread(p, sizeof(char), left_bytes, fp);
        left_bytes -= read_bytes;
        p += read_bytes;
    }   
    return ret_code;
}
void uploadfile(int sockfd, const char *filepath, char *storage_index) 
{
    char out_buf[512];
    TrackerHeader *pHeader;
    char *p = out_buf;
    char *buf = NULL;

    //TrackerHeader 10 bytes
    pHeader = (TrackerHeader*)out_buf;
    p += sizeof(TrackerHeader);

    //storage index  1 bytes
    *p++ = *storage_index;
    //filesize 8bytes
    long int filesize = 0;
    int ret = 0;
    if((ret = getfilebuf(&buf, &filesize, filepath) != 0))
    {
        fprintf(stderr, "getfilebuf failed: %s\n", strerror(ret));
        return;
    }
    printf("filesize: %ld\n", filesize);
    write_int64(filesize, p);
    p += FDFS_PROTO_PKG_LEN_SIZE;

    //ext_name
    memset(p, 0, FDFS_FILE_EXT_NAME_MAX_LEN);
    p += FDFS_FILE_EXT_NAME_MAX_LEN;

    //set TrackerHeader
    write_int64(p - out_buf + filesize - sizeof(TrackerHeader), pHeader->pkg_len);
    pHeader->cmd  = STORAGE_PROTO_CMD_UPLOAD_FILE;
    pHeader->status = 0;

    //send header
    int count;
    int ret_code = 0;
    if((ret_code = tcpsenddata(sockfd, out_buf, p - out_buf, 10, &count) != 0)) {
        fprintf(stderr, "tcpsenddata failed: %s\n", strerror(errno));
        return;
    }
    //send body
    if((ret_code = tcpsenddata(sockfd, buf, filesize, 10, &count)) != 0) {
        fprintf(stderr, "tcpsenddata body failed: %s\n", strerror(errno));
        return;
    }

    //recv response
    TrackerHeader resp;
    if((ret_code = tcprecvdata(sockfd, &resp, sizeof(TrackerHeader), 1000, &count)) != 0) {
        fprintf(stderr, "tcprecvdata failed: %s\n", strerror(ret_code));
        return;
    }   
    if(count != sizeof(TrackerHeader)) {
        fprintf(stderr, "invalid header");
        return;
    }

    int64_t bodylen = read_int64(resp.pkg_len);

    char *in_buf = (char*)calloc(bodylen + 1, sizeof(char));
    if((ret_code = tcprecvdata(sockfd, in_buf, bodylen, 10, &count)) != 0) 
    {
        fprintf(stderr, "read body failed: %s\n", strerror(ret_code));
        return;
    }

    //groupname
    char group_name[FDFS_GROUP_NAME_MAX_LEN + 1];
    memcpy(group_name, in_buf, FDFS_GROUP_NAME_MAX_LEN);
    group_name[FDFS_GROUP_NAME_MAX_LEN] = '\0';

    //remote filename
    char remote_filename[bodylen - FDFS_GROUP_NAME_MAX_LEN + 1];
    memcpy(remote_filename, in_buf + FDFS_GROUP_NAME_MAX_LEN, bodylen - FDFS_GROUP_NAME_MAX_LEN + 1);

    cout << "groupname: " << group_name << endl;
    cout << "remote_filename: " << remote_filename << endl;

    char httpaddr[128] = {0};
    sprintf(httpaddr, "http://127.0.0.1:8080/%s/%s", group_name, remote_filename);
    cout << "httpaddr" <<  httpaddr << endl;
}

void write_int64(int64_t n, char *buff)
{
    unsigned char *p;
    p = (unsigned char *)buff;
    *p++ = (n >> 56) & 0xFF;
    *p++ = (n >> 48) & 0xFF;
    *p++ = (n >> 40) & 0xFF;
    *p++ = (n >> 32) & 0xFF;
    *p++ = (n >> 24) & 0xFF;
    *p++ = (n >> 16) & 0xFF;
    *p++ = (n >> 8) & 0xFF;
    *p++ = n & 0xFF;
}
int64_t read_int64(const char *buff)
{
    unsigned char *p;
    p = (unsigned char *)buff;
    return  (((int64_t)(*p)) << 56) | \
        (((int64_t)(*(p+1))) << 48) |  \
        (((int64_t)(*(p+2))) << 40) |  \
        (((int64_t)(*(p+3))) << 32) |  \
        (((int64_t)(*(p+4))) << 24) |  \
        (((int64_t)(*(p+5))) << 16) |  \
        (((int64_t)(*(p+6))) << 8) | \
        ((int64_t)(*(p+7)));
}

int setnonblocking(int sockfd)
{
    int ret_code = 0;
    if(fcntl(sockfd, F_SETFD, O_NONBLOCK) < 0) {
        ret_code = errno;
    }
    return ret_code;
}
int tcpsenddata(int sockfd, void *data, const int size,\
const int timeout_ms, int *count) 
{
    int left_bytes = size;
    int write_bytes = 0;
    int ret_code = 0;
    int res = 0;
    char *p = (char*)data;

    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(sockfd, &rfds);
    while(left_bytes > 0) {
        write_bytes = send(sockfd, p, left_bytes, 0);
        if(write_bytes > 0) 
        {
            left_bytes -= write_bytes;
            p += write_bytes;
            continue;
        }
        else if(write_bytes < 0) 
        {
            if(!(errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
            {
                ret_code = errno == 0 ? errno : EINTR;
                break;
            }

        }
        else
        {
            ret_code = ENOTCONN;
            break;
        }

        if(timeout_ms <= 0) 
        {
            res = select(sockfd + 1, &rfds, NULL, NULL, NULL); 
        }
        else 
        {
            struct timeval tv;
            tv.tv_usec = timeout_ms;
            tv.tv_sec = 0;
            res = select(sockfd + 1, &rfds, NULL, NULL, &tv);
        }
        if(res == 0)
        {
            ret_code = ETIMEDOUT;
            break;
        }
        if(res < 0) 
        {
            if(errno == EINTR)
            {
                continue;
            }
            ret_code = errno == 0 ? errno : EINTR;
        }
    }
    if(count != NULL)
    {
        *count = size - left_bytes;
    }
    return ret_code;
}

int tcprecvdata(int sockfd, void *data, const int size,\
    const int timeout_ms, int *count) {

    int left_bytes = size;
    int read_bytes = 0;
    int ret_code = 0;
    int res = 0;
    char *p = (char*)data;

    fd_set rfds;
    FD_ZERO(&rfds);
    FD_SET(sockfd, &rfds);

    while(left_bytes > 0) {
        read_bytes = recv(sockfd, p, left_bytes, 0);
        if(read_bytes > 0) 
        {
            left_bytes -= read_bytes;
            p += read_bytes;
            continue;
        } 
        else if(read_bytes < 0) 
        {
            if(!(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) 
            {
                ret_code = errno != 0 ? errno : EINTR;
                break;  
            }
        }
        else 
        {
            ret_code = ENOTCONN;
            break;
        }

        if(timeout_ms <= 0) 
        {
            res = select(sockfd + 1, &rfds, NULL, NULL, NULL);
        }   
        else 
        {
            struct timeval tv;
            tv.tv_usec = timeout_ms;
            tv.tv_sec  = 0;
            res = select(sockfd + 1, &rfds, NULL, NULL, &tv);
        }

        if(res == 0) 
        {
            ret_code = ETIMEDOUT;
            break;
        }
        if(res < 0)
        {
            if(errno == EINTR)
            {
                continue;
            }
            ret_code = errno == 0 ? errno : EINTR;
            break;          
        }
    }

    if(count != NULL)
    {
        *count = size - left_bytes;
    }
    return ret_code;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章