ssize_trio_writen(int fd, void *usrbuf, size_t n){
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
while (nleft > 0) {
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* Interrupted by sig handler return */
nwritten = 0; /* and call write() again */elsereturn-1; /* errno set by write() */
}
nleft -= nwritten;
bufp += nwritten;
}
return n;
}
💡첫번째 문제해결 방법
while 문 없애기
ssize_trio_writen(int fd, void *usrbuf, size_t n){
size_t nleft = n;
ssize_t nwritten;
char *bufp = usrbuf;
if ((nwritten = write(fd, bufp, nleft)) <= 0) {
if (errno == EINTR) /* Interrupted by sig handler return */
nwritten = 0; /* and call write() again */elsereturn-1; /* errno set by write() */
}
return n;
}
잘 작동한다.
하지만 제대로 된 해결 방법은 아니다..
원인을 제대로 분석해보자.
★원인은 SIGPIPE이다.
위에 성공했을 때 스크린샷을 참고해보자. 처음에 브라우저는 video.mp4를 document 타입으로 보낸다. 브라우저 입장에서는 html이 우선순위로 response 받아야 하기 때문이다. 근데 서버에서 mp4 를 던져주는게 아닌가?! 여기서 문제가 발생한다. 브라우저는 document로 request보낸 것을 끊어버리고 video.mp4를 받을 파일디스크립터(fd)를 새로 판다.
rio_writen에서 while문을 통해 이전 fd(파일디스크립터)에 또 요청을 하니 SIGPIPE 신호를 받는다.
[SIGPIPE]
프로세스가 읽기가 안되는 파이프에 쓰려고 한다면, 커널로 부터 SIGPIPE 신호를 받게 된다.
Client가 연결을 끊은 소켓에 서버가 재요청할 때 발생한다.
닫은 fd로 쓰기를 하려는 순간 발생했던 것!
💡SIGPIPE 해결
1. signal.h 헤더파일을 포함한다. (csapp.h에 이미 포함되어있긴함)
2. 첫번째 파이프를 열기 전에 시그널(SIGPIPE,SIG_IGN)을 호출한다. 이 호출은
리눅스 커널에게 만약 SIGPIPE 시그널이 발생하게 되면 무시
1. tiny.c의 main함수 맨 위에 signal(SIGPIPE, SIG_IGN); 쓰기
signal(SIGPIPE, SIG_IGN); 를 사용함으로 SIGPIPE 에러 발생한 부분을 무시해버리기
2. Rio_writen안에 unix_error 처리해서 서버 다운 방지
Rio writen 안에 unix_error 함수 고치기
SIGPIPE로 인해 rio_writen에서 -1을 리턴하기 때문에 unix_error 부분에서 exit(0)을 통해 서버가 다운되니 에러넘버가 EPIPE가 아닐 때만 exit(0)을 실행하게 바꿔준다.