2018年5月5日土曜日

開発環境

Head First C ―頭とからだで覚えるCの基本 (David Griffiths (著)、Dawn Griffiths (著)、中田 秀基 (監修)、木下 哲也 (翻訳)、オライリージャパン)の11章(ソケットとネットワーキング - 127.0.0.1という場所はない)、自分で考えてみよう(p. 487)を取り組んでみる。

自分で考えてみよう(p. 487)

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

void error(char *msg) {
  fprintf(stderr, "%s: %s\n", msg, strerror(errno));
  exit(1);
}
int catch_signal(int sig, void (*handler)(int)) {
  struct sigaction action;
  action.sa_handler = handler;
  sigemptyset(&action.sa_mask);
  action.sa_flags = 0;
  return sigaction(sig, &action, NULL);
}
int open_listener_socket() {
  int s = socket(PF_INET, SOCK_STREAM, 0);
  if (s == -1) {
    error("ソケットを開けません");
  }
  return s;
}
void bind_to_port(int socket, int port) {
  struct sockaddr_in name;
  name.sin_family = PF_INET;
  name.sin_port = (in_port_t)htons(port);
  name.sin_addr.s_addr = htonl(INADDR_ANY);
  int reuse = 1;
  if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse,
                 sizeof(int)) == -1) {
    error("ソケットに再利用オプションを設定できません");
  }
  int c = bind(socket, (struct sockaddr *)&name, sizeof(name));
  if (c == -1) {
    error("ソケットにバインドできません");
  }
}
int read_in(int socket, char *buf, int len) {
  char *s = buf;
  int slen = len;
  int c = recv(socket, s, slen, 0);
  while ((c > 0) && (s[c - 1] != '\n')) {
    s += c;
    slen -= c;
    c = recv(socket, s, slen, 0);
  }
  if (c < 0) {
    return c;
  }
  if (c == 0) {
    buf[0] = '\0';
  } else {
    s[c - 1] = '\0';
  }
  return len - slen;
}
int say(int socket, char *s) {
  int result = send(socket, s, strlen(s), 0);
  if (result == -1) {
    fprintf(stderr, "%s: %s\n", "クライアントの通信エラー", strerror(errno));
  }
  return result;
}
int listener_d;
void handle_shutdown(int sig) {
  if (listener_d) {
    close(listener_d);
  }
  fprintf(stderr, "さようなら!\n");
  exit(0);
}
int main(int arc, char *argv[]) {
  catch_signal(SIGINT, handle_shutdown);
  listener_d = open_listener_socket();
  bind_to_port(listener_d, 30000);
  if (listen(listener_d, 10) == -1) {
    error("接続待ちできません");
  }
  puts("接続を待っています。");
  while (true) {
    struct sockaddr_storage client_addr;
    unsigned int address_size = sizeof(client_addr);
    int connect_d =
        accept(listener_d, (struct sockaddr *)&client_addr, &address_size);
    if (connect_d == -1) {
      error("第2のソケットを開けません");
    }    
    if (!fork()) {
      close(listener_d);
      if (say(connect_d, "Knock! Knock!\r\n") != -1) {
        char buf[255];
        read_in(connect_d, buf, sizeof(buf));
        if (strncmp("Who's there?", buf, 12) == 0) {
          if (say(connect_d, "Oscar\r\n") != -1) {
            read_in(connect_d, buf, sizeof(buf));
            if (strncmp("Oscar who?", buf, 10) == 0) {
              say(connect_d,
                  "oscar silly question, you get a silly answer\r\n");
            } else {
              say(connect_d, "bye.\r\n");
            }
          }
        } else {
          say(connect_d, "bye.\r\n");
        }
      }
      close(connect_d);
      exit(0);
    }
    puts("親プロセス");
    close(connect_d);
  }
}

入出力結果(Terminal)

# server
$ make
cc sample.c -o sample
./sample
接続を待っています。
親プロセス
親プロセス
^Cさようなら!

$

# client1
$ telnet 127.0.0.1 30000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Knock! Knock!
Who's there?
Oscar
Oscar who?
oscar silly question, you get a silly answer
Connection closed by foreign host.
$

# client2
$ telnet 127.0.0.1 30000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Knock! Knock!
Who's there?
Oscar
Oscar who?
oscar silly question, you get a silly answer
Connection closed by foreign host.
$ 

0 コメント:

コメントを投稿