[C 언어] 소켓통신 코드 분석
카테고리: C
1. 리눅스안에서의 소켓 통신
1. 서버
1-1. 소켓 생성
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
-> 성공 시 File Descriptor 반환, 실패 시 -1 반환
socket의 매개변수를 디테일하게 파악해보자.
- int domain.
소켓이 사용할 Protocol Family ( 프로토콜 체계 )의 정보를 전달한다.
Name | Protocol Family |
---|---|
PF_INET | IPv4 |
PF_INET6 | IPv6 |
- int type
데이터의 전송방식
PF_INET의 대표적인 socket type 2개가 있다.
- 연결 지향형 socket type: SOCK_STREAM
(1) 중간에 데이터가 소멸하지 않는다.
(2) 전송 순서대로 데이터가 수신이 된다.
(3) 데이터의 경계가 존재하지 않는다 -> 경계가 없으니 여러번에 걸쳐서 전송하거나 수신할 수 있다 !
(4) 소켓 대 소켓 연결은 반드시 1대 1 구조 이다.
- 비연결 지향형 socket type: SOCK_DGRAM
(1) 데이터 손실 및 파손이 가능하다.
(2) 전송순서 상관없이 빠른 속도의 전송을 지향!
(3) 데이터의 경계가 존재 한다.
(4) 한 번에 전송할 수 있는 데이터의 크기가 제한
1-2. 소켓의 주소 할당
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);
-> 성공 시 0 반환, 실패 시 -1 반환
1-3. 연결 요청이 가능한 상태로 만들기
#include <sys/socket.h>
int listen(int sockfd, int backlog);
-> 성공 시 0 반환, 실패 시 -1 반환
1-4. 연결 요청의 수락
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
-> 성공 시 File Descriptor 반환, 실패 시 -1 반환
1-5. 놓칠 수 있는 것들 & 정리
- 서버와 클라이언트의 가장 큰 차이점은 소켓 개수의 차이이다.
서버는 2개, 클라이언트는 1개
( 뼈대 정리 )
단계 | 기능 | 함수 |
---|---|---|
1단계 | 소켓 생성 | socket() |
2단계 | IP, Port 담당 | bind() |
3단계 | 연결요청 가능 | listen() |
4단계 | 연결요청 수락 | accept() |
4.5단계 | 입출력 처리 | write(), read() |
5단계 | 해제 | close() |
-
서버 소켓 = listening socket 이라고도 부른다 !
-
File Descriptor란?
- OS가 만든 파일을 구분하기 위한 숫자
- 저수준 파일 입출력 함수는 입출력 목적으로 fd를 요구한다.
- 만들면 3번부터 갖게된다.
이유
0 -> standard input
1 -> standard output
2 -> standard error
이렇게 0 ~ 2번까지는 이미 차지하고 있기 때문 !
1-6. 서버안에서 처리 과정
- open 함수 사용
필요한 헤더
각각의 헤더들의 필요처를 쓸 것
- #include <sys/types.h>
- #include <sys/stat.h>
- #include
</span>
int open(const char* path, int flag);
-> 성공 시 File Descriptor 반환, 실패 시 -1 반환
- 첫번째 매개변수는 파일의 경로를 뜻한다.
- 2번째 매개변수는 어떤 행동을 할 것인지에 따라 아래처럼 다르다.
(1) Flag
오픈 모드 | 의미 |
---|---|
O_CREAT | 필요하면 파일 생성 |
O_TRUNC | 기존 데이터 전부 삭제 |
O_APPEND | 기존 데이터 모두 보관한 채로 다음에 이어서 저장 |
O_RDONLY | 읽기 전용으로 파일 오픈 |
O_WRONLY | 쓰기 전용으로 파일 오픈 |
O_RDWR | 읽기와 쓰기 겸용으로 파일 오픈 |
주의사항 | O_CREAT에서 E 를 쓰면 안된다 ! |
- (2) 플래그 연속으로 쓰기
- 만들고 삭제하고 쓰기 모드로 하겠다.
O_CREAT | O_TRUNC | O_WRONLY
(3) 닫기
필요한 헤더
- #include
</span>
int close(int fd);
- write와 read 함수 사용
필요한 헤더
#include
- write 함수
ssize_t write(int fd, const void* buf, size_t nbytes); -> 성공 시 전달한 바이트 수 반환, 실패 시 -1 반환
- read 함수
ssize_t read(int fd, void* buf, size_t nbytes); -> 성공 시 수신한 바이트 수(단, 파일의 끝을 만나면 0) 반환, 실패 시 -1 반환
2. 클라이언트
2-1. 연결 요청
#include <sys/socket.h>
int connect(int sockfd, sturct sockaddr* serv_addr, socklen_t addrlen);
-> 성공 시 0 반환, 실패 시 -1 반환
2. 윈도우에서의 소켓 통신
1. 서버
1. 윈속 초기화
#include <winsock2.h>
int WSAStartup(WORD wVersionRequested, LPWSADATA lpwSAData);
-> 성공 시 0 반환, 실패 시 -1 반환
2. 윈속 소켓 생성
#include <winsock2.h>
SOCKET socket(int af, int type, int protocol);
-> 성공 시 socket handle 반환, 실패 시 INVALID_SOCKET 반환
socket handle 은 리눅스의 file descriptor 와 비슷하다 -> socket을 구분하기 위한 number
3. 윈속 소켓의 주소 할당 및 연결
#include <winsock2.h>
int bind(SOCKET s, const struct sockaddr* name, int namelen);
-> 성공 시 0 반환, 실패 시 SOCKET_ERROR 반환
4. 윈속 연결이 가능한 상태로 만들기
#include <winsock2.h>
int listen(SOCKET s, int backlog);
-> 성공 시 0 반환, 실패 시 SOCKET_ERROR 반환
5. 윈속 연결 요청의 수락
진짜 중요한 부분:
여기선 클라이언트의 소켓 생성과 더불어 주소 할당까지 한다.
#include <winsock2.h>
SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
-> 성공 시 socket handle, 실패 시 INVALID_SOCKET 반환
2. 클라이언트
2-1. 연결 요청
#include <winsock2.h>
int connect(SOCKET s, const struct sockaddr* name, int namelen);
-> 성공 시 0 반환, 실패 시 SOCKET_ERROR 반환
3. 공용
#include <winsock2.h>
int closesocket(SOCKET s);
-> 성공 시 0 반환, 실패 시 -1 반환
4. 처리 과정
4-1. 읽기
#include <winsock2.h>
int send(SOCKET s, const char* buf, int len, int flags)
-> 성공 시 전달한 바이트 수 반환, 실패 시 SOCKET_ERROR 반환
4-2. 쓰기
#include <winsock2.h>
int recv(SOCKET s, const char* buf, int len, int flags);
-> 성공 시 수신한 바이트 수(단, 파일이 끝나면 EOF) 반환, 실패 시 SOCKET_ERROR 반환
댓글 남기기