5서클 드디어 진입!
그 첫과제로 IRC를 진행하게 되었다.
나 IRC라는 단어도 처음 들어봤잖아..
Internet Relay Chat
의 약자로 실시간 기본 채팅 서비스라고 한다.
보통 통신망에서는 각 통신망에 있는 사람들끼리만 채팅이 가능한데 IRC는 IRC 서버끼리 연계가 되어있다면
서로 다른 서버에 접속해 있는 유저들도 같이 채팅이 가능하다!
UDP
- 신속성
- 응답을 확인하지 않음
- 보낸 순서대로 도착하지 않을 수 있음
- 속도를 필요로 하는 서비스들이 UDP를 사용
TCP
- 정확성
- 보낸 것을 받았는 지 응답을 확인함
- 보낸 순서대로 도착함
- 속도는 느리지만 신뢰성이 높다
- 데이터를 보내기 전에 반드시 연결이 형성되어야 함
- 1:1 통신만 가능
서버 소켓
- 서버 프로그램에서만 사용하는 소켓
- 클라이언트로부터 연결 요청이 오기를 기다렸다가 연결 요청이 오면 클라이언트와 연결을 맺고 다른 소켓을 만든다
- socket() 함수로 소켓 생성
- bind() 함수로 ip와 port번호 설정
- listen() 함수로 클라이언트 접근 요청 수신 대기열 생성
- accept() 클라이언트와의 연결을 기다림
클라이언트 소켓
- 클라이언트 소켓을 생성 후 데이터를 전송하고 받는 역할을 한다
- socket() 함수로 소켓 생성
- connect() 통신 할 서버의 ip와 port 번호에 통신 시도
- 서버 accept() 함수를 통해 클라이언트의 소켓 디스크립터 반환
- 이를 통해 클라이언트와 서버가 통신
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 메모리 할당을 위한 공간이 없음
#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이면 양수를 반환
이번 IRC 코드에서는 포트 번호를 지정하여 해당 포트번호로 들어온 클라이언트에 소켓을 열어주게 된다. 그때 반환 된 fd를 poll에 설정해 놓고 해당 fd를 통해 송수신하게 된다.
🚨 주의해야 할 점 🚨
우리가 하면서 헤맸던 부분들이 몇가지 있었다
1. 벡터
의 인덱스로 참조한 객체를 다른 곳에 넘길 경우 벡터에 erase로 데이터를 지우면 벡터가 값들을 하나씩 앞으로 땡기면서 원하지 않는 결과를 발생시킬 수 있다..
- 나는 그 객체 주소를 넘겨 줄 줄 알았지..
- 벡터 인덱스 공간의 주소를 넘겨 줄 줄 알았냐구...
- 벡터의 인덱스로 참조하여 다른 곳에 객체를 넘겨주지 말자..
- 우리는 리스트로 해결했다
2. ctrl + D
입력에 대한 대처를 해야 한다
-\r\n
입력이 들어오기 전까지 리드한 것들을 모조리 저장해 놓고있다가 실제 \r\n
이 들어오면 그때 최종적으로 커맨드를 실행해 줘야 한다.
3. ctrl + C 시그널 등으로 클라이언트가 quit이 아닌 다른 루트를 통해 접속이 종료되는 경우
도 처리 해줘야 한다
- 해당 처리를 하지 않으면 실제 클라이언트의 접속이 종료되었음에도 불구하고 종료된 지 몰라 클라이언트 객체가 계속해서 살아있음은 물론이고, 해당 fd가 닫히지 않아 그만큼 실제 클라이언트를 받을 수 없게 된다.