CS/Network

02_Socket_IO

@~@ 2024. 7. 23. 23:52

Socket is defined by a 5-tuple

e.g., 2개의 브라우저가 example.com의 80번 포트에 동시접속 하는 경우

  • Local IP address: 브라우저를 실행한 컴퓨터의 IP 주소
  • Local port: 각 브라우저의 인스턴스의 로컬 포트번호
  • Remote IP address: example.com의 IP주소
  • Remote port: 80
  • Protocol(TCP or UDP): TCP

→ 두 브라우저의 소켓에서 Local IP, Remote IP, Remote port, Protocol 값은 똑같음

 

UNIX 시스템 콜 - open, read, write, close

Open()

int open (const char* Path, int flags);
  • Path: open 하려는 파일의 pathname
  • flags:
  • file access mode
    • O_RDONLY: reading만 가능
    • O_WRONLY: write만 가능
    • O_RDWR: read, write만 가능
    • any combination
    • O_APPEND: 파일의 offset이 파일의 끝으로 이동하여 이어쓰기 가능
    • O_CREAT: 없는 파일이면 새로 생성
    • O_EXCL: 파일이 있는데 O_CREAT를 fail 발생시킴
    • O_TRUNC: 파일 길이를 0으로 세팅
  • 리턴값
    • 성공: fd
    • 실패: -1
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
extern int errno;
int main(){
    // if file does not have in directory
    // then file foo.txt is created.
    int fd = open("foo.txt", O_RDONLY | O_CREAT);
    printf("fd = %d\n", fd);
    if (fd == -1) {
        // print which type of error have in a code
        printf("Error Number % d\n", errno);
        // print program detail "Success or failure"
        perror("Program");
    }
    return 0;
}

 

read()

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  • fd: read하려는 파일의 fd
  • buf: read한 내용을 저장하는 곳
  • count: 읽으려는 바이트 수
  • 리턴값
    • 성공: 읽은 바이트 수
    • 실패: -1
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
    int fd, sz;
    char* c = (char*)calloc(100, sizeof(char));
    fd = open("foo.txt", O_RDONLY);
    if (fd < 0) {
        perror("r1");
        exit(1);
    }
    sz = read(fd, c, 10);
    printf("called read(% d, c, 10). returned that"
    " %d bytes were read.\n",
    fd, sz);
    c[sz] = '\0';
    printf("Those bytes are as follows: % s\n", c);
    return 0;
}

write()

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  • fd: write 연산을 수행할 파일의 fd
  • buf: write할 내용이 있는 버퍼의 포인터
  • count: write할 바이트 수
  • 리턴값
    • 성공: write한 바이트 수
    • 실패: -1
#include<stdio.h>
#include <fcntl.h>
main(){
    int sz;
    int fd = open("foo.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0){
        perror("r1");
        exit(1);
    }
    sz = write(fd, "hello geeks\n", strlen("hello geeks\n"));
    printf("called write(% d, \"hello geeks\\n\", %d)."
    " It returned %d\n", fd, strlen("hello geeks\n"), sz);
    close(fd);
    } 

close()

#include <unistd.h>
int close(int fd);
  • fd: close할 파일의 fd
  • 리턴값
    • 성공: 0
    • 실패: -1
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
    int fd1 = open("foo.txt", O_RDONLY);
    if (fd1 < 0) {
        perror("c1");
        exit(1);
    }
    printf("opened the fd = % d\n", fd1);
    // Using close system Call
    if (close(fd1) < 0) {
        perror("c1");
        exit(1);
    }
    printf("closed the fd.\n");
}

 

ifaddrs 구조체

 

getifaddrs()

int getifaddrs(struct ifaddrs **ifap);
void freeifaddrs(struct ifaddrs *ifa);
  • 로컬 시스템에서 네트워크 인터페이스의 정보를 얻는 함수
  • 네트워크 인터페이스의 정보를 struct ifaddrs 라는 구조체에 저장하고 구조체들을 링크드리스트 형태로 만듦
  • *ifap: 구조체 링크드리스트의 첫 번째 ifaddrs 구조체를 가리킴

 

addrinfo 구조체

 

네트워크 소켓의 두 가지 유형: Connection-oriented, Connectionless

Connection-oriented

  • TCP
  • 데이터 도착 순서를 보장
  • 데이터가 두 번 도착하여 중복되지 않도록 방지
  • HTTP, SMTP, FTP, SSH

