Skip to content

Latest commit

 

History

History
231 lines (178 loc) · 6.9 KB

File metadata and controls

231 lines (178 loc) · 6.9 KB

13장 다양한 입출력 함수들

13-1 send & recv 입출력 함수

send & recv

read / write는 옵션, 특성을 부여할 수 없지만 send / recv는 부여할 수 있다.

  • send : 데이터 전송
#include <sys/socket.h>

int send(int sockfd, const void *msg, size_t nbytes, int flags);
// 성공 시 전송된 바이트 수, 실패 시 -1 반환
  • recv : 데이터 수신
#include <sys/socket.h>

ssize_t recv(int socket, void *buf, size_t len, int flags);
// 성공 시 수신한 바이트 수, 실패 시 -1 반환
  • flags : 데이터 송수신에 사용되는 옵션 정보

→ OR 연산자(| 연산자)를 이용해서 둘 이상을 동시에 지정 가능

→ 옵션의 종류와 지원 여부는 운영체제별로 차이가 있음

https://blog.kakaocdn.net/dn/bhqVBw/btqZ6jZ32J4/45WqgqKKbfUgiU6KidZyXK/img.png

  • MSG_OOB : OOB 옵션을 줄 수 있음
  • MSG_PEEK : read 시 입력 버퍼에 있는 데이터가 소멸되지 않음
  • MSG_DONTROUT : 로컬 상에서만 데이터를 전송하겠다는 옵션
  • MSG_DONTWAIT : 읽어들일 데이터가 없거나 쓸게 없더라도 block되지 않고 바로 반환
  • MSG_WAITALL : 요청한 바이트 수만큼 받을때까지 반환되지 않음

MSG_OOB

MSG_OOB(OUT-OF-BAND) 옵션은 메시지를 송수신할 수 있는 옵션으로서, 긴급 데이터를 의미하고 긴급 데이터라고 해서 TCP의 기본인 전송 순서를 유지하는 틀을 깨지는 않고 이 메시지가 긴급이라고 알리기 위한 옵션에 해당한다.

밑의 예시에서는 OOB 메세지를 운영체제에서 받으면 프로세스에 알려주게 되는데 프로세스에 알려주기 위해 SIGUSR 시그널을 등록하고 이를 sigaction을 통해 시그널을 이용하여 보낸 값을 확인하게 하였다.

int main(int argc, char *argv[])
{
	...;
	struct sigaction act;

	act.sa_handler=urg_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;

	acpt_sock=socket(PF_INET, SOCK_STREAM, 0);
	...;
	fcntl(recv_sock, F_SETOWN, getpid());
    // recv_sock의 소유자를 현재 프로세스로 변경
    // recv_sock에 대한 시그널이 발생했을 때 현재 프로세스로 받을 것임을 명시
	state=sigaction(SIGURG, &act, 0);
	// 시그널 등록

	while((str_len=recv(recv_sock, buf, sizeof(buf), 0))!= 0)
	{
		if(str_len==-1) {
			continue;
        	}
		buf[str_len]=0;
		puts(buf);
	}
	close(recv_sock);
	close(acpt_sock);
	return 0;
}

void urg_handler(int signo)
{
	int str_len;
	char buf[BUF_SIZE];
	str_len=recv(recv_sock, buf, sizeof(buf)-1, MSG_OOB);
	buf[str_len]=0;
	printf("Urgent message: %s \n", buf);
}r

긴급으로 보낸 메세지의 양에 상관없이 1바이트만 반환해서 긴급이라는 것을 알려주는데, 이는 평소의 pipe로 데이터를 받아오는 것이 아닌 전혀 다른 통신 경로로 전송되는 데이터 로 긴급 메세지를 받아온다.

그렇다면 이렇나 긴급 모드는 어떻게 동작하는 가?

Urgent mode의 동작원리

TCP 헤더 부분에 URG=1을 통해 긴급 메세지가 존재하는 패킷임을 보여주고, 문자열의 맨 끝부분을 urgent pointer로 구성하여, 이 앞에 몇 글자가 메세지인지를 urgent pointer의 offset을 제공함으로서 보여준다. 결국 urgent mode는 메세지 처리를 재촉하는데 의미가 있을 뿐 제한된 형태의 메세지를 긴급으로 전송하는데 의미가 있는 것은 아니다.

