khstar
SOCKET PROGRAMMING TUTORIAL 본문
반응형
SOCKET PROGRAMMING TUTORIAL
Contents
- What is a socket?
- How do I get a socket?
- The bind function Call
- Server Process
- Client Process
- For Talk
- When leaving
- 소켓의 동작 모드
- Tip for Chatting Server and Client
What is a socket?
Socket 이란 서버와 서로 특정한 규약을 사용하여 데이터를 전송하기 위한 방식이다. 소켓을 경유한 프로세스 통신은 클라이언트-서버 모델에 기초하고 있다. 서버 프로세스로 알려진 프로세스는 이름이 클라이언트 프로세스에게 소켓을 생성해준다. 클라이언트 프로세스는 그 명명된 소켓의 연결을 경유해서, 서버 프로세스와 대화할 수 있다. 이를 수행하기 위해서 먼저 클라이언트 프로세서는 명명되지 않은 소켓을 생성하고, 이를 서버의 명명된 소켓과 연결해 줄 것을 요청한다. 연결에 성공하면, 클라이언트와 서버에게 각각 파일 기술자를 하나씩 반환하는데, 두 파일 기술자 모두가 읽기와 쓰기용으로 사용될 수 있다.How do I get a socket?
프로세스는 socket()을 사용하여 소켓을 생성할 수 있다. socket()은 명시된 domain , type, protocol이 명명되지 않은 소켓을 생성한다. 만일 socket()이 성공한다면, 새로 생성된 소켓에 연관된 파일 기술자를 반환한다. 그렇지 않으면 -1을 반환한다. 이 함수는 socket을 만들기만 할 뿐 아직 연결을 하는 단계는 아니다.` prototype
int socket(int family, int type, int protocol);
- family field- 소켓의 도메인은 서버와 클라이언트 소켓이 존재하는 장소를 가리킨다.
- AF_UNIX : 클라이언트와 서버는 동일한 기계에 존재해야 한다.
- AF_INET : 클라이언트와 서버는 인터네트 어느 곳이든지 존재할 수 있다.
- AF_NS : 클라이언트와 서버는 인터네트 어느 곳이든지 존재할 수 있다.
- AF_IMPLINK IMP연결선 계층
AF_ 접두어는 address family를 의미한다. 정의된 또 다른 용어의 집합이 있는데 protocol family를 나타내는 PF_를 접두어로 시작한다.
- type field - 소켓 유형은 클라이언트와 서버 사이에 존재할 수 있는 통신의 유형을 결정한다.
- SOCK_STREAM : 일련번호가 붙은, 신뢰적, 양방향 연결에 기초한 바이트의 가변 길이의 스트림
- SOCK_DGRAM : 전보와 비슷한 , 비연결(connectionless). 비신뢰적 고정 길이의 메시지.
- SOCK_SEQPACKET : 일련번호가 붙은, 신뢰적, 양방향 연결에 기초한 바이트의 고정 길이 패킷.
- SOCK_ROW : 내부 네크워크 프로토콜과 인터페이스에 대한 접근을 제공
- SOCK_RDM
The bind function Call
socket()함수를 통해 생성된 socket과 socket 구조체 정보와의 접속을 수행한다. socket 구조체는 protocol, address, TCP/IP port를 저장하고 있다가 connect(), listen() 등의 작업을 수행할 때 사용된다. 자기의 주소와 포트를 지정하는 역할을 한다. 즉 이름 없는 socket에 이름을 부여하는 역할을 한다.prototype int bind(int socketfd, struct sockaddr *myaddr, int addrlen)
생성된 socket에 "myaddr"구조체로 주어진 주소를 부여하고 보통 "server"가 자신이 제공하는 server port를 지정하기 위해 사용 한다. client의 경우 bind()시스템 호출을 사용하지 않으면 kernel이 사용하지 않는 port를 자동적으로 할당 하게 된다.
두 번째 변수는 주소를 나타내고 세 번째 변수는 이 주소 structure의 크기이다.
bind()의 용도는 다음 세 가지이다.
- Server는 주지된 주소를 시스템에 기록한다.
- client는 자신을 위한 특정한 주소를 기록할 수 있다.
- 비연결형 client는 시스템이 자신에게 유일한 주소를 부여하였음을 확인할 필요가 있으며, 이로써 상대측 server가 응답을 보낼 유효한 반송주소를 갖게 되는 것이다.
The listen function Call
bind된 socket을 가지고 외부에서 접속 요청을 기다리는 함수이다. 만약 접속 요청이 들어오면 accept함수를 통해 새로운 socket을 만들어 통신을 한다. 연결요청을 받아들이기 위한 내부 큐를 설정하는 함수로 새로운 socket 식별자를 돌려준다. 즉, 이 새로운 식별자를 통해 데이터를 주고받을 수 있는 것이다.
일반적으로, 이것은 socket의 bind() 시스템 호출 후와 accept() 시스템 호출 전에 수행된다.prototype int listen(int sockfd, int backlog)
첫번째 파라메터는 우리들 소켓의 descriptor이고, 두 번째 파라메터인 backlog 의 역할은 다음과 같다.
일반적인 connection의 setup은 cient가 connect() 사용하여 connection request를 전송하면 server는 accept()를 사용하여 connection request를 받아들인다. 그런데 만약 server가 다른 작업을 진행중이면, accept()를 할 수 없는 경우가 발생하고 이 경우 connection request는 queue에서 대기하는데 "backlog"는 이와같이 outstanding한 conncection request의 최대 개수이다. 보통 5정도의 value를 사용한다.
Accepting a new connection
일단 소켓이 생성되고, 이름이 부여되고, 큐의 크기가 지정된 후의 마지막 단계는 클라이언트 연결 요구를 수용하는 것이다. 이를 수행하여 위해서 서버는 accept()를 사용하는데, 이는 연결요청을 받아들여 실제로 연결을 설정하는 함수로 새로운 socket 식별자를 돌려준다. 즉 이 새로운 socket 식별자를 통해 데이터를 주고받을 수 있다.
- prototype int accept( int sockfd, struct sockaddr *peer, int addrlen)
- Example
#include
#include ... int newSocket; struct sockaddr_in newAddress; int newAddressSize; newSocket=accept(thisSocket,(struct sockaddr_in*)&newAddress,&newAddressSize);
peer, len 은 현재 수신된 connection request의 상대자, 죽 client의 주소이다. 이것들은 복귀값을 위한 buffer이고, 만약 NULL로 주어지면 해당정보를 복귀하지 않는다.
Connecting to the server
connect()는 bind()된 socket을 이용하여 접속하고자하는 상대방 컴퓨터에 접속을 요청하는 함수이다. 대기상태(listen())의 Server와 연결을 결정하며, Client 프로세스는 Server와의 연결을 설정하기 위해 socket() 시스템 호출 후 socket 지정번호를 connect에 사용한다.
prototype int connect(int sockfd, struct sockaddr *servaddr, int addrien)
client가 remote socket 혹은 server로 연결을 설정하기 위하여 사용한다. 여기서, servaddr는 connection을 연결키 위한 상대자의 주소이고, sockfd는 socket() 시스템 호출에서 얻은 socket 지정번회다. 두 번째와 세 번째 독립변수는 socket주소 structuer와 그것의 크기이다. Connectionless protocol의 경우는 connect()를 호출할 필요가 없다.
대부분 연결지향형 규약에 있어 connect 시스템 호출은 지역 시스템과 외부 시스템 사이의 실제적인 연결을 설정한다. data은 두 시스템간에 교환되며 통신에 관련된 특별한 매개 변수가 일치되어야 한다.
Sending data
prototype //for datastream int send(int sockfd, char buff, int nbytes, int flags) //for datagram int sendto(int sockfd, char buff, int nbytes, int flags,struct sockaddr *to, int addrlen)
Receiving data
prototype //for datastream int recv(int sockfd, char buff, int nbytes, int flags) //for datagram int recvfrom( int sockfd, char buff, int nbytes, int flags struct socketaddr *from, int *addrlen)
Closing the socket
shutdown()- prototype int shutdown(int sockfd, int howto);
- howto: 0: no more data can be received 1: no more output can be allowed 2: both send/receive to be disabled
소켓의 동작 모드
소켓의 동작모드에는 blocking, non-blocking 그리고 비동기(asynchronous) 모드 세 가지가 있으며 소켓을 처음 생성하면 blocking 모드의 소켓이 생성된다. blocking 모드의 소켓이란 이 소켓에 어떤 소켓 관련 시스템 콜을 호출하였을 때 네트웍 시스템(즉, TCP/IP)이 동작을 완료할 때까지 응용 프로세스가 멈추어 있게(block) 되는 소켓을 말한다. 응용 프로그램에서는 필요에 따라 이 blocking 모드의 소켓을 non-blocking 모드 또는 비동기 모드로 변경하여 사용하여야 한다.
Non-blocking 모드의 소켓이란, 소켓 관련 시스템 콜에 대하여 네트웍 시스템이 일단 결과를 바로 리턴하여 응용 프로그램이 block되지 않게 하는 소켓을 말한다. 소켓관련 시스템 콜 중에 block될 수 있는 것은 listen(), connect(), accept(), send(), recv(), close() 등이다. 한편 비동기 모드는 non-blocking 모드에서처럼 block될 수 있었던 소켓 시스템 콜에 대해서 일단 리턴을 하고 시스템 콜의 해당 동작이 완료되면 비동기적으로(asynchronously) 그 결과를 응용 프로그램에게 알려주는 소켓의 동작 모드이다.
select()
select 시스템 콜의 사용 문법은 다음과 같다.int select ( int maxfdp1, /* 최대 파일(및 소켓)번호 크기 + 1 */ fd_set *readfds, /* 읽기상태 변화를 감지할 소켓 지정 */ fd_set *writefds, /* 쓰기상태 변화를 감지할 소켓 지정 */ fd_set *exceptfds, /* 예외상태 변화를 감지할 소켓 지정 */ struct timeval *tvptr; /* select() 시스템 콜이 기다리는 시간 */
select()의 첫번째 인자 maxfdp1은 'I/O 변화를 감시할 총 소켓의 개수 +1'의 값을 지정하여야 하는데 보통 현재 open된 소켓번호 중 가장 큰 값에 1을 더한 값을 사용한다.
fd_set(file descriptor set) 타입의 인자 readfds, writefds, exceptfds는 각각 읽기, 쓰기, 예외상황 발생과 같은 I/O 변화가 발생하였을 때 이를 감지할 대상이 되는 소켓들을 지정하는 배열형 구조체이다. 즉, 이 세 가지 구조체를 통하여 어떤 소켓에서 어떤 I/O 변화 발생을 감지할지를 선택하여 지정할 수 있다.
마지막 인자 tvptr은 select() 시스템 콜이 기다리는 시간을 지정하는데 tvptr이 NULL인 경우에는 지정한 I/O 변화가 발생할 때까지 계속 기다리며, 0인 경우에는 기다리지 않고 바로 리턴되고 그 외의 값인 경우에는 지정된 시간만큼 또는 도중에 I/O 변화가 발생할 때까지 기다린다.
fd_set을 사용하기 위한 매크로FD_ZERO(fd_set *fdset) // *fdset의 모든 비트를 지운다. FD_SET(int fd, fd_set *fdset) // *fdset 중 소켓 fd에 해당하는 비트를 1로 한다. FD_CLR(int fd, fd_set *fdset) // *fdset 중 소켓 fd에 해당하는 비트를 0으로 한다. FD_ISSET(int fd, fd_set *fdset) // *fdset 중 소켓 fd에 해당하는 비트가 세트되어 있으면 양수값인 fd를 리턴한다.
select를 호출하려면 먼저 fd_set 구조체를 만들어야 하는데, server의 경우에는 읽기에 대한 I/O만 확인하면 되므로 fd_set 타입의 구조체 read_fds 하나만 선언하고 FD_ZERO(&read_fds)를 호출하여 read_fds의 모든 소켓번호 위치를 disable시킨다.
Tip for chatting Server and Client
- 채팅 서버 프로그램 채팅 서버는 임의의 클라이언트로부터의 채팅 참가요구를 처리하면서 동시에 어떤 클라이언트가 보내온 채팅 메시지를 모든 클라이언트에게 방송하는 일을 처리하여야 한다.한편 채팅 서버 프로그램은 클라이언트의 접속요청을 처리하는 동시에 다른 클라이언트들이 보내온 메시지를 모든 채팅 참가자 클라이언트에게 전달해야 하므로 프로세스가 어느 한 곳에 멈추어 있을 수 있는 blocking 모드로 동작하면 안 된다.따라서 소켓을 통한 I/O를 비동기 모드로 처리하여야 하는데 이를 위하여 select() 시스템 콜을 사용한다.
- 채팅 클라이언트 프로그램 채팅 클라이언트 프로그램은 사용자의 입력 메시지를 서버로 전송하고, 서버가 보내온 모든 메시지를 사용자 화면에 출력한다.
서버에서는 비동기 모드로 처리하기 위하여 select()시스템 콜을 사용하였으나, client에서는 fork()를 이용하여 두 개의 프로세스를 만들어, 사용자의 키보드 입력 처리와 수신 메시지 출력 두 가지 일을 동시에 수행할 수 있게 할 수도 있고, select() 시스템 콜을 이용하여 소켓을 비동기 모드로 바꾸어 두 가지 입출력을 하나의 프로세스에서 처리하도록 할 수 있다.
출처 : http://home.postech.ac.kr/~colorful/project/network/tutorial.html
위의 자료는 포항공대의 자료입니다. 자료 유지를 위하여 개인 블러그로 옮겨 왔습니다.
반응형
Comments