[42seoul] 5 circle - FT_IRC

하이초·2023년 2월 15일
0

42seoul

목록 보기
10/11
post-thumbnail

FT_IRC github 😋

5서클 드디어 진입!
그 첫과제로 IRC를 진행하게 되었다.
나 IRC라는 단어도 처음 들어봤잖아..

1. IRC🤔?

Internet Relay Chat의 약자로 실시간 기본 채팅 서비스라고 한다.
보통 통신망에서는 각 통신망에 있는 사람들끼리만 채팅이 가능한데 IRC는 IRC 서버끼리 연계가 되어있다면
서로 다른 서버에 접속해 있는 유저들도 같이 채팅이 가능하다!

2. 그래서 네트워크 통신 그거 어떻게 하는 건데?!

🥨 TCP

  • 인터넷 7계층에서 4계층인 TCP/IP 전송 계층에서 사용되는 프로토콜이다.
  • 이 프로토콜에는 TCP와 UDP가 있는데 이 두 가지에는 차이점이 있다.

UDP

  • 신속성
  • 응답을 확인하지 않음
  • 보낸 순서대로 도착하지 않을 수 있음
  • 속도를 필요로 하는 서비스들이 UDP를 사용

TCP

  • 정확성
  • 보낸 것을 받았는 지 응답을 확인함
  • 보낸 순서대로 도착함
  • 속도는 느리지만 신뢰성이 높다
  • 데이터를 보내기 전에 반드시 연결이 형성되어야 함
  • 1:1 통신만 가능

🥨 포트

  • 포트 번호는 서버가 클라이언트를 식별하기 위해 사용한다
  • 같은 ip로 보냈어도 어떤 포트번호의 프로세스에게 보낸 것인지 확인해서 실제 해당 정보가 필요한 프로세스에게 해당 포트로 정보를 전달할 수 있는 것이다!
  • 🤔 근데 포트 정보는 어디에 저장되어있는 걸까?? 포트라는 것이 또 하나의 논리적인 주소일텐데.. 뭘 어케 알고 여기가 20번포트 ㅇㅋ 내가 갈 곳ㅇㅇ 이렇게 가는 거냔 말임... 포트를 어케 열고 닫는단 말임...

🥨 소켓

  • 소켓은 두 프로그램이 네트워크를 통해 서로 통신을 수행할 수 있도록 양쪽에 생성되는 링크의 단자다.
  • 클라이언트 프로그램과 서버 프로그램이 각각 자신의 포트를 통해 통신해야 하는데 이 포트를 사용하기 위해서 소켓을 이용해야 한다..!

서버 소켓

  • 서버 프로그램에서만 사용하는 소켓
  • 클라이언트로부터 연결 요청이 오기를 기다렸다가 연결 요청이 오면 클라이언트와 연결을 맺고 다른 소켓을 만든다
  • socket() 함수로 소켓 생성
  • bind() 함수로 ip와 port번호 설정
  • listen() 함수로 클라이언트 접근 요청 수신 대기열 생성
  • accept() 클라이언트와의 연결을 기다림

클라이언트 소켓

  • 클라이언트 소켓을 생성 후 데이터를 전송하고 받는 역할을 한다
  • socket() 함수로 소켓 생성
  • connect() 통신 할 서버의 ip와 port 번호에 통신 시도
  • 서버 accept() 함수를 통해 클라이언트의 소켓 디스크립터 반환
  • 이를 통해 클라이언트와 서버가 통신

3. function

🥨 poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

1) fds: pollfd의 구조체 포인터

