Socket Programming이란?

  • 두개의 노드를 네트워크 상에서 커뮤니케이션하는 방법 중 하나이다.
  • 하나의 socket이 IP의 특정 port에서 listen하고
  • 다른 하나의 socket이 connection을 만들어서 접근한다.
  • 주로 Server가 listener socket의 역할을 하고 client가 접근하는 socket이 된다.

Stages for Server

int socket_created = socket(domain, type, protocol);
  • domain : communication domain을 의미(주소 체계 나타내는 프로토콜)
  • 예를 들어, AF_INET(IPv4 protocol), AF_INET6(IPv6 protocol)이 있다.
  • type : communication type을 의미한다.
  • 예를 들어, SOCK_STREAM(TCP), SOCK_DGRAM(UDP)
  • 여기서 TCP와 UDP의 차이는 데이터를 보내고 확인이 가능한가 안한가의 차이이다. UDP는 걍 보냄
  • protocol : protocl value for internet Protocol.
bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 생성 후에 socket을 해당 IP의 Port로 엮어준다.
listen(int sockfd, int backlog);
  • 서버 socket이 passive mode이면(bind 됐다면) client가 다가올 때까지 기다린다.
  • backlog는 queue의 maximum length를 의미한다.
int new_socket= accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • listening socket에 들어온 connection request를 extract한다.
  • responsive한 통신을 위해 listening socket을 계속 listening에만 집중할 수 있게 한다.

Stages for Client

  • Socket을 생성하는 과정은 서버와 같다.
connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • client socketfd와 서버 IP Port address에다가 conenction을 생성하는 함수

예제 코드

server.c

// Partly taken from https://www.geeksforgeeks.org/socket-programming-cc/

#include <unistd.h> 
#include <stdio.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <netinet/in.h> 
#include <string.h> 

void child_proc(int conn){
  char buf[1024] ;
  char * data = 0x0 ;
  int len = 0 ;
  int s ;
  
  while ( (s = recv(conn, buf, 1023, 0)) > 0 ) {
    buf[s] = 0x0 ;
    if (data == 0x0) {
      data = strdup(buf) ;
      len = s ;
    }
    //다시 메모리할당해줘요
    else {
      data = realloc(data, len + s + 1) ;
      strncpy(data + len, buf, s) ;
      data[len + s] = 0x0 ;
      len += s ;
    }
  
  }
  printf(">%s\n", data) ;
  //conn한테 다시 데이터를 보내줘요
  //s 만큼만 보내줄거에요 반복해요
  while (len > 0 && (s = send(conn, data, len, 0)) > 0) {
    data += s ;
    len -= s ;
  }
  shutdown(conn, SHUT_WR) ;
  if (data != 0x0) free(data) ;
}

int main(int argc, char const *argv[]) 
{ 
  int listen_fd, new_socket ; 
  struct sockaddr_in address; 
  int opt = 1; 
  int addrlen = sizeof(address); 
  
  char buffer[1024] = {0}; 
  
  listen_fd = socket(AF_INET /*IPv4*/, SOCK_STREAM /*TCP*/, 0 /*IP*/) ;
  if (listen_fd == 0)  { 
    perror("socket failed : "); 
    exit(EXIT_FAILURE); 
  }
  memset(&address, '0', sizeof(address)); 
  address.sin_family = AF_INET; 
  address.sin_addr.s_addr = INADDR_ANY /* the localhost*/ ; 
  address.sin_port = htons(8080); 
  if (bind(listen_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
    perror("bind failed : "); 
    exit(EXIT_FAILURE); 
  } 
  
  while (1) {
    if (listen(listen_fd, 16 /* the size of waiting queue*/) < 0) { 
      perror("listen failed : "); 
      exit(EXIT_FAILURE); 
    } 
    //message가 들어오면 시작해봅시다
    //new comer를 위해서 new socket을 생성해주자
    new_socket = accept(listen_fd, (struct sockaddr *) &address, (socklen_t*)&addrlen) ;
    if (new_socket < 0) {
      perror("accept"); 
      exit(EXIT_FAILURE); 
    } 
    
    if (fork() > 0) {
      //child보고 일하라고 하고 parent는 리스닝 다시
      child_proc(new_socket) ;
    }
    else {
      close(new_socket) ;
    }
  }
} 

client.c

#include <stdio.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
#include <string.h> 

int main(int argc, char const *argv[]) { 
  struct sockaddr_in serv_addr; 
  int sock_fd ;
  int s, len ;
  char buffer[1024] = {0}; 
  char * data ;
  sock_fd = socket(AF_INET, SOCK_STREAM, 0) ;
  if (sock_fd <= 0) {
    perror("socket failed : ") ;
    exit(EXIT_FAILURE) ;
  } 
  
  memset(&serv_addr, '0', sizeof(serv_addr)); 
  serv_addr.sin_family = AF_INET; 
  serv_addr.sin_port = htons(8080); 
  if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
    perror("inet_pton failed : ") ; 
    exit(EXIT_FAILURE) ;
    } 
  
  if (connect(sock_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    perror("connect failed : ") ;
    exit(EXIT_FAILURE) ;
  }
  
  scanf("%s", buffer) ;
  data = buffer ;
  len = strlen(buffer) ;
  s = 0 ;
  while (len > 0 && (s = send(sock_fd, data, len, 0)) > 0) {
    data += s ;
    len -= s ;
  }
  
  shutdown(sock_fd, SHUT_WR) ;
  
  char buf[1024] ;
  data = 0x0 ;
  len = 0 ;
  while ( (s = recv(sock_fd, buf, 1023, 0)) > 0 ) {
    buf[s] = 0x0 ;
    if (data == 0x0) {
      data = strdup(buf) ;
      len = s ;
    }
    else {
      data = realloc(data, len + s + 1) ;
      strncpy(data + len, buf, s) ;
      data[len + s] = 0x0 ;
      len += s ;
    }
  
  }
  printf(">%s\n", data); 
  
} 

느낀점

  • socket programming의 목적을 알겠다.
  • 그리고 socket을 통해서 client와 server가 통신할 수 있게 한 것은 매우 혁신적인 것 같다.