首页 文章

c socket发送大文件发生破坏管道错误

提问于
浏览
1

我正在尝试使用C套接字发送文件 .

我用pthread创建了一个服务器 . 服务器按缓冲区的大小读取文件,并将其发送到客户端,读取大小 .

它适用于小尺寸文件,但是当我尝试发送大文件时,如mp3文件(超过5MB),它不能正常工作 . 客户端再次发送请求,管道坏了 .

我的服务器在OSX上运行,我使用浏览器作为客户端 .

当html文件有mp3资源作为标记时,mp3发送OK . (我称之为localhost:9999 / index.html)但是当我直接调用mp3文件时(例如localhost:9999 / music.mp3),会发生破坏管道错误 . (localhost:9999 / image.jpeg没问题)

我添加了忽略SIGPIPE,但仍然发生了损坏的管道错误 .

我无法理解 . 有什么问题,我该如何解决?

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>
#include <fcntl.h>
#include <signal.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_t pthread;
void error(char *msg)
{
    perror(msg);
    exit(1);
}

void *pthread_read_and_write(void *arg);
int writeToClient(int newsockfd, char* msg);
void sendError(int newsockfd);
void sendResponseHeader(int newsockfd, char *httpMsg, long contentLen, char *contentType);
void requestHandler(int newsockfd, char *reqMsg);
int main(int argc, char *argv[])
{
    signal(SIGPIPE, SIG_IGN);
    int sockfd, newsockfd;
    int portno;
    socklen_t clilen;
    struct sockaddr_in serv_addr, cli_addr;

    int n;
    if (argc < 2) {
        fprintf(stderr,"ERROR, no port provided\n");
        exit(1);
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        error("ERROR opening socket");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = atoi(argv[1]);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
        error("ERROR on binding");

    listen(sockfd,10);

    clilen = sizeof(cli_addr);

    while(1){
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0)
            error("ERROR on accept");
        pthread_create(&pthread, NULL, *pthread_read_and_write, (void *)(intptr_t)newsockfd);
        pthread_detach(pthread);
    }
    close(sockfd);

    return 0;
}

void *pthread_read_and_write(void *arg){
    int newsockfd = (int)arg;
    char reqMsg[500];
    int n;

    bzero(reqMsg,500);
    n = read(newsockfd,reqMsg,499);
    if (n < 0) error("ERROR reading from socket");
    printf("========Request Message======\n%s\n",reqMsg);
    requestHandler(newsockfd, reqMsg);
    printf("=============================\n");

    return NULL;
}
void requestHandler(int newsockfd, char *reqMsg){

    printf("client socket : %d\n", newsockfd);

    char file[100];
    char *method = strtok(reqMsg, " /");
    strcpy(file, strtok(NULL, " /"));
    char tmpFileName[100];
    strcpy(tmpFileName, file);
    strtok(tmpFileName, ".");
    char *extension = strtok(NULL, ".");
    printf("method : %s\n", method);
    printf("file : %s\n", file);
    printf("extension : %s\n", extension);
    if(strcmp(method, "GET") == 0 || strcmp(method, "get") == 0){

    }else{
        sendError(newsockfd);
        return;
    }
    if(strcmp(file, "HTTP") == 0 || strcmp(file, "http") == 0){
        strcpy(file , "index.html");
    }
    printf("compare success\n");
    long fsize;
    char type[20];

    if(extension == NULL || strcmp(extension, "html") == 0){
        strcpy(type, "text/html");
    }else if(strcmp(extension, "jpeg") == 0){
        strcpy(type,"image/jpeg");
    }else if(strcmp(extension, "gif") == 0){
        strcpy(type,"image/gif");
    }else if(strcmp(extension, "mp3") == 0){
        strcpy(type, "audio/mpeg");
    }else if(strcmp(extension, "pdf") == 0){
        strcpy(type, "application/pdf");
    }else{
        strcpy(type, "text/plain");
    }
    printf("compare success\n");
    printf("type : %s\n", type);
    FILE *fp = fopen(file, "rb");
    if(fp == NULL){
        sendError(newsockfd);
        return;
    }
    fseek(fp, 0, SEEK_END);
    fsize = ftell(fp);
    fclose(fp);

    char rcvBuf[BUFSIZ+1];
    int fd;
    printf("reading file...\n");
    if((fd = open(file, O_RDONLY)) <0 ){
        printf("sending error...\n");
        sendError(newsockfd);
        printf("send error OK\n");
        return;
    }
    printf("open fd OK\n");

    char *httpMsgOK = "200 OK";
    printf("sending header...\n");
    pthread_mutex_lock(&mutex);
    sendResponseHeader(newsockfd, httpMsgOK, fsize, type);
    printf("send header OK\n");

    pthread_mutex_unlock(&mutex);
    int n;
    bzero(rcvBuf, BUFSIZ + 1);
    if(fd >= 0) {
        while((n=read(fd, rcvBuf, BUFSIZ)) > 0){

            printf("sending rcvBuf : %d, remain : %ld\n", n, fsize-=n);
            int res = send(newsockfd, rcvBuf, n + 1, 0);
            if(res <0) {
                char errMsg[100];
                sprintf(errMsg, "ERROR writing to socket __sock : %d __", newsockfd);
                error(errMsg);
            }
            bzero(rcvBuf, BUFSIZ + 1);
        }
    }
    close(newsockfd);
    printf("closed client socket\n");

}
void sendError(int newsockfd){
    char *msg = "<html><body><h1>400 Bad Request</h1></body></html>";
    sendResponseHeader(newsockfd, "400 Bad Request", strlen(msg), "text/html");
    writeToClient(newsockfd, msg);
    close(newsockfd);
    printf("closed client socket\n");
}

