[C 언어] 소켓통신 코드 분석

Date:     Updated:

카테고리:

태그:

1. 리눅스안에서의 소켓 통신

1. 서버

1-1. 소켓 생성

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
-> 성공  File Descriptor 반환, 실패  -1 반환

socket의 매개변수를 디테일하게 파악해보자.

  1. int domain.
    소켓이 사용할 Protocol Family ( 프로토콜 체계 )의 정보를 전달한다.
Name Protocol Family
PF_INET IPv4
PF_INET6 IPv6
  1. 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. 놓칠 수 있는 것들 & 정리

  1. 서버와 클라이언트의 가장 큰 차이점은 소켓 개수의 차이이다. 서버는 2개, 클라이언트는 1개

( 뼈대 정리 )

단계 기능 함수
1단계 소켓 생성 socket()
2단계 IP, Port 담당 bind()
3단계 연결요청 가능 listen()
4단계 연결요청 수락 accept()
4.5단계 입출력 처리 write(), read()
5단계 해제 close()
  1. 서버 소켓 = listening socket 이라고도 부른다 !

  2. File Descriptor란?

    • OS가 만든 파일을 구분하기 위한 숫자
    • 저수준 파일 입출력 함수는 입출력 목적으로 fd를 요구한다.
    • 만들면 3번부터 갖게된다.

이유
0 -> standard input

1 -> standard output

2 -> standard error

이렇게 0 ~ 2번까지는 이미 차지하고 있기 때문 !

1-6. 서버안에서 처리 과정

  1. 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);
    



  1. write와 read 함수 사용

필요한 헤더
#include </span>

  • 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 반환

C 카테고리 내 다른 글 보러가기

댓글 남기기