【问题标题】:c socket send large file occur broken pipe errorc socket发送大文件发生断管错误
【发布时间】:2018-03-24 13:35:51
【问题描述】:

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

我已经使用 pthread 创建了一个服务器。服务器按照缓冲区的大小读取文件,按照读取的大小发送给客户端。

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

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

当html文件有mp3资源作为标签时,mp3发送OK。(我叫localhost:9999/index.html)但是当我直接调用mp3文件(例如localhost:9999/music.mp3)时,会出现断管错误。 (localhost:9999/image.jpeg 可以)

我添加了忽略SIGPIPE,但仍然出现断管错误。

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

服务器.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 中写入,直到发送完所有消息。我添加了下面的代码。

    long toSend = strlen(msg);
    while(toSend > 0){
        n = write(newsockfd, msg, toSend);
        printf("write :  %ld\n", n);
        toSend -= n;
    }
    

编辑 2017.10.13 - 2nd

当我使用write时,检查它的返回值是0。如果返回值为0close客户端套接字和return退出pthread。

但仍然发生同样的错误。

【问题讨论】:

  • 好的,谢谢。很抱歉上传完整代码。
  • 可能有很多事情,但首先,为什么调用 'void sendResponseHeader' 需要一个互斥锁?
  • '服务器按缓冲区大小读取文件,并按照读取大小发送给客户端'.......'while((n=read(fd, rcvBuf , BUFSIZ)) > 0){'.....'int res = send(newsockfd, rcvBuf, n + 1, 0);'. n != n+1。
  • 其实我不明白为什么要使用互斥锁。我看到了这样使用互斥锁的示例。所以我猜测互斥锁可以解决管道问题,并围绕该功能编写互斥锁。

标签: c sockets tcp


【解决方案1】:

服务器按缓冲区大小读取文件,并按照读取大小将其发送给客户端..

不,不是。

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

这会将 n 字节准确地加载到您的缓冲区中,不多也不少。

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

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

【讨论】:

  • 我第一次尝试send(newsockfd, rcvBuf, n, 0);。但同样的问题也会发生。我现在将 n+1 更改为 n。但仍然发生断管错误。
【解决方案2】:

我添加了忽略SIGPIPE,但仍然出现断管错误。

SIGPIPE 可以忽略,但如果你得到太多管道错误,操作系统可能会杀了你。

你的问题是你不明白 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)。

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

【讨论】:

  • n0 时,我添加了close 客户端套接字和return。但仍然出现同样的错误。
猜你喜欢
  • 2012-06-26
  • 2021-07-09
  • 2015-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多