TCP를 유저모드 어플리케이션 프로세스가 접근할 수 있도록 파일 형식으로 추상화 한 인터페이스가 소켓이다.
계층 | 식별자 |
---|---|
Transport | PORT |
Network | IP |
Data | MAC Adress |
Want to do | Do it |
---|---|
소켓 생성 | socket 함수 호출 |
IP, PORT 번호 할당 | bind 함수 호출 |
연결 요청 가능한 상태로 변경 | listen 함수 호출 |
연결 요청에 대한 수락 | accept 함수 호출 |
연결을 요청 | connect 함수 호출 |
네트워크를 통한 데이터 전달은 전화기에 비유해서 설명하면 좀 더 쉽게 이해할 수 있는 측면이 있다. 실제로도 음성데이터를 주고 받는다는 점에서 더더욱 네트워크와 비슷한 면모가 존재한다.
서버 측 소켓은 전화를 받는 전화기에 비유해서 생각해볼 수 있다.
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
// 성공 시 FD(File Descriptor) 반환
// 실패 시 -1 반환
socket()함수를 통해 파일로 추상화된 TCP를 코드단으로 처리할 수 있게 한다.
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
// 성공 시 0 반환
// 실패 시 -1 반환
bind() 함수 호출을 통해 소켓에 포트를 할당했다. 전화번호가 생긴 셈이다.
코드단에서 네트워크 계층의 IP와 전송 계층의 PORT번호를 지정한다.
#include <sys/socket.h>
int listen(int sockfd, int backlog);
// 성공 시 0 반환
// 실패 시 -1 반환
listen()함수를 통해 소켓을 연결 가능한 상태로 변환
#include <sys/socket.h>
int accept(int sockfc, struct sockaddr *addr socklen_t *addrlen);
// 성공 시 FD(File Discriptor) 반환
// 실패 시 -1 반환
소켓에 데이터 송수신 요청이 들어오면 accept함수를 통해 그 요청을 수락한다.
TCP/IP로 들어온 요청이 file로 추상화 되어 코드단에서 처리가 가능해진다.
#include <stdio.h>
#include <stdilib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
char message[] = "hi!";
if(arc!=2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
serv_sock=socket(PF_INET, SOCK_STREAM, 0); // 1. 소켓을 생성하고 FD 반환
if(serv_sock == -1)
error_handling("socket() error");
memset(&serv_addr, 0, sizeof(serv_addr)); // 소켓 초기화
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = hotns(atoi(argv[1]));
if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1) // 2. bind 함수를 통해 IP, PORT번호 할당
error_handling("bind() error");
if(listen(serv_sock, 5)==-1) // 3. listen 함수를 통해 소켓이 연결 요청을 받을 수 있는 상태로 변환
error_handling("listen() error");
clnt_addr_size = sizeof(clnt_addr);
// accept 함수가 호출되면 연결 요청이 있을 때까지 함수를 반환하지 않는다.
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); // 4. accept함수를 통해 연결요청을 수락
if(clnt_sock==-1)
error_handling("accept() error");
write(cln_sock, message, sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
void error_handling(char * message)
{
fputs(message,stderr);
fputc('\n', stderr);
exit(1)
}
클라이언트 측 소켓을 전화를 거는 측 전화기에 비유해서 생각해 볼 수 있다.
1. 전화를 걸려면 전화기가 있어야 한다. socket()함수를 통해 file로 TCP/IP 추상화
(서버측과 같으므로 생략)
#include <sys/socket.h>
int connect(int sockfd, struc sockaddr *serv)addr, socklen_t addrlen);
// 성공 시 0 반환
// 실패 시 -1 반환
connect() 함수를 통해 서버 측 소켓에 연결한다.