Tuesday, May 27, 2008

Socket, bind(), listen() and accept()

socket()을 통해 소켓을 생성했다면 다음은 bind()이다. bind()는 아래와 같다.

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);

sockfd는 앞서 만든 socket이고, sockaddr은 IP/Port 등의 주소 정보를 가진 구조체다. addrlen은 이 구조체의 크기이다. Unix socket을 사용하는 경우 sockaddr_un을 사용하지만, Network socket을 사용하는 경우 sockaddr_in을 사용한다. 두 구조체 모두 bind에 넣을 때는 (sockaddr *)로 캐스팅해서 넣어준다(bind내부에서 sockaddr로 캐스팅된 구조체의 앞부분을 보고 network socket인지 unix socket인지 알수 있다). 구조체를 잠깐 살펴 보자.

struct sockaddr_in {
unsigned short sin_family; // AF_INET
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8]; // not used
}
struct sockaddr_un {
short sun_family;
char sun_path[108];
}
struct in_addr {
unsigned long s_addr;
}


특별한 건 없고, AF_INET/AF_UNIX, 주소, 포트 번호 등이 들어가 있다. 주소는 in_addr 구조체로 들어가 있는데, unsigned long 타입으로 되어 있다. IP주소가 x.x.x.x 식으로 되어 있고 x < 256 이므로(8비트), 32bit 만 있으면 충분하다. 그래서 in_addr의 s_addr이 unsinged long (32bit) 이다. 여기에 INADDR_ANY (0)을 넣으면 서버에 지정된 모든 IP에 해당된다. 여기에 주소를 넣을 때는 inet_addr(), inet_aton()을 사용할 수 있는데, 전자의 경우에는 unix 표준이지만, 리턴되는 -1값이 INADDR_NONE(255.255.255.255) 인 경우와 에러인 경우를 모두 포함하므로 구별하기가 모호하다. inet_aton은 구분이 가능하지만 표준에 들어있지 않아.. 거시기 하다ㅡㅡ; 암튼 대충 이렇고, 실제로 주소를 넣는 코드는 아래와 같다.

struct sockaddr_in saddr_svr;
saddr_svr.sin_family = AF_INET;
saddr_svr.sin_port = htons(12000); //port number
// 방법1
saddr_svr.sin_addr.s_addr =
inet_addr ("192.168.100.20");
// 방법2
if (!inet_aton("192.168.100.20", &(saddr_svr.sin_addr.s_addr)))
{
fprintf (stderr, "inet_aton");
}

bind는 성공시 0을, 실패시 -1을 리턴하고 errno를 설정해준다. 위처럼 설정한 sockaddr_in을 아래처럼 binding하면된다.

if (bind(sock_fd, (sockaddr *)saddr_svr, sizeof(saddr_svr)) == -1)
perror("bind");

bind된 socket으로 실제 TCP 요청을 받기 위해(TCP 접속을 위해) listen()을 사용한다. 인자로 backlog 값이 사용되는데, 커널에서 큐로 구현되어 있으며 default로 1024 로 되어 1024개 까지 TCP 접속을 허용할 수 있게된다.

int listen (int s, int backlog);

생성된 소켓으로 외부 요청을 받을 수 있도록 주소를 binding하고 listen 하기 시작했다면, 요청을 기다리다가 들어온 요청을 accept() 하여 받을 수 있다. accept 함수는 bind 소켓과 client의 주소정보를 담을 sockaddr 구조체를 인자로 주면 요청이 들어올 때까지 코드를 block 한다.

int client_sockfd;
struct sockaddr_in client_saddr;
socklen_t client_socklen;

client_socklen = sizeof(client_saddr);
client_sockfd = accept(sock_fd, (struct sockaddr *)&client_saddr, &client_socklen);

printf("client : %s:%d\n", inet_ntoa(client_saddr.sin_addr), ntohs(client_saddr.sin_port));

close(...);

위 코드의 실행시 client의 요청에 대해 아래와 같이 출력된다.

./server 127.0.0.1 21650
Client : fd(4) 127.0.0.1:35130

No comments:

Post a Comment