void sendResponseHeader(int newsockfd, char *httpMsg, long contentLen, char *contentType){
    char resMsg[40];
    char conLen[100];
    char conType[50];
    sprintf(resMsg, "HTTP/1.1 %s\r\n",httpMsg);
    sprintf(conLen, "Content-length: %ld\r\n", contentLen);
    sprintf(conType, "Content-Type: %s\r\n\r\n", contentType);

    printf("response message\n%s\n%s\n%s\n", resMsg, conLen, conType);
    writeToClient(newsockfd, resMsg);
    printf("send resMsg OK\n");
    writeToClient(newsockfd, conLen);
    writeToClient(newsockfd, conType);
    printf("send conType OK\n");
}

int writeToClient(int newsockfd, char* msg){
    int n =  write(newsockfd, msg, strlen(msg));
    if (n < 0) error("ERROR writing to socket");
    return n;
}

结果如下

========Request Message======
GET /run.mp3 HTTP/1.1
Host: localhost:9999
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4


client socket : 4
method : GET
file : run.mp3
extension : mp3
compare success
compare success
type : audio/mpeg
reading file...
open fd OK
sending header...
response message
HTTP/1.1 200 OK

Content-length: 5187428

Content-Type: audio/mpeg


send resMsg OK
send conType OK
send header OK
sending rcvBuf : 1024, remain : 5186404
sending rcvBuf : 1024, remain : 5185380
sending rcvBuf : 1024, remain : 5184356
sending rcvBuf : 1024, remain : 5183332
sending rcvBuf : 1024, remain : 5182308
sending rcvBuf : 1024, remain : 5181284
sending rcvBuf : 1024, remain : 5180260
sending rcvBuf : 1024, remain : 5179236
sending rcvBuf : 1024, remain : 5178212
sending rcvBuf : 1024, remain : 5177188
sending rcvBuf : 1024, remain : 5176164
sending rcvBuf : 1024, remain : 5175140
sending rcvBuf : 1024, remain : 5174116

.....

