[実習] TCPプログラミング

1 Star2 Stars3 Stars4 Stars5 Stars (まだ評価されていません)
Loading...

はじめに

講義でネットワークプログラミングをやり始めたのでノート代わりにまとめてみます。
当面は、プログラムをどう書いたとかをメモするだけで、基本概念とかまで突っ込むつもりは今の所ありません。
投稿は分けずに随時編集していく予定です。
プログラミング言語はpython 2.7(研究で使っているので), c言語(講義指定)で書きます。ただし、全体ではなく一部なので参考にする場合は、読み込むライブラリや変数宣言は各自で汲み取ってください。

進め方

講義や課題に合わせて書いていきます。
現在は、下記の内容を書いてます。なお、接続先サーバーの情報に関しては先生に公開の確認をしてみます。

  • クライアントがサーバーにTCP接続をするプログラム

クライアント

とりあえず一回繋ぐ!!

条件

  • host, portをコマンドライン引数で渡せる
  • IPv4とIPv6両対応
  • addrinfoじゃなくてsockaddr系を使います。(講義の流れ的に)

実装(python:github)

  • TCP通信の仕方:IPv4
    socketモジュールを使う。使い方はここより引用
    socket.AF_INETがIPv4プロトコルの指定で、SOCK_STREAMがストリームソケット(TCP)の指定となる。IPv6の場合、AF_INET6となり、UDPの場合はSOCK_DGRAMとなる。

ipv4_simple_client.py

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with closing(sock):
sock.connect((host, port))
sock.send(b'Hello world')
print(sock.recv(bufsize))
return

  • TCP通信の仕方:IPv6
    基本的にIPv4と同じ。connectメソッドの引数が(host, port, flowinfo, scopeid)の長さ4のタプルになる。flowinfoとscopeidに何を入れたら良いのかは現状まだ理解できていない。。。

AF_INET6 アドレスファミリは (host, port, flowinfo, scopeid) の長さ4のタプルで示し、 flowinfo と scopeid にはそれぞれCの struct sockaddr_in6 における sin6_flowinfo と sin6_scope_id の値を指定します。後方互換性のため、 socket モジュールのメソッドでは sin6_flowinfo と sin6_scope_id を省略する事ができますが、 scopeid を省略するとスコープを持ったIPv6アドレスの処理で問題が発生する場合があります。

ipv6_simple_client.py

sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
with closing(sock):
sock.connect((host, port, flowninfo, scopeid))
sock.send(b'Hello world')
print(sock.recv(bufsize))
return

  • 引数の渡し方
    argparseモジュールを使う。使い方はここを参照。ポート指定は-p--portとし、host指定は-b--bindingにした(-hがhelpで埋まってて、railsでIPサーバー起動時のコマンドがrails s -b <IP Address>だったので…)

argparse_sample.py

p = argparse.ArgumentParser()
p.add_argument('-b', '--binding', default='127.0.0.1')
p.add_argument('-p', '--port', default=3000, type=int)
args = p.parse_args()
print(args.binding)
print(args.port)

  • IPv4とIPv6両対応
    注)正しい方法かはわからないです。
    socket.inet_pton(address_family, ip_string)を用いて、不正なIPアドレスの場合socket.errorが発生することを利用して、IPv4かIPv6を判定した。

confirm_address_family.py

address_family = socket.AF_INET
try:
socket.inet_pton(address_family, host)
except:
address_family = socket.AF_INET6
try:
socket.inet_pton(address_family, host)
except:
raise Exception , "invalid host. please confirm the value of -b or --binding"
finally:
print('address_family is %s' %address_family)

実装(c言語:github)

  • TCP通信の仕方:IPv4
  1. int socket(int domain, int type, int protocol);でsocketを作成する。
  2. sockaddr_in構造体にAddress Familyとportを指定する。
  3. int inet_pton(int af, const char *src, void *dst);で文字列 src を、アドレスファミリー af のネットワークアドレス構造体に変換し、dst にコピーする。
  4. int connect(int sockfd, const struct sockaddr *addr,
    socklen_t addrlen);
    でサーバに繋ぐ。

ipv4_simple_client.c

int family = AF_INET;
int sock = socket(family, SOCK_STREAM, 0);
struct sockaddr_in server;
server.sin_family = family;
server.sin_port = htons(port);
int pton = inet_pton(family, host, &server.sin_addr);
if (s != 1){
perror("invalid host.");
exit(1);
}
int  con = connect(sock, (struct sockaddr *)&server, sizeof(server));
if (con < 0){
perror("connection failure.");
exit(1);
}
char buf[244];
memset(buf, 0, sizeof(buf));
read(sock, buf, sizeof(buf));
printf("%s\n", buf);

  • TCP通信の仕方:IPv6
    基本的にIPv4と同じ。IPv6向けの構造体とかを利用するよう変更

ipv6_simple_client.c

int family = AF_INET6;
int sock = socket(family, SOCK_STREAM, 0);
struct sockaddr_in6 server;
server.sin6_family = family;
server.sin6_port = htons(port);
int pton = inet_pton(family, host, &server.sin6_addr);
if (s != 1){
perror("invalid host.");
exit(1);
}
int  con = connect(sock, (struct sockaddr *)&server, sizeof(server));
if (con < 0){
perror("connection failure.");
exit(1);
}
char buf[244];
memset(buf, 0, sizeof(buf));
read(sock, buf, sizeof(buf));
printf("%s\n", buf);

  • 引数の渡し方
    getoptを使う。使い方はここを参照。ポート指定は-pとし、host指定は-bにした。

getopt_sample.c

int result;
char *host;
int port;
while((result=getopt(argc,argv,"b:p:"))!=-1){
switch(result){
case 'b':
host = optarg;
break;
case 'p':
port = atoi(optarg);
break;
}
}

  • IPv4とIPv6両対応
    注)正しい方法かはわからないです。
    inet_ptonを用いて、成功の場合は返り値が1となることを利用して、IPv4かIPv6を判定した参考ページ。pythonと違って、inet_ptonの引数にAddress Familyに適した構造体を渡さないといけないため、いろいろややこしくなった。また、connectの第三引数のsizeはsockaddr構造体のsizeでなくsockaddr_inまたはsockaddr_in6のsizeである必要がある。詳しくはgithubのコードで。

1 Star2 Stars3 Stars4 Stars5 Stars (まだ評価されていません)
Loading...
      この投稿は審査処理中  | 元のサイトへ