입력버퍼 검사하기

MSG_PEEK옵션은 MSG_DONTWAIT 옵션과 함께 설정되어 입력버퍼에 수신된 데이터가 존재하는지 확인하는 용도로 사용된다.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int acpt_sock, recv_sock;
	struct sockaddr_in acpt_adr, recv_adr;
	int str_len, state;
	socklen_t recv_adr_sz;
	char buf[BUF_SIZE];
	if(argc!=2) {
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}
	
	acpt_sock=socket(PF_INET, SOCK_STREAM, 0);
	memset(&acpt_adr, 0, sizeof(acpt_adr));
	acpt_adr.sin_family=AF_INET;
	acpt_adr.sin_addr.s_addr=htonl(INADDR_ANY);
	acpt_adr.sin_port=htons(atoi(argv[1]));
	
  if(bind(acpt_sock, (struct sockaddr*)&acpt_adr, sizeof(acpt_adr))==-1)
		error_handling("bind() error");
	listen(acpt_sock, 5);
	
	recv_adr_sz=sizeof(recv_adr);
	recv_sock=accept(acpt_sock, (struct sockaddr*)&recv_adr, &recv_adr_sz);
	
	while(1)
	{
		str_len=recv(recv_sock, buf, sizeof(buf)-1, MSG_PEEK|MSG_DONTWAIT);
		// MSG_PEEK과 MSG_DONTWAIT을 통해 데이터를 훑되, 데이터가 존재하지 않더라도 블로킹 상태에 들어가지 않는다.
		if(str_len>0)
			break;
	}

	buf[str_len]=0;
	printf("Buffering %d bytes: %s \n", str_len, buf);
 	
	str_len=recv(recv_sock, buf, sizeof(buf)-1, 0);
	// 이번에는 옵션을 주지 않았기 때문에 데이터가 사라짐.
	buf[str_len]=0;
	printf("Read again: %s \n", buf);
	close(acpt_sock);
	close(recv_sock);
	return 0; 
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

13-2 readv & writev 입출력 함수

readv & writev 함수의 사용

데이터를 모아서 전송하고, 모아서 수신하는 기능의 함수

기존 write, read 함수는 하나의 버퍼를 가지고 데이터를 송수신하였지만, writev, readv 함수는 데이터를 둘 이상의 버퍼를 통해 송수신하여, 입출력 함수호출의 수를 줄일 수 있다.

#include <sys/uio.h>

ssize_t writev(int fildes, const struct iovec* iov, int iovcnt);
ssize_t readv(int fildes, const struct iovec* iov, int iovcnt);
// files : 소켓이나 파일의 디스크립터 전달
// iov : 구조체 iovec 배열의 주소 값으로 전송하거나 받을 데이터의 위치 및 크기 정보
// iovcnt : iov 배열의 길이 정보

struct iovec
{
    void* iov_base;		// 버퍼의 주소
    size_t iov_len;		// 버퍼의 크기
}
// writev 예시
int main(int argc, char *argv[])
{
	struct iovec vec[2];
	char buf1[]="ABCDEFG";
	char buf2[]="1234567";
	int str_len;

	vec[0].iov_base=buf1;
	vec[0].iov_len=3;
	vec[1].iov_base=buf2;
	vec[1].iov_len=4;

	str_len=writev(1, vec, 2);
	puts("");
	printf("Write bytes: %d \n", str_len);
	return 0;
}

// readv 예시
int main(int argc, char *argv[])
{
	struct iovec vec[2];
	char buf1[BUF_SIZE]={0,};
	char buf2[BUF_SIZE]={0,};
	int str_len;

	vec[0].iov_base=buf1;
	vec[0].iov_len=5;
	vec[1].iov_base=buf2;
	vec[1].iov_len=BUF_SIZE;

	str_len=readv(0, vec, 2);
	printf("Read bytes: %d \n", str_len);
	printf("First message: %s \n", buf1);
	printf("Second message: %s \n", buf2);
	return 0;
}

https://blog.kakaocdn.net/dn/cbxiSs/btqZ1a4orYI/y7SXUrSq9VqGkhAJZruEu1/img.png