- ネットワークプログラミング(サーバ)
- サーバサイドのソケットでのやりとり
BLAB: Bind, Listen, Accept, Begin
ヘッダーはsys/socket.h
- 一個一個解説すると大変なので、以下にサンプルコード貼っときます。
シグナルのサンプルとかも入ってる。
合言葉形式でサーバから返答が返ってくるサンプル。
- サーバサイドのソケットでのやりとり
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
//exitラッパー
void error (char *msg) {
fprintf (stderr, "%s :%s\n", msg, strerror(errno));
exit (1);
}
//sigactionラッパー
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);
}
//sendラッパー
int say (int socket, char *s) {
int result = send (socket, s, strlen(s), 0);
if (result == -1)
fprintf (stderr, "クライアントとの接続エラー!: %s¥n", strerror(errno));
return result;
}
//recvラッパー
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] = '\n';
else
s[c-1] = '\0';
return len - slen;
}
int listener_d;
//for SIGINT handler
void must_die (int sig) {
if (listener_d)
close (listener_d);
fprintf (stderr, "シャットダウン\n");
exit (0);
}
int main () {
//set signal
if (catch_signal (SIGINT, must_die) == -1)
error ("割り込みハンドラ設定失敗!\n");
//create listener socket
listener_d = socket (PF_INET, SOCK_STREAM, 0);
if (listener_d == -1)
error ("ソケット開くの失敗!\n");
//set up listener socket
struct sockaddr_in server;
server.sin_family = PF_INET;
server.sin_port = (in_port_t)htons(PORT); //ポートは適当に設定。サーバ側の設定でポート開放も忘れずに。
server.sin_addr.s_addr = htonl (INADDR_ANY); //INADDR_ANY は、同じホストのすべてのIPアドレスを割り当てるの意。
//socket reuse set up
int reuse = 1;
if (setsockopt (listener_d, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof (int)) == -1)
error ("ソケットの再利用設定失敗!\n");
//bind
if (bind (listener_d, (struct sockaddr *)&server, sizeof(server)) !=0)
error ("バインド失敗!\n");
//listen
if (listen (listener_d, 10) != 0)
error ("リッスン失敗!\n");
//create connect socket
struct sockaddr_storage client;
unsigned int client_len = sizeof (client);
puts ("接続待機");
char buf[255];
while (1) {
//accept
int connect_d = accept (listener_d, (struct sockaddr *)&client, &client_len);
if (connect_d == -1)
error ("接続ソケット開くの失敗!\n");
//fork for concect
if (!fork()) {
close (listener_d);
say (connect_d, "合言葉\n");
read_in (connect_d, buf, sizeof (buf));
if (strncmp (buf, "nobara", 6) != 0) {
say (connect_d, "きさまら はんらんぐんだな!\n");
close (connect_d);
exit (0);
}
say (connect_d, "ダイナミック\n");
read_in (connect_d, buf, sizeof (buf));
if (strncmp (buf, "daikuma", 7) != 0) {
say (connect_d, "きさまら はんらんぐんだな!\n");
close (connect_d);
exit (0);
}
say (connect_d, "通ってよし!\n");
close (connect_d);
exit (0);
}
close (connect_d);
}
close (listener_d);
return 0;
}
- ネットワークプログラミング(クライアント)
- クライアントサイドのソケットでのやりとり
これも、一個一個解説するとめっちゃ長くなるのでサンプルコード貼っときます。
getaddrinfo()
の使い方がポイント。ヘッダーはnetdb.h
。
wikipedia からページの html を引っ張ってくるプログラム。 - Webサーバには、「GETコマンド」、「ホスト名」、「空行」を送る必要あり。
- クライアントサイドのソケットでのやりとり
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
//exitラッパー
void error (char *msg) {
fprintf (stderr, "%s :%s\n", msg, strerror(errno));
exit (1);
}
//sendラッパー
int say (int socket, char *s) {
int result = send (socket, s, strlen(s), 0);
if (result == -1)
fprintf (stderr, "クライアントとの接続エラー!: %s¥n", strerror(errno));
return result;
}
//set up socket function
int open_socket (char *host, char *port) {
//set up socket struct
struct addrinfo *res;
struct addrinfo hints;
memset (&hints, 0 ,sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo (host, port, &hints, &res) == -1)
error ("アドレスがわからん\n");
//create socket
int d_sock = socket (res -> ai_family, res -> ai_socktype, res -> ai_protocol);
if (d_sock == -1)
error ("ソケット開くの失敗!\n");
//connect
int c = connect (d_sock, res -> ai_addr, res -> ai_addrlen);
freeaddrinfo (res);
if (c == -1)
error ("コネクト失敗!\n");
return d_sock;
}
int main (int argc, char *argv[]) {
//create socket
int d_sock = open_socket ("en.wikipedia.org", "80");
//http request
char buf[255];
sprintf (buf, "GET /wiki/%s http/1.1\r\n", argv[1]);
say (d_sock, buf);
say (d_sock, "HOST: en.wikipedia.org\r\n\r\n");
//get and show http
char rec[256];
int bytesRcvd = recv (d_sock, rec, 255, 0);
while (bytesRcvd) {
if (bytesRcvd == -1)
error ("サーバーから読み取れない!\n");
rec [bytesRcvd] = '\0';
printf ("%s", rec);
bytesRcvd = recv (d_sock, rec, 255, 0);
}
close (d_sock);
return 0;
}
- スレッド(※C11 から標準ライブラリにもスレッド機能が組み込まれているとのこと。コメントでご指摘いただきました。API 体系はだいたい一緒の模様。詳しく知りたい人は検索してみてください。)
- POSIXスレッドライブラリ(pthread)を使用する。
- ヘッダーは
pthread.h
-
pthread_t
データ構造体を作成 -
int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);
attr
はスレッド属性。デフォルトはNULL
。詳しくは調べるべし。
start_routine
はスレッドで実行する関数。arg
は関数の第一引数。何もなければNULL。 -
int pthread_join(pthread_t th, void **thread_return);
でスレッドの終了を待つ。
th
の終了した時の返り値がthread_return
に格納される。 - ミューテックス
共有のデータリソースにアクセスする際、2つ以上のスレッドでの同時アクセスを防ぐ。
pthread_mutex_t a_lock = PTHREAD_MUTEX_INITIALIZER
でミューテックスを作成。
ロックしたい部分を、
pthread_mutex_lock (&a_lock);
pthread_mutex_unlock (&a_lock);
で挟んでロック。
いじょー!プロセスとかネットワークとかスレッドあたりは正直覚えることとか、テクニックとかが多すぎてすごい表面の基礎的なとこしかできなかった。
今後はこれを元に精進していきたいと思います。
かしこ