我正在尝试使用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)
int
至ssize_t
read
,send
和write
时,使用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;
}
当我使用write
时,请检查其返回值是0
。如果返回值为0
close
客户端套接字,则return
用于退出pthread。
但仍然发生同样的错误。
答案 0 :(得分:0)
服务器按缓冲区的大小读取文件,并将其发送到客户端,与读取大小一样多。
不,不是。
while((n=read(fd, rcvBuf, BUFSIZ)) > 0){..
这会将n
字节正好加载到缓冲区中,不多也不少。
int res = send(newsockfd, rcvBuf, n + 1, 0);
这会向您的客户端发送n+1
个字节。额外字节在当前缓冲区数据中无效。如果n
正好是BUFSIZ
,那么这就是缓冲区溢出。
答案 1 :(得分:-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)。
在真正的服务器中,您总是在写入之前阅读,通过阅读,您可以处理文件末尾,但在您的代码中,您无法正确处理客户端。