멀티 프로세스를 이용한 에코 및 채팅 서버

이동욱

2021/08/13

Categories: 네트워크

멀티 프로세스 에코 서버


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

void error_proc();
void error_print();

int main(int argc, char* argv[]) {
  int server_sd, client_sd;
  struct sockaddr_in server_addr, client_addr;

  int client_addr_len, read_len;
  char read_buffer[BUFSIZ];
  pid_t pid;

  if (argc != 2) {
    printf("usage: %s [port] \n", argv[0]);
    exit(1);
  }
  printf("server start...\n");

  server_sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (server_sd == -1) error_proc();

  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(atoi(argv[1]));

  if (bind(server_sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) error_proc();

  if (listen(server_sd, 5) < 0) error_proc();

  client_addr_len = sizeof(client_addr);

  while(1) {
    client_sd = accept(server_sd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_sd == -1) {
      error_print();
      continue;
    }
    printf("client %s: %d is connected...\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    pid = fork();
    if (pid == 0) { /* child process */
      close(server_sd);
      while (1) {
        read_len = read(client_sd, read_buffer, sizeof(read_buffer) - 1);
        if (read_len == 0) break;
        read_buffer[read_len] = '\0';
        printf("client (%d): %s\n", ntohs(client_addr.sin_port), read_buffer);
        write(client_sd, read_buffer, strlen(read_buffer));
      }
      printf("client (%d) : is disconnected\n", ntohs(client_addr.sin_port));
      close(client_sd);
      return 0;
    } else if (pid == -1) error_proc("fork");
    else {
      close(client_sd);
    }
  }
  close(server_sd);
  return 0;
}

간단한 UDP 멀티 프로세스 채팅 프로그램


채팅 서버의 역할은 다음과 같다.

  1. 클라이언트의 접속 처리
  2. 클라이언트가 전송하는 문자열을 다른 클라이언트들에게 전송
  3. 클라이언트의 접속 종료 처리

채팅 서버

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

#define MAX_CLIENT 10

void error_proc(const char*);

int check_sock_list(struct sockaddr_in *entry, struct sockaddr_in *list, int count);

int main(int argc, char* argv[]) {
  int my_sock, read_len, n_recv, res;
  char buff[BUFSIZ];
  char name_buffer[50];
  char *str_addr;
  struct sockaddr_in src_addr, dest_addr;

  socklen_t addr_len;
  int n_client = 0, i = 0, port;
  struct sockaddr_in sockets[MAX_CLIENT];

  if (argc != 2) {
    fprintf(stderr, "usage: %s port", argv[0]);
    return 0;
  }

  memset(&sockets, 0, sizeof(sockets[0])*MAX_CLIENT);
  my_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if (my_sock == -1) error_proc("socket");

  memset(&src_addr, 0, sizeof(src_addr));
  src_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  src_addr.sin_family = AF_INET;
  src_addr.sin_port = htons(atoi(argv[1]));

  res = bind(my_sock, (struct sockaddr*)&src_addr, sizeof(src_addr));
  if (res == -1) error_proc("bind");
  addr_len = sizeof(dest_addr);

  while (1) {
    n_recv = recvfrom(my_sock, buff, BUFSIZ - 1, 0,
        (struct sockaddr *) &dest_addr, &addr_len);
    if (n_recv == -1) error_proc("recvfrom");
    res = check_sock_list(&dest_addr, sockets, n_client);

    if (res == n_client) {
      if (res == MAX_CLIENT) continue;
      else {
        memcpy(&sockets[res],
            &dest_addr, sizeof(dest_addr));
        n_client++;
      }
    }
    printf("n_client: %d\n", n_client);
    str_addr = inet_ntoa(dest_addr.sin_addr);
    sprintf(name_buffer, "%s: %d >> ", str_addr, ntohs(dest_addr.sin_port));

    for (i = 0; i < n_client; i++) {
      if (i == res) continue; // sender == receiver skip
      sendto(my_sock, name_buffer, strlen(name_buffer), 0, (struct sockaddr *) &sockets[i], addr_len);
      sendto(my_sock, buff, n_recv, 0, (struct sockaddr *) &sockets[i], addr_len);
    }
  }
  return 0;
}

채팅 클라이언트

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

void error_proc(const char*);

int main(int argc, char* argv[]) {
  int my_sock, read_len, n_sent, n_recv;
  char buff[BUFSIZ];
  char str_addr;
  struct sockaddr_in dest_addr;
  socklen_t addr_len;
  pid_t pid;

  my_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

  memset(&dest_addr, 0, sizeof(dest_addr));
  dest_addr.sin_addr.s_addr = inet_addr(argv[1]);
  dest_addr.sin_family = AF_INET;
  dest_addr.sin_port = htons(atoi(argv[2]));
  addr_len = sizeof(dest_addr);

  pid = fork();
  if (pid == -1) error_proc("fork");
  if (pid == 0) {
    while (1) {
      n_recv = recvfrom(my_sock, buff, BUFSIZ - 1, 0, (struct sockaddr*) &dest_addr, &addr_len);
      if (n_recv == -1) error_proc("read");
      write(1, buff, n_recv);
    }
  } else {
    while (1) {
      fgets(buff, BUFSIZ - 1, stdin);
      read_len = strlen(buff);
      if (read_len == 0) error_proc("fgets");
      n_sent = sendto(my_sock, buff, read_len, 0, (struct sockaddr*) &dest_addr, addr_len);
      if (n_sent == -1) error_proc("write");
      buff[read_len - 1] = '\0';
      if (!strcmp(buff, "END")) break;
    }
    kill(pid, SIGTERM);
  }
  return 0;
}

void error_proc(const char* str) {
  fprintf(stderr, "%s:%s \n", str, strerror(errno));
  exit(1);
}

서버 화면

클라이언트1

클라이언트2

참고 문헌


>> Home