sending rcvBuf : 1024, remain : 2593636
sending rcvBuf : 1024, remain : 2592612
sending rcvBuf : 1024, remain : 2591588
========Request Message======
GET /run.mp3 HTTP/1.1
Host: localhost:9999
Connection: keep-alive
Accept-Encoding: identity;q=1, *;q=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Accept: */*
Referer: http://localhost:9999/run.mp3
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
Range: bytes=0-


client socket : 6
method : GET
file : run.mp3
extension : mp3
compare success
compare success
type : audio/mpeg
reading file...
open fd OK
sending header...
response message
HTTP/1.1 200 OK

Content-length: 5187428

Content-Type: audio/mpeg


send resMsg OK
send conType OK
send header OK
send header OK
sending rcvBuf : 1024, remain : 5186404
sending rcvBuf : 1024, remain : 5185380
sending rcvBuf : 1024, remain : 5184356
sending rcvBuf : 1024, remain : 5183332
sending rcvBuf : 1024, remain : 5182308
sending rcvBuf : 1024, remain : 5181284
sending rcvBuf : 1024, remain : 5180260
sending rcvBuf : 1024, remain : 5179236
sending rcvBuf : 1024, remain : 5178212
sending rcvBuf : 1024, remain : 5177188

.........

sending rcvBuf : 1024, remain : 4781924
sending rcvBuf : 1024, remain : 4780900
sending rcvBuf : 1024, remain : 4779876
sERROR writing to socket __sock : 4 __: Broken pipe
ending rcvBuf : 1024, remain : 4778852
sending rcvBuf : 1024, remain : 4777828
sending rcvBuf : 1024, remain : 4776804
sending rcvBuf : 1024, remain : 4775780
sending rcvBuf : 1024, remain : 4774756
sending rcvBuf : 1024, remain : 4773732
sending rcvBuf : 1024, remain : 4772708
sending rcvBuf : 1024, remain : 4771684
(EXIT)

编辑2017.10.13

  • intssize_t

  • 当我使用 readsendwrite 时,使用 ssize_t 而不是 int

  • 确定请求消息是 NUL 我在调用后添加了以下代码 read

if(reqMsg[0] == 0){
    printf("req msg is null\n");
    close(newsockfd);
    return NULL;
}
requestHandler(newsockfd, reqMsg);
bzero(reqMsg, 500);
  • writeToClient 中,写入直到发送所有msg . 我在下面添加了代码 .
long toSend = strlen(msg);
while(toSend > 0){
    n = write(newsockfd, msg, toSend);
    printf("write :  %ld\n", n);
    toSend -= n;
}

编辑2017.10.13 - 第2

当我使用 write 时,检查其返回值是 0 . 如果返回值为 0 close 客户端套接字,则 return 用于退出pthread .

但仍然发生同样的错误 .

2 回答

  • 0

    服务器按缓冲区的大小读取文件,并将其发送到客户端,与读取大小一样多 .

    不它不是 .

    while((n=read(fd, rcvBuf, BUFSIZ)) > 0){..
    

    这将 n 字节正好加载到缓冲区中,不多也不少 .

    int res = send(newsockfd, rcvBuf, n + 1, 0);
    

    这会向您的客户端发送 n+1 个字节 . 额外字节在当前缓冲区数据中无效 . 如果 n 恰好是 BUFSIZ ,这就是缓冲区溢出 .

  • -1

    我添加了忽略SIGPIPE,但仍然发生了损坏的管道错误 .

    SIGPIPE可以忽略,但如果管道错误太多,OS可能会杀了你 .

    您的问题是您不明白SIGPIPE意味着您的程序中有错误:

    当SIGPIPE信号尝试写入管道而没有连接到另一端的进程时,它将被发送到进程 .

    这很清楚,你试着写一个已经关闭的套接字 . 因此,解决方案是查看程序中忘记正确处理错误的位置 .

    n = read(newsockfd,reqMsg,499);
    if (n < 0) error("ERROR reading from socket");
    

    这里如果 n 等于 0 套接字已经关闭 .

    int res = send(newsockfd, rcvBuf, n + 1, 0);
    if(res <0) {
    
    writeToClient(newsockfd, msg);
    
    writeToClient(newsockfd, resMsg);
    printf("send resMsg OK\n");
    writeToClient(newsockfd, conLen);
    writeToClient(newsockfd, conType);
    
    int n =  write(newsockfd, msg, strlen(msg));
    if (n < 0) error("ERROR writing to socket");
    

    看,你永远不会验证你写了至少1个字节(如果msglen也为0,ofc write可以返回0) .

    在真正的服务器中,您总是在写入之前阅读,通过读取您可以处理文件结尾但在您的代码中您无法正确处理客户端 .

相关问题