SlideShare a Scribd company logo
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
60分でわかるソケットプログラミン
グ
60分でわかるソケットプログラミン
グ
木本雅彦 <kimoto@soum.co.jp>
<kimoto@ohnolab.org>
株式会社創夢 第三開発部 シニアプロジェクトマネージャ
初版:2010年4月作成、改定版:2015年3月作成
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
創夢とはどういう会社か
UNIXとTCP/IPネットワークに強い会社
全員がUNIXに詳しい
全員がIPに詳しい
ということは → 全社員がネットワークプログラミングくら
い出来るよね!?
ということで、60分レクチャーです
1/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
この発表での前提条件
C言語でのプログラミングはできる
system call programming
file I/O basics
TCP/IPの基礎は理解している
OSI階層モデル
IPアドレス、ヘッダの構造など
2/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
IPというプロトコルについて
IP = InternetProtocol
はじめに: ホストにはIPアドレスがついているらしいぜ
(以下略。知らない人は自力で勉強してください)
3/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
TCPとUDPというプロトコル
TCP: Transmission Control Protocol
送達確認(ACK)の利用による信頼性のある通信
輻輳制御による「フェアな」通信量の制御
UDP: User Datagram Protocol
信頼性のない通信
パケット単位でデータを投げるだけ
4/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
port番号
TCP, UDPで用いる通信口の番号
符号なし16bit
通信相手のアプリケーションを同定する
5/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
well-known portとephemeral port
well-known port
代表的なサービスに割り当てられた番号
0 - 1023
IANAが管理している
ephemeral port
使い捨てポート
RFCでのdynamic port: 49152〜65535
Linuxの実装: 32768〜61000
古いBSDの実装: 1024〜4999
6/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
一応IPv6について、ひとこと
次世代インターネットプロトコル
といいつつ、すでに実用化されている
IPアドレスが128bitに拡張されたという一点がもっとも
重要
アドレスの自動設定
マルチキャストの積極的な利用
7/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
UNIXのプロセス間通信
pipe
双方向パイプ
FIFO
共有メモリ(SYSV由来)
セマフォ(SYSV由来)
signal
socket(BSD由来)
8/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
socket
4.2BSDで導入されたTCP/IPの抽象化および実装
通信路の片方の末端を指し示す
ファイルデスクリプタで表される
→ネットワーク通信をファイルI/Oと同様に記述できる
9/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
余談:socketという敗北
4.2BSDでsocketが導入されたのは、従来のUNIXの
ようにファイルシステム上にネットワークI/Oをマッピングで
きなかったため
主に性能上の理由
これが後々まで影響を及ぼす
例:jailやDocker(コンテナ型仮想化)において、ネットワークの制約の
ための枠組みが別途必要になる
10/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
サーバクライアントモデル
ネットワーク通信のモデル化の一つ
vs Peer to Peer
サーバ
サービスを提供する側
要求を待って処理した結果を返す
クライアント
サービスを利用する側
サーバに接続し要求を出し処理結果を受け取る
11/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
サーバクライアントの通信の流れ
12/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
サーバ側の処理の流れ
socket(2)でsocketを作る
bind(2)でsocketに待受アドレスとポートを指定する
listen(2)で待受開始
select(2)で複数ソケットのイベントループを回す
接続があったらaccept(2)で受信して通信用ソケット
を得る
read(2), write(2)で送受信
13/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
socket(2)
ソケットを作成するsystemcall
ソケットを表すファイルデスクリプタを返す
int socket(int domain, int type, int protocol);
domain: PF_INET, PF_INET6, ....
type: SOCK_STREAM, SOCK_DGRAM, ..
protocol: IPPROTO_IP, ...
14/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
bind(2)
ソケットに名前をつける
名前とは:
IPアドレス
ポート番号
int bind(int s, const struct sockaddr *addr, socklen_t addrlen);
第2引数で名前=アドレスを指定する
15/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
listen(2)
接続を待ち受ける
int listen(int s, int backlog);
backlogは最大保留数
16/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
accept(2)
TCPの接続を受けつけ、dynamic portを割り当てる
int accept(int s, struct sockaddr * restrict addr,
socklen_t * restrict addrlen);
17/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
イベントドリブン型プログラミング
外部からのイベントを契機に処理を駆動するプログラミ
ングモデル
イベント待ち受け
イベント受信
ディスパッチ
if文の羅列による分岐
ハンドラ/ジャンプテーブル
タイマ処理と合わせて擬似スレッドも実現できる
18/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
select(2)システムコール
fd_set: fdの集合(ビットマップ)
read/write/errでチェックするfdを指定(複数可)
selectで待つ
fd_setにイベントが発生したsocketのfdがセットされる
19/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
典型的なselect loop
sock = accept(....);
for (;;) {
FD_ZERO(&fds);
FD_SET(sock, &fds);
result = select(getdtablesize(),&fds,NULL,NULL, NULL);
if (FD_ISSET(sock,&fds)) {
**** read from socket ******
}
}
20/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
多重待受のselect loop
listenしているポートもselectの対象にする
listen(lsock, ....);
for (;;) {
FD_ZERO(&fds);
FD_SET(lsock, &fds);
if (sock >= 0) {FD_SET(sock, &fds);}
result = select(getdtablesize(),&fds,NULL,NULL, NULL);
if (FD_ISSET(lsock,&fds)) {
sock = accept(lsock, ...);
continue;
}
if (sock >= 0 && FD_ISSET(sock,&fds)) {
**** read from socket ******
}
}
21/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
クライアント側の処理の流れ
socket(2)でsocketを作る
bind(2)でsocketに通信先アドレスとポートを指定す
る
connect(2)で接続する
read(2), write(2)で送受信
22/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
connect(2)
ソケット経由で接続開始する
int connect(int s, const struct sockaddr *name, socklen_t namelen);
23/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
readn, writen
socketからの読み書きは1度のread/writeで完了す
る保証がない
指定した長さが完了するまでread/writeを繰り返す
必要がある
readn() / writen() 関数を使う
cf. Stevens' UNIX Network Programming
ただしブロックしてしまうので注意が必要
24/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
sample code of writen:
int
writen(int fd,char *ptr,size_t size)
{
int i;
while(size > 0) {
if ((i = write(fd,ptr,size)) < 0) return i;
ptr += i;
size -= i;
}
return size;
}
25/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
UDPとTCPのプログラミングの違い
UDPクライアントはbindしなくていい(してもいい)
UDPクライアントはconnectしなくていい(してもいい)
UDPはsendto(2)/recvfrom(2)で相手を指定して
送受信できる
connectしていればread/writeでもよい
26/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
文字列からIPアドレスを得る古い方法
inet_aton
IPアドレスの文字列を数値に変換する
gethostbyname
ホスト名からIPアドレスを得る
gethostbyname2
AddressFamilyを指定してホスト名からIPアドレスを得る
getservbyname
サービス名からポート番号を得る
27/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
IPアドレスを得る新しい方法
getaddrinfoを使ったクライアントの例
getaddrinfo(peer, buf, &hints, &res0));
s = -1;
for (res = res0; res; res = res->ai_next) {
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s < 0) continue;
connect(s, res->ai_addr, res->ai_addrlen);
break;
}
28/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
ソケットが使っているアドレスを得る方法
getsockname
ソケットの自分側のsockaddrを得る
getpeername
ソケットの相手側のsockaddrを得る
29/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
poll, libevent
selectを使った実装は重くなる
fdごとにif文でチェックするため
poll(struct pollfd fds[], nfds_t nfds, int
timeout);
チェックのコストが低い
libevent
イベントハンドラを記述するだけ
socket以外のイベントも監視できる
30/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
タイマ処理
タイマ割り込みによる定期的な処理
alarm(unsigned int seconds);
setitimer(int which, .....);
selectのtimeoutを用いてタイマ処理を実装すること
もある
31/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
selectとsignal
signalを受信してselectを抜ける場合があるので注
意
タイマ割り込みなど
selectがエラーで終了したら、errnoの値を調べる必要がある
do {
result = select(getdtablesize(),&fds,&wfds,NULL, &tv2);
} while((result < 0) && (errno == EINTR));
32/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
bind: address already in use
bindしようとしたら、他のプロセスが既にportを使って
いる
プロセスが異常終了した後、サーバーを再起動すると
出ることがある
setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&sockopt, sizeof(sockopt));
33/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
setsockopt
socketの動作を設定する
SO_REUSEADDR enables local address reuse
SO_REUSEPORT enables duplicate address and port bindings
SO_KEEPALIVE enables keep connections alive
SO_DONTROUTE enables routing bypass for outgoing messages
SO_LINGER linger on close if data present
.....
34/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
non blocking socket
複数のサーバに同時に非同期にconnectしたい場合
サーバとクライアントを兼ねている場合
e.g. bgpd, P2P application
connectでblockしない
f = fcntl(s, F_GETFL);
fcntl(s, F_SETFL, f | O_NONBLOCK);
35/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
自分のIPアドレスを得る方法
ダメな例
gethostname() → gethostbyname()
多くの場合は127.0.0.1になる
実用的な例
UDP socketを作る
遠くのアドレスに向けてconnectする
getsocknameする
ただし経路がない場合はこの方法は使えない
36/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
struct sockaddr
異なるプロトコルのアドレスを格納するための構造体
struct sockaddr {
unsigned char sa_len; /* total length */
sa_family_t sa_family; /* address family */
char sa_data[14]; /* actually longer; address value */
};
OSの違いによるsa_lenの有無に注意が必要
領域を確保するためには、sockaddr_storageを使う
37/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
UNIX domain socket
いわゆる名前付きパイプ
単一ホスト内の通信に使う
PF_UNIX or PF_LOCAL
bindする時にパス名を使う
実際にファイルが作成される
e.g. try, ls -la /tmp/
38/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
socketpair(2)
名前なしパイプ
連結した二つのsocketを返す
パイプと同様に使える
双方向に通信可能
39/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
コネクション切断を識別する
selectでreadableなのにreadすると0byte
→ connectionが切れている
この処理をしないとselect loopが回り続けるので注
意
40/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
inetd: Internet super daemon
クライアントからの待ち受けをinetdが行う
接続があったら、プロセスに引き渡す
socketがプロセスの標準入出力に割り当てられる
標準入出力のI/Oだけでサーバが記述できる
41/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
daemon化するということ
daemon化には決まった手順がある
2度forkした後、途中のprocessを終了させ、initの子供になる
標準入出力を/dev/nullに割り当てる
制御端末を切り離す
通常はroot directoryに移動する
see daemon(3)
42/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
raw socket
IPペイロードを直接入出力できる
ICMP, OSPFなどで使われている
see ping
root権限が必要
よって、pingはsetuidされている
43/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
BPF: Berkley Packet Filter
生のEthernet Frameを送受信できる
/dev/bpf?をopenする
ioctlでインタフェースを割り当てる
readするとBPFヘッダがついてくる
複数のパケットが読める場合もある
writeするとそのままEthernet I/Fに出て行く
read時にfilterを記述できる
filterは「状態マシン」
44/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
Packet Socket : LinuxでL2のパケットを
送受信する方法
PF_PACKET, SOCK_RAWを指定してsocketを作
る
L2のパケットを直接送受信できる
インタフェースはbind()で割り当てる
簡単なフィルタはあるが、主にPPPoE用と思われる
送受信はread()/write()で行う
sock = socket(PF_PACKET, SOCK_RAW, 0);
45/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
パケット構造設計の注意点
独自でバイナリ形式のプロトコルを設計する場合
バージョン番号はつけること
パケットの長さは先頭部分で明示したほうがよい
メモリ領域の確保のため
ヘッダとボディとに分けるとよい
ボディはTLV(Type, Length, Value)でいいかもしれない
46/47
(c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6
Where should you start from?
inetd
1 session server and client
multi-session server
and more...
47/47

More Related Content

PPTX
ゲームAI・実装事例の紹介
PDF
ユーザーインタビューするときは、どうやらゾンビのおでましさ
PDF
正しいものを正しくつくる
PDF
ユーザーにうれしいチャットボットのUX 7原則 - 7 Principles to Design UX of Chatbots
PDF
実践イカパケット解析α
PDF
初心者向けCTFのWeb分野の強化法
PDF
リアクティブプログラミングとMVVMパターンについて
PDF
まじめに!できる!LT
ゲームAI・実装事例の紹介
ユーザーインタビューするときは、どうやらゾンビのおでましさ
正しいものを正しくつくる
ユーザーにうれしいチャットボットのUX 7原則 - 7 Principles to Design UX of Chatbots
実践イカパケット解析α
初心者向けCTFのWeb分野の強化法
リアクティブプログラミングとMVVMパターンについて
まじめに!できる!LT

What's hot (20)

PDF
「ユーザーを理解するって言うほどカンタンじゃないよね」 UXデザイン・UXリサーチをもう一度ちゃんと理解しよう!
PDF
Scapyで作る・解析するパケット
PDF
心理的安全性を 0から80ぐらいに上げた話
PDF
Findy(ファインディ) スタートアップが取り組むべき採用課題まとめ(成長フェーズ別)
PDF
Oss貢献超入門
PDF
45分間で「ユーザー中心のものづくり」ができるまで詰め込む
PDF
はじめてのPRD
PDF
あなたの手元の本よりいい方法がある! UXデザインのプロはこうやってユーザーのインサイトを確実に見つける
PPTX
アカツキ_Cedec2021_「モバイルゲーム事業で大規模スクラム(LeSS)を導入するまでの1年間とその後」
PDF
「UXデザインとは」からはじめる「本流」のUXデザインはじめの一歩 | UXデザイン基礎セミナー 第1回
PDF
良いプレゼン 良いスライド
PDF
【2023年版】UXデザインの資格ってどんなの? HCD-Net認定 人間中心設計スペシャリスト・人間中心設計専門家
PDF
“UXデザイン”のキモ『ユーザーインタビュー』の具体的テクニックを詳解!| UXデザイン基礎セミナー 第2回
PDF
C++ マルチスレッドプログラミング
PPTX
PSR-1 と PSR-2 を 5分でざっくり理解する
PDF
UXデザインの資格ってどんなの? HCD-Net認定 人間中心設計スペシャリスト・人間中心設計専門家
PDF
Holographic Whisper - CHI2017 oral presentation by Yoichi Ochiai
PDF
多機能ボイチャを簡単に導入する方法
PDF
IT推進に必要なUXを学ぶ ~UXを意識して真の業務改善・真のDXを目指す~
PPTX
決定版:サービスの盛り上がり具合をユーザの数(DAU)から読み解く方法
「ユーザーを理解するって言うほどカンタンじゃないよね」 UXデザイン・UXリサーチをもう一度ちゃんと理解しよう!
Scapyで作る・解析するパケット
心理的安全性を 0から80ぐらいに上げた話
Findy(ファインディ) スタートアップが取り組むべき採用課題まとめ(成長フェーズ別)
Oss貢献超入門
45分間で「ユーザー中心のものづくり」ができるまで詰め込む
はじめてのPRD
あなたの手元の本よりいい方法がある! UXデザインのプロはこうやってユーザーのインサイトを確実に見つける
アカツキ_Cedec2021_「モバイルゲーム事業で大規模スクラム(LeSS)を導入するまでの1年間とその後」
「UXデザインとは」からはじめる「本流」のUXデザインはじめの一歩 | UXデザイン基礎セミナー 第1回
良いプレゼン 良いスライド
【2023年版】UXデザインの資格ってどんなの? HCD-Net認定 人間中心設計スペシャリスト・人間中心設計専門家
“UXデザイン”のキモ『ユーザーインタビュー』の具体的テクニックを詳解!| UXデザイン基礎セミナー 第2回
C++ マルチスレッドプログラミング
PSR-1 と PSR-2 を 5分でざっくり理解する
UXデザインの資格ってどんなの? HCD-Net認定 人間中心設計スペシャリスト・人間中心設計専門家
Holographic Whisper - CHI2017 oral presentation by Yoichi Ochiai
多機能ボイチャを簡単に導入する方法
IT推進に必要なUXを学ぶ ~UXを意識して真の業務改善・真のDXを目指す~
決定版:サービスの盛り上がり具合をユーザの数(DAU)から読み解く方法
Ad

Viewers also liked (20)

PPTX
ESJ62自由集会・オープンデータからオープンサイエンスへ
PDF
Windowsのパケットモニタ作成
PDF
日曜プログラマーが
1週間くらいで通信対戦ゲームを作ってみた
PDF
Pythonによる非同期プログラミング入門
PDF
PHPで大規模ブラウザゲームを開発してわかったこと
PDF
LLDP機能の概要
ODP
カーネルモジュールプログラミング超入門 #1(仮)
PDF
STPとその仲間たち 〜 STP, RSTP, MSTPの概要 〜
PDF
究極のゲーム用通信プロトコルを探せ!
PPTX
Tremaとtrema edgeの違い
PDF
Open vSwitchソースコードの全体像
PDF
ニューロンになってみる
PDF
Jpug study-pq 20170121
PDF
MySQLやSSDとかの話 後編
PPTX
RPGにおけるイベント駆動型の設計と実装
PPTX
Wiresharkの解析プラグインを作る ssmjp 201409
PDF
Wireshark だけに頼らない! パケット解析ツールの紹介
PPTX
パケットジェネレータipgenから見るnetmap
PDF
CPUに関する話
PDF
Advanced technic for OS upgrading in 3 minutes
ESJ62自由集会・オープンデータからオープンサイエンスへ
Windowsのパケットモニタ作成
日曜プログラマーが
1週間くらいで通信対戦ゲームを作ってみた
Pythonによる非同期プログラミング入門
PHPで大規模ブラウザゲームを開発してわかったこと
LLDP機能の概要
カーネルモジュールプログラミング超入門 #1(仮)
STPとその仲間たち 〜 STP, RSTP, MSTPの概要 〜
究極のゲーム用通信プロトコルを探せ!
Tremaとtrema edgeの違い
Open vSwitchソースコードの全体像
ニューロンになってみる
Jpug study-pq 20170121
MySQLやSSDとかの話 後編
RPGにおけるイベント駆動型の設計と実装
Wiresharkの解析プラグインを作る ssmjp 201409
Wireshark だけに頼らない! パケット解析ツールの紹介
パケットジェネレータipgenから見るnetmap
CPUに関する話
Advanced technic for OS upgrading in 3 minutes
Ad

Similar to 60分でわかるソケットプログラミング (20)

PPT
V6prog OSC2013Hokkaido
PDF
[Basic 6] DNS / ソケット通信 / その他
PDF
Node.js入門
PDF
Ethernetの受信処理
PDF
Rps・rfs等最新linux kernel事例
PPT
20060520.tcp
PPTX
Enshu3
PDF
Hokkaido.cap#2 一般的なプロトコルのパケットを覗いてみよう
PDF
ネットワーク通信入門
PDF
第7回勉強会 ネットワークの基礎
PPTX
Mmo game networking_1
ODP
Programming under capability mode
PDF
Scapy presentation Remake(訂正)
KEY
2012-09-09.nagoyapm07
PDF
サイボウズ・ラボユース成果報告会
PDF
Kyoto Tycoon Guide in Japanese
PDF
ネットワークコマンド入力に対応したツール事例
PDF
Trema day 1
PPTX
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
PDF
Scapy presentation
V6prog OSC2013Hokkaido
[Basic 6] DNS / ソケット通信 / その他
Node.js入門
Ethernetの受信処理
Rps・rfs等最新linux kernel事例
20060520.tcp
Enshu3
Hokkaido.cap#2 一般的なプロトコルのパケットを覗いてみよう
ネットワーク通信入門
第7回勉強会 ネットワークの基礎
Mmo game networking_1
Programming under capability mode
Scapy presentation Remake(訂正)
2012-09-09.nagoyapm07
サイボウズ・ラボユース成果報告会
Kyoto Tycoon Guide in Japanese
ネットワークコマンド入力に対応したツール事例
Trema day 1
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
Scapy presentation

60分でわかるソケットプログラミング

  • 1. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 60分でわかるソケットプログラミン グ 60分でわかるソケットプログラミン グ 木本雅彦 <kimoto@soum.co.jp> <kimoto@ohnolab.org> 株式会社創夢 第三開発部 シニアプロジェクトマネージャ 初版:2010年4月作成、改定版:2015年3月作成
  • 2. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 創夢とはどういう会社か UNIXとTCP/IPネットワークに強い会社 全員がUNIXに詳しい 全員がIPに詳しい ということは → 全社員がネットワークプログラミングくら い出来るよね!? ということで、60分レクチャーです 1/47
  • 3. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 この発表での前提条件 C言語でのプログラミングはできる system call programming file I/O basics TCP/IPの基礎は理解している OSI階層モデル IPアドレス、ヘッダの構造など 2/47
  • 4. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 IPというプロトコルについて IP = InternetProtocol はじめに: ホストにはIPアドレスがついているらしいぜ (以下略。知らない人は自力で勉強してください) 3/47
  • 5. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 TCPとUDPというプロトコル TCP: Transmission Control Protocol 送達確認(ACK)の利用による信頼性のある通信 輻輳制御による「フェアな」通信量の制御 UDP: User Datagram Protocol 信頼性のない通信 パケット単位でデータを投げるだけ 4/47
  • 6. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 port番号 TCP, UDPで用いる通信口の番号 符号なし16bit 通信相手のアプリケーションを同定する 5/47
  • 7. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 well-known portとephemeral port well-known port 代表的なサービスに割り当てられた番号 0 - 1023 IANAが管理している ephemeral port 使い捨てポート RFCでのdynamic port: 49152〜65535 Linuxの実装: 32768〜61000 古いBSDの実装: 1024〜4999 6/47
  • 8. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 一応IPv6について、ひとこと 次世代インターネットプロトコル といいつつ、すでに実用化されている IPアドレスが128bitに拡張されたという一点がもっとも 重要 アドレスの自動設定 マルチキャストの積極的な利用 7/47
  • 9. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 UNIXのプロセス間通信 pipe 双方向パイプ FIFO 共有メモリ(SYSV由来) セマフォ(SYSV由来) signal socket(BSD由来) 8/47
  • 10. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 socket 4.2BSDで導入されたTCP/IPの抽象化および実装 通信路の片方の末端を指し示す ファイルデスクリプタで表される →ネットワーク通信をファイルI/Oと同様に記述できる 9/47
  • 11. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 余談:socketという敗北 4.2BSDでsocketが導入されたのは、従来のUNIXの ようにファイルシステム上にネットワークI/Oをマッピングで きなかったため 主に性能上の理由 これが後々まで影響を及ぼす 例:jailやDocker(コンテナ型仮想化)において、ネットワークの制約の ための枠組みが別途必要になる 10/47
  • 12. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 サーバクライアントモデル ネットワーク通信のモデル化の一つ vs Peer to Peer サーバ サービスを提供する側 要求を待って処理した結果を返す クライアント サービスを利用する側 サーバに接続し要求を出し処理結果を受け取る 11/47
  • 13. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 サーバクライアントの通信の流れ 12/47
  • 14. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 サーバ側の処理の流れ socket(2)でsocketを作る bind(2)でsocketに待受アドレスとポートを指定する listen(2)で待受開始 select(2)で複数ソケットのイベントループを回す 接続があったらaccept(2)で受信して通信用ソケット を得る read(2), write(2)で送受信 13/47
  • 15. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 socket(2) ソケットを作成するsystemcall ソケットを表すファイルデスクリプタを返す int socket(int domain, int type, int protocol); domain: PF_INET, PF_INET6, .... type: SOCK_STREAM, SOCK_DGRAM, .. protocol: IPPROTO_IP, ... 14/47
  • 16. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 bind(2) ソケットに名前をつける 名前とは: IPアドレス ポート番号 int bind(int s, const struct sockaddr *addr, socklen_t addrlen); 第2引数で名前=アドレスを指定する 15/47
  • 17. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 listen(2) 接続を待ち受ける int listen(int s, int backlog); backlogは最大保留数 16/47
  • 18. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 accept(2) TCPの接続を受けつけ、dynamic portを割り当てる int accept(int s, struct sockaddr * restrict addr, socklen_t * restrict addrlen); 17/47
  • 19. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 イベントドリブン型プログラミング 外部からのイベントを契機に処理を駆動するプログラミ ングモデル イベント待ち受け イベント受信 ディスパッチ if文の羅列による分岐 ハンドラ/ジャンプテーブル タイマ処理と合わせて擬似スレッドも実現できる 18/47
  • 20. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 select(2)システムコール fd_set: fdの集合(ビットマップ) read/write/errでチェックするfdを指定(複数可) selectで待つ fd_setにイベントが発生したsocketのfdがセットされる 19/47
  • 21. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 典型的なselect loop sock = accept(....); for (;;) { FD_ZERO(&fds); FD_SET(sock, &fds); result = select(getdtablesize(),&fds,NULL,NULL, NULL); if (FD_ISSET(sock,&fds)) { **** read from socket ****** } } 20/47
  • 22. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 多重待受のselect loop listenしているポートもselectの対象にする listen(lsock, ....); for (;;) { FD_ZERO(&fds); FD_SET(lsock, &fds); if (sock >= 0) {FD_SET(sock, &fds);} result = select(getdtablesize(),&fds,NULL,NULL, NULL); if (FD_ISSET(lsock,&fds)) { sock = accept(lsock, ...); continue; } if (sock >= 0 && FD_ISSET(sock,&fds)) { **** read from socket ****** } } 21/47
  • 23. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 クライアント側の処理の流れ socket(2)でsocketを作る bind(2)でsocketに通信先アドレスとポートを指定す る connect(2)で接続する read(2), write(2)で送受信 22/47
  • 24. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 connect(2) ソケット経由で接続開始する int connect(int s, const struct sockaddr *name, socklen_t namelen); 23/47
  • 25. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 readn, writen socketからの読み書きは1度のread/writeで完了す る保証がない 指定した長さが完了するまでread/writeを繰り返す 必要がある readn() / writen() 関数を使う cf. Stevens' UNIX Network Programming ただしブロックしてしまうので注意が必要 24/47
  • 26. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 sample code of writen: int writen(int fd,char *ptr,size_t size) { int i; while(size > 0) { if ((i = write(fd,ptr,size)) < 0) return i; ptr += i; size -= i; } return size; } 25/47
  • 27. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 UDPとTCPのプログラミングの違い UDPクライアントはbindしなくていい(してもいい) UDPクライアントはconnectしなくていい(してもいい) UDPはsendto(2)/recvfrom(2)で相手を指定して 送受信できる connectしていればread/writeでもよい 26/47
  • 28. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 文字列からIPアドレスを得る古い方法 inet_aton IPアドレスの文字列を数値に変換する gethostbyname ホスト名からIPアドレスを得る gethostbyname2 AddressFamilyを指定してホスト名からIPアドレスを得る getservbyname サービス名からポート番号を得る 27/47
  • 29. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 IPアドレスを得る新しい方法 getaddrinfoを使ったクライアントの例 getaddrinfo(peer, buf, &hints, &res0)); s = -1; for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) continue; connect(s, res->ai_addr, res->ai_addrlen); break; } 28/47
  • 30. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 ソケットが使っているアドレスを得る方法 getsockname ソケットの自分側のsockaddrを得る getpeername ソケットの相手側のsockaddrを得る 29/47
  • 31. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 poll, libevent selectを使った実装は重くなる fdごとにif文でチェックするため poll(struct pollfd fds[], nfds_t nfds, int timeout); チェックのコストが低い libevent イベントハンドラを記述するだけ socket以外のイベントも監視できる 30/47
  • 32. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 タイマ処理 タイマ割り込みによる定期的な処理 alarm(unsigned int seconds); setitimer(int which, .....); selectのtimeoutを用いてタイマ処理を実装すること もある 31/47
  • 33. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 selectとsignal signalを受信してselectを抜ける場合があるので注 意 タイマ割り込みなど selectがエラーで終了したら、errnoの値を調べる必要がある do { result = select(getdtablesize(),&fds,&wfds,NULL, &tv2); } while((result < 0) && (errno == EINTR)); 32/47
  • 34. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 bind: address already in use bindしようとしたら、他のプロセスが既にportを使って いる プロセスが異常終了した後、サーバーを再起動すると 出ることがある setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *)&sockopt, sizeof(sockopt)); 33/47
  • 35. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 setsockopt socketの動作を設定する SO_REUSEADDR enables local address reuse SO_REUSEPORT enables duplicate address and port bindings SO_KEEPALIVE enables keep connections alive SO_DONTROUTE enables routing bypass for outgoing messages SO_LINGER linger on close if data present ..... 34/47
  • 36. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 non blocking socket 複数のサーバに同時に非同期にconnectしたい場合 サーバとクライアントを兼ねている場合 e.g. bgpd, P2P application connectでblockしない f = fcntl(s, F_GETFL); fcntl(s, F_SETFL, f | O_NONBLOCK); 35/47
  • 37. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 自分のIPアドレスを得る方法 ダメな例 gethostname() → gethostbyname() 多くの場合は127.0.0.1になる 実用的な例 UDP socketを作る 遠くのアドレスに向けてconnectする getsocknameする ただし経路がない場合はこの方法は使えない 36/47
  • 38. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 struct sockaddr 異なるプロトコルのアドレスを格納するための構造体 struct sockaddr { unsigned char sa_len; /* total length */ sa_family_t sa_family; /* address family */ char sa_data[14]; /* actually longer; address value */ }; OSの違いによるsa_lenの有無に注意が必要 領域を確保するためには、sockaddr_storageを使う 37/47
  • 39. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 UNIX domain socket いわゆる名前付きパイプ 単一ホスト内の通信に使う PF_UNIX or PF_LOCAL bindする時にパス名を使う 実際にファイルが作成される e.g. try, ls -la /tmp/ 38/47
  • 40. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 socketpair(2) 名前なしパイプ 連結した二つのsocketを返す パイプと同様に使える 双方向に通信可能 39/47
  • 41. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 コネクション切断を識別する selectでreadableなのにreadすると0byte → connectionが切れている この処理をしないとselect loopが回り続けるので注 意 40/47
  • 42. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 inetd: Internet super daemon クライアントからの待ち受けをinetdが行う 接続があったら、プロセスに引き渡す socketがプロセスの標準入出力に割り当てられる 標準入出力のI/Oだけでサーバが記述できる 41/47
  • 43. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 daemon化するということ daemon化には決まった手順がある 2度forkした後、途中のprocessを終了させ、initの子供になる 標準入出力を/dev/nullに割り当てる 制御端末を切り離す 通常はroot directoryに移動する see daemon(3) 42/47
  • 44. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 raw socket IPペイロードを直接入出力できる ICMP, OSPFなどで使われている see ping root権限が必要 よって、pingはsetuidされている 43/47
  • 45. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 BPF: Berkley Packet Filter 生のEthernet Frameを送受信できる /dev/bpf?をopenする ioctlでインタフェースを割り当てる readするとBPFヘッダがついてくる 複数のパケットが読める場合もある writeするとそのままEthernet I/Fに出て行く read時にfilterを記述できる filterは「状態マシン」 44/47
  • 46. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 Packet Socket : LinuxでL2のパケットを 送受信する方法 PF_PACKET, SOCK_RAWを指定してsocketを作 る L2のパケットを直接送受信できる インタフェースはbind()で割り当てる 簡単なフィルタはあるが、主にPPPoE用と思われる 送受信はread()/write()で行う sock = socket(PF_PACKET, SOCK_RAW, 0); 45/47
  • 47. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 パケット構造設計の注意点 独自でバイナリ形式のプロトコルを設計する場合 バージョン番号はつけること パケットの長さは先頭部分で明示したほうがよい メモリ領域の確保のため ヘッダとボディとに分けるとよい ボディはTLV(Type, Length, Value)でいいかもしれない 46/47
  • 48. (c) Masahiko KIMOTO, Ph.D. - http://www.earthlight.jp/ Powered by Rabbit 2.1.6 Where should you start from? inetd 1 session server and client multi-session server and more... 47/47