Connectionless

  • UDP
  • 각 패킷을 독립적으로 처리 → 프로토콜 관점에서, 중복 데이터도 별개의 데이터로 처리함
  • 패킷이 도착할 거라는 보장X → 패킷이 도착하지 않았을 때 이를 알릴 방법도 없음
  • 패킷이 순서대로 도착할 거라는 보장X

 

Socket functions

  • socket(): 새로운 소켓 생성
  • bind(): 소켓에 IP, port 주소 할당
  • listen(): 서버 입장에서 사용, 새로운 연결 요청 기다림
  • connect(): 클라이언트 입장에서 사용, TCP 연결 요청 전송
  • accept(): 서버 입장에서 사용, 새로운 소켓의 TCP 연결 요청 수락

위 세 개 listen, connect, accept는 TCP 연결에만 존재

  • send(), recv(): 데이터 교환
  • sendto(), recvfrom(): 데이터 교환 시 대상 주소를 명시 (UDP)
  • close(): 소켓 종료 (TCP의 경우, connection terminate)
  • shutdown(): TCP에서 소켓의 특정 방향 통신을 중단
  • select(): 여러 소켓을 모니터링
  • getnameinfo(), getaddrinfo(): hostname, port 정보를 주소 정보로 변환
  • setsockopt(): 소켓 옵션 설정
  • fcntl(): 소켓 옵션 보기

 

TCP program flow

Client

  1. TCP 서버의 주소를 알아야 함 → client프로그램을 실행할 때 커맨드 라인에 서버의 hostname(IP), 포트번호를 같이 입력해서 알려줌
  2. 입력받은 주소(e.g., www.example.com)를 getaddrinfo()를 호출하여 struct addrinfo 구조체로 변환
  3. socket() 호출하여 소켓 생성
  4. connect() 호출하여 새로운 TCP connection을 설정
    → 성공적으로 연결되면 send()와 recv()를 사용하여 데이터 교환

Server

  1. 특정 포트와 인터페이스에서 연결 요청을 수신하도록 설정
    struct addrinfo 구조체를 사용하여 올바른 IP, port 주소로 초기화
  2. socket() 호출하여 소켓 생성
    → bind() 호출하여 생성된 소켓을 올바른 IP, port주소에 바인딩
  3. listend() 호출하여 소켓이 새로운 연결을 기다리는 상태로 만듦
  4. accept() 호출하여 새로운 연결 요청을 수락
    → 연결 성공: 새로운 소켓을 리턴
    → 리턴한 소켓으로 client와 recv(), send() 함수로 데이터 교환
  5. client가 여러 개인 경우, 처음 만든 소켓은 계속해서 새로운 연결 요청을 수락하는 상태로 유지
    → accept()를 반복적으로 호출 → 여러 client를 처리

 

UDP program flow

Client

UDP client 프로그램은 TCP와 달리 연결을 설정하지 않고 데이터를 주고 받기 때문에 remote UDP peer의 주소를 알고 첫 번째 패킷을 전송해야 한다.

  1. getaddrinfo()를 호출하여 주소를 struct addrinfo 로 변환
  2. sendto() 호출하여 첫 번째 패킷을 전송
  3. sendto(), recvfrom()을 반복 호출하여 추가적인 패킷을 송수신
  • TCP와 달리 handshake 과정이 없기 때문에 데이터를 먼저 보내야만 데이터를 받을 수 있음

Server

  1. struct addrinfo 를 사용하여 올바른 IP, port 주소로 초기화
    → getaddrinfo()를 호출하여 프로토콜에 독립적인 주소 정보를 설정
    → UDP client로부터의 연결을 수신
  2. socket() 호출하여 새로운 소켓 생성
    → bind() 호출하여 생성된 소켓을 올바른 IP, port 주소에 바인딩
  3. recvfrom() 호출하여 client로부터 데이터 수신
    → recvfrom()은 데이터 수신할 때까지 block 상태임
  4. sendto()로 첫 번째로 수신한 데이터에 대한 응답을 전송
    4-1. 추가 데이터 수신을 위해 recvfrom()을 반복 호출할 수 있음

'CS > Network' 카테고리의 다른 글

04_Multiprocess  (2) 2024.07.24
03_TCP Connections  (0) 2024.07.24
TCP flow control이란?  (0) 2024.03.18
다중화와 역다중화  (0) 2024.02.01
OSI 7계층 모델과 TCP/IP 4계층 모델 비교  (0) 2024.01.31