Programming Languages/C

Ch. 11. Socket & Buffer option

ubiquitous4g 2021. 4. 22. 04:27

 

getsockopt & setsockopt

#include <sys/socket.h> 

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);

// 성공시 0 , 실패시 -1 반환 
// sock    옵션확인을 위한 소켓의 파일 디스크립터 전달 
// level   확인할 옵션의 프로토콜 레벨 전달 
// optname 확인할 옵션의 이름 전달
// optval  확인결과의 저장을 위한 버퍼의 주소 값 전달 
// optlen  네 번째 매개변수 optval로 전달된 주소 값의 버퍼크기를 담고 있는 변수의 주소 값 
//         함수호출이 완료되면 네 번째 인자를 통해 반환된 옵션정보의 크기가 바이트 단위로 저장 
#include <sys/socket.h>

int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

// 성공시 0 , 실패시 -1 반환
// sock    옵션확인을 위한 소켓의 파일 디스크립터 전달 
// level   확인할 옵션의 프로토콜 레벨 전달 
// optname 확인할 옵션의 이름 전달
// optval  확인결과의 저장을 위한 버퍼의 주소 값 전달 
// optlen  네 번째 매개변수 optval로 전달된 옵션정보의 바이트 단위 크기 전달

getsockopt 예제

"소켓의 타입은 소켓 생성시 한번 결정되면 변경이 불가능하다."

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, char *argv[]) 
{
	int tcp_sock, udp_sock;
	int sock_type;
	socklen_t optlen;
	int state;
	
	optlen=sizeof(sock_type);
	tcp_sock=socket(PF_INET, SOCK_STREAM, 0);
	udp_sock=socket(PF_INET, SOCK_DGRAM, 0);	
	printf("SOCK_STREAM: %d \n", SOCK_STREAM);
	printf("SOCK_DGRAM: %d \n", SOCK_DGRAM);
	
	state=getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
	if(state)
		error_handling("getsockopt() error!");
	printf("Socket type one: %d \n", sock_type);
	
	state=getsockopt(udp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
	if(state)
		error_handling("getsockopt() error!");
	printf("Socket type two: %d \n", sock_type);
	return 0;
}

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

SO_SNDBUF & SO_RCVBUF

소켓이 생성되면 입력 버퍼와 출력 버퍼가 생성된다.

SO_SNDBUF, SO_RCVBUF를 통해 버퍼의 크기 참조, 변경 가능하다.

버퍼 사이즈 출력

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int sock;  
	int snd_buf, rcv_buf, state;
	socklen_t len;
	
	sock=socket(PF_INET, SOCK_STREAM, 0);	
	len=sizeof(snd_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
	if(state)
		error_handling("getsockopt() error");
	
	len=sizeof(rcv_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
	if(state)
		error_handling("getsockopt() error");
	
	printf("Input buffer size: %d \n", rcv_buf);
	printf("Outupt buffer size: %d \n", snd_buf);
	return 0;
}

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

버퍼 사이즈 변경

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
	int sock;
	int snd_buf=1024*3, rcv_buf=1024*3;
	int state;
	socklen_t len;
	
	sock=socket(PF_INET, SOCK_STREAM, 0);
	state=setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, sizeof(rcv_buf));
	if(state)
		error_handling("setsockopt() error!");
	
	state=setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, sizeof(snd_buf));
	if(state)
		error_handling("setsockopt() error!");
	
	len=sizeof(snd_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf, &len);
	if(state)
		error_handling("getsockopt() error!");
	
	len=sizeof(rcv_buf);
	state=getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void*)&rcv_buf, &len);
	if(state)
		error_handling("getsockopt() error!");
	
	printf("Input buffer size: %d \n", rcv_buf);
	printf("Output buffer size: %d \n", snd_buf);
	return 0;
}

void error_handling(char *message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
/*
root@com:/home/swyoon/tcpip# gcc get_buf.c -o getbuf
root@com:/home/swyoon/tcpip# gcc set_buf.c -o setbuf
root@com:/home/swyoon/tcpip# ./setbuf
Input buffer size: 2000 
Output buffer size: 2048 
*/


Time-wait

호스트 A(Server)의 연결 해체는 Four-way handshaking 이후에 소켓이 바로 소멸되지 않고 Time-wait 상태라는 것을 일정시간 거친다.

 

해당 소켓의 PORT번호가 사용중인 상태이기 때문dp bind 함수의 호출과정에서 오류가 발생한다.

 

클라이언트 소켓의 PORT번호는 서버와 달리 프로그램이 실행될 때마다 유동적으로 할당되기 때문에 Time-wait 상태에 대해 고려하지 않아도 된다 .

 

 

서버가 시스템 문제로 갑작스럽게 종료된 경우, 재빨리 서버를 재가동시켜 서비스를 이어가야 하는데 Time-wait 때문에 기다릴 수밖에 없다면 이는 문제가 될 수 있다 .

 

SO_REUSEADDR의 디폴트 값은 O(FALSE) 인데 , 이는 Time-wait 상태에 있는 소켓의 PORT변호는 할당이 불가능함을 의미한다.

 

변경 >> 

optlen=sizeof(option);

option=TRUE ;

setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen)

Nagle

"앞서 전송한 데이터의 ACK 메시지를 받아야만, 다음 데이터를 전송하는 알고리즘"

TPC는 기본적으로 Nagle 알고리즘을 적용

ACK가 수신될때까지 최대한 버퍼링 후 전송

 

Nagle 알고리즘의 중단

 

Nagle 알고리즘을 고집할 필요는 없이 필요하다면 Nagle 알고리즘을 중단시켜야 한다.

 

TCP _NODELA을 1(TRUE)로 변경 >>

int opt_val=1;

setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,(void*)&opt_val, sizeof(opt_val));