새소식

C, C++/C TCP, IP

1.2 Server Client

  • -

서버 소켓 프로그래밍 (Server) 

1. 서버 소켓 생성

소켓(socket)은 서버와 클라이언트가 동일한 형태다.

#include <sys/socket.h>

int socket(int domain, int type, int protocol)
// 성공시 파일 디스크립터 반환, 실패시 -1 반환

2. 서버 소켓 결합

서버에서 생성된 소켓(Server Socket) 서버가 사용할 IP주소, Port번호를 결합(bind)시킨다.

#include <sys/socket.h>

int bind(int sockfd, struct sockaddr *myaddr, socklen_t adrlen);
// 성공시 0, 실패시 -1

3. 서버의 클라이언트 연결 요청 대기

listen( )의 역할은, 바인딩된 서버 소켓(Server Socket)으로 수신될 외부 클라이언트의 connect( ) 요청을 대기하며, 연결 요청을 수신 받으면 해당 "클라이언트 정보"를 시스템 내부 큐(Queue)에 쌓는다. 수신 성공(0) 시점에서 서버와 클라이언트는 아직 연결되지 않은 상태다.

 

listen( )이 성공하더라도, 리턴 값에 클라이언트의 요청에 대한 정보는 들어 있지 않으며 접근도 불가능하다.

리턴 값으로 판단할 수 있는 것은 클라이언트 연결 요청을 잘 수신하여 내부에 저장하였는지(0), 그렇지 않고 에러가 발생했는지(-1) 뿐이다.

int listen(int sockfd, int backlog);
// 성공시 0, 실패시 -1

4. 서버와 클라이언트 연결 수립

accept( ) 실행 이후에 실질적인 클라이언트-서버 연결(Connection)이 이루어진다.

주의할 점은 accept( )를 통해 생성된 소켓의 파일 디스크립터는, 앞서 bind( )와 listen( )에 사용된 서버 소켓(Server Socket)이 아니다.

 

accept( ) 내부에서 서버 소켓(Server Socket)과 클라이언트 소켓(Client Socket)을 연결한 새로운 소켓(connected socket)의 파일 디스트립터를 반환하고, 실질적인 데이터 송수신에 이를 이용한다.

 

서버 소켓(Server Socket)의  목적은 클라이언트와 연결된 새로운 소켓을 만드는 것이다.

연결된 새로운 소켓(connected socket)은 연결된 두 기기간의 데이터 송수신을 목적이다.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// 성공시 파일 디스크립터 반환, 실패시 -1 반환

 

5. 데이터 송수신

클라이언트와 동일하다.

ssize_t write(int fd , const void *buf , size_t nbytes);
// 성공시 전달한 바이트 수, 실패시 -1 반환
ssize_t read(int fd , void *buf , size_t nbytes);
// 성공시 전달한 바이트 수, 실패시 -1 반환

6. 소켓 연결 종료

클라이언트와 동일하다.

int close(int fd);
// 성공시 0, 실패시 -1

 

클라이언트 소켓 프로그래밍 (Client)

1. 클라이언트 소켓 생성

int socket(int domain, int type, int protocol)
// 성공시 디스크립터 반환, 실패시 -1 반환

처음 소켓이 만들어지는 시점에는 "연결 대상(서버)"에 대한 어떠한 정보도 들어 있지 않다.

2. 연결 요청

int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
// 성공시 0, 실패시 -1

 

connect() 는 "IP주소"와 "Port 번호"로 식별되는 연결 대상(서버)으로 연결 요청을 보낸다.

3. 데이터 송수신

ssize_t write(int fd , const void *buf , size_t nbytes);
// 성공시 전달한 바이트 수, 실패시 -1 반환
ssize_t read(int fd , void *buf , size_t nbytes);
// 성공시 전달한 바이트 수, 실패시 -1 반환

 

데이터를 보내는 write(), send()의 경우, 주체가 자기 자신이기 때문에, 얼마만큼의 데이터를 보낼 것인지, 언제 보낼 것인지를 알 수 있다.

데이터를 전송 받는 경우, 대상이 언제, 어떤 데이터를 보낼 것인지를 특정할 수 없기 때문에 read(), recv() 가 한번 실행되면 언제 끝날지 모르는 상태다.

4. 소캣 닫기

int close(int fd);
// 성공시 0, 실패시 -1

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.