C - ソケットとネットワーキング - 127.0.0.0.1という場所はない - インターネットノックノックサーバー
Head First C ―頭とからだで覚えるCの基本、 David Griffiths(著)、 Dawn Griffiths(著)、 中田 秀基(監修)、 木下 哲也(翻訳)、 O’Reilly Media)の 11章(ソケットとネットワーキング - 127.0.0.0.1という場所はない)、p.480(長いエクササイズ)の解答を求めてみる。
Makefile
all: a.out
./a.out
a.out: main.c
cc main.c
コード
main.c
#include <sys/socket.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
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);
}
void error(char *msg)
{
fprintf(stderr, "%s:%s\n", msg, strerror(errno));
exit(1);
}
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(30000);
name.sin_addr.s_addr = htonl(INADDR_ANY);
int reuse = 1;
if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int)) == -1)
{
error("ソケットに再利用オプションを設定できません。");
}
if (bind(socket, (struct sockaddr *)&name, sizeof(name)) == -1)
{
error("ソケットにバインドできません。");
}
}
int say(int socket, char *s)
{
int result = send(socket, s, strlen(s), 0);
if (result == -1)
{
fprintf(stderr, "クライアントの通信エラー:%s", strerror(errno));
}
return result;
}
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;
}
else if (c == 0)
{
buf[0] = '\0';
}
else
{
s[c - 2] = '\0';
}
printf("%s:%li\n", buf, strlen(buf));
return len - slen;
}
int listener_d;
void handle_shutdown(int sig)
{
if (listener_d)
{
close(listener_d);
}
fprintf(stderr, "さようなら!\n");
exit(0);
}
int main()
{
catch_signal(SIGINT, handle_shutdown);
listener_d = open_listener_socket();
bind_to_port(listener_d, 30000);
if (listen(listener_d, 10) == -1)
{
error("接続持ちできません。");
}
puts("接続を待っています。");
char buf[255];
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 (say(connect_d, "Knock! Knock!\r\n") != -1)
{
read_in(connect_d, buf, sizeof(buf));
if (strcmp(buf, "Who's there?") == 0)
{
if (say(connect_d, "Oscar\r\n") != -1)
{
read_in(connect_d, buf, sizeof(buf));
if (strcmp(buf, "Oscar who?") == 0)
{
say(connect_d, "oscar silly question, you get a silly answer\r\n");
}
else
{
say(connect_d, "indvalid input\r\n");
}
}
}
else
{
say(connect_d, "invalid input\r\n");
}
}
close(connect_d);
}
}
入出力結果(Terminal, Zsh)
% make
cc main.c
./a.out
接続を待っています。
Who's there?:12
Oscar who?:10
Who?:4
Who's there?:12
oscar?:6
^Cさようなら!
%
クライアント
入出力結果(Terminal, Zsh)
% gtelnet 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.
% gtelnet 127.0.0.1 30000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Knock! Knock!
Who?
invalid input
Connection closed by foreign host.
% gtelnet 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?
indvalid input
Connection closed by foreign host.
%