struct pollfd {
	int fd; // 점검할 fd
    short events; // fd에 대해서 주시할 event. |(or)로 여러 가지를 줄 수 있다.
    short revents; // fd에 발생한 event. kernel이 설정해주며 이를 보고 어떤 event가 발생했는 지 확인할 수 있다.

2) nfds: fds 배열의 크기를 의미
3) timeout: poll이 대기할 시간(ms)을 설정할 수 있음.
- timeout == -1 : 무한 대기
- timeout == 0 : 기다리지 않음

  • return
    - 1 이상: event가 발생한 fd의 갯수
    - 0: timeout
    - -1: 오류 발생, errno 설정
    @ EFAULT: fds 변수가 프로그램의 주소 공간에 있지 않음
    @ EINTR: signal 발생
    @ EINVAL: nfds 값이 RLIMIT_NOFILE 초과
    @ ENOMEM: fd table 메모리 할당을 위한 공간이 없음
  • 여러 file descriptor를 감시하여 event가 발생할 때까지 대기
  • 특정한 fd에 blocking 되지 않음
  • I/O를 수행하기 위한 준비가 될 때까지 기다리는 함수
  • event
    • 우리는 클라이언트에 대해 POLLIN과 POLLHUP 이벤트를 받도록 했다.
    • 왜냐? 들어오는 이벤트도 알아야 하지만 나간 것도 알아야 하기 때문!

🥨 select

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout);

⚡️ fd_set

  • fd를 관리하기 위해 디자인 된 구조체
  • 배열 형태로 0번 인덱스부터 fd 0을 매핑
  • 관련 함수
    - FD_ZERO: fd_set의 모든 비트를 0으로 초기화
    - FD_SET: 인자로 전달된 fd_set의 인덱스를 1로 설정
    - FD_CLR: 인자로 전달된 fd_set의 인덱스를 0으로 설정
    - FD_ISSET: 인자로 전달된 fd_set의 해당 인덱스가 1이면 양수를 반환
  • 어느 소켓의 fd에 read, write, exception이 발생했는지 확인하는 함수
  • fd_set을 전달하여 변화가 발생한 소켓의 디스크립터만 1로 설정
  • fd_set에 대한 주소값을 전달하고 각 액션에 대한 결과를 적용하기 때문에 원본을 복사하여 복사본을 전달해야 함
  • select 함수 호출에 전달된 정보는 운영체제에 등록되지 않는다. 따라서 select 함수를 호출할 때마다 매번 관련 정보를 전달해야 함 -> I/O 멀티플렉싱이 느려짐

🥨 epoll

  • 상태변화의 확인을 위한 전체 파일 디스크립터를 대상으로 하는 반복문이 필요 없음
  • select의 업그레이드 함수
  • linux에서만 사용 가능. so, 넘어가겠음

4. HOW TO MAKE LAZYIRC

이번 IRC 코드에서는 포트 번호를 지정하여 해당 포트번호로 들어온 클라이언트에 소켓을 열어주게 된다. 그때 반환 된 fd를 poll에 설정해 놓고 해당 fd를 통해 송수신하게 된다.

🚨 주의해야 할 점 🚨

우리가 하면서 헤맸던 부분들이 몇가지 있었다
1. 벡터의 인덱스로 참조한 객체를 다른 곳에 넘길 경우 벡터에 erase로 데이터를 지우면 벡터가 값들을 하나씩 앞으로 땡기면서 원하지 않는 결과를 발생시킬 수 있다..
- 나는 그 객체 주소를 넘겨 줄 줄 알았지..
- 벡터 인덱스 공간의 주소를 넘겨 줄 줄 알았냐구...
- 벡터의 인덱스로 참조하여 다른 곳에 객체를 넘겨주지 말자..
- 우리는 리스트로 해결했다

2. ctrl + D 입력에 대한 대처를 해야 한다
-\r\n 입력이 들어오기 전까지 리드한 것들을 모조리 저장해 놓고있다가 실제 \r\n이 들어오면 그때 최종적으로 커맨드를 실행해 줘야 한다.

3. ctrl + C 시그널 등으로 클라이언트가 quit이 아닌 다른 루트를 통해 접속이 종료되는 경우도 처리 해줘야 한다
- 해당 처리를 하지 않으면 실제 클라이언트의 접속이 종료되었음에도 불구하고 종료된 지 몰라 클라이언트 객체가 계속해서 살아있음은 물론이고, 해당 fd가 닫히지 않아 그만큼 실제 클라이언트를 받을 수 없게 된다.

참고

  1. poll
  2. select
profile
개발국대가 되는 그 날까지. 지금은 개발 응애.

0개의 댓글