C 언어에서 send()는 connect()가 끝난 소켓이나 accept()로 얻은 소켓에 데이터를 보낼 때 사용한다.
이름만 보면 버퍼 전체를 한 번에 보낼 것 같지만 실제로는 그렇지 않다.
형태는 ssize_t send(int sockfd, const void *buf, size_t len, int flags) 이다.
성공하면 커널 송신 버퍼에 복사한 바이트 수를 반환한다.
반환값이 len보다 작을 수 있으므로 남은 데이터를 다시 보내는 반복문이 필요하다.
특히 논블로킹 소켓에서는 일부 전송이 흔하다.
아래 send_all()은 블로킹 소켓용 보조 함수다. 논블로킹 소켓의 EAGAIN은 쓰기 가능 이벤트를 기다렸다가 이어 보내는 상태 관리가 필요해 이 반복문만으로 해결하지 않는다.
ssize_t send_all(int fd, const void *data, size_t length) {
const char *p = data;
size_t sent = 0;
while (sent < length) {
ssize_t n = send(fd, p + sent, length - sent, MSG_NOSIGNAL);
if (n > 0) {
sent += (size_t)n;
continue;
}
if (n < 0 && errno == EINTR)
continue;
return -1;
}
return (ssize_t)sent;
}
리눅스에서 이미 닫힌 소켓에 보내면 EPIPE와 함께 SIGPIPE가 발생할 수 있다.
MSG_NOSIGNAL을 주거나 프로세스 차원에서 SIGPIPE 처리를 정해야 한다.
send() 성공은 상대 프로그램이 데이터를 처리했다는 뜻이 아니라 로컬 커널이 전송을 받아들였다는 뜻이다.
답글 달기