---
# System prepended metadata

title: I2実験 考察レポート

---

# I2実験 考察レポート
<div style='text-align: right'>
03-190421<br>
工学部電子情報工学科B3<br>
近藤 佑亮</div>

## 考察

### ifconfig
準備課題 5.2で、ifconfigを用いて自分のアドレスを調べた。
このとき、`ifconfig`と `grep` とパイプすることで、楽にIPv4アドレスを見つけることができる。
```shell=
$ ifconfig | grep inet
    inet 172.22.0.1  netmask 255.255.0.0  broadcast 172.22.255.255
    inet6 fe80::42:96ff:febe:6a72  prefixlen 64  scopeid 0x20<link>
    inet 172.19.0.1  netmask 255.255.0.0  broadcast 172.19.255.255
    inet 172.21.0.1  netmask 255.255.0.0  broadcast 172.21.255.255
    inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
    inet 127.0.0.1  netmask 255.0.0.0
    inet6 ::1  prefixlen 128  scopeid 0x10<host>
    inet6 fe80::1c3e:9dff:fe2f:4e4a  prefixlen 64  scopeid 0x20<link>
    inet6 fe80::709b:15ff:fe9b:a44  prefixlen 64  scopeid 0x20<link>
    inet6 fe80::30ab:7ff:feb7:f28d  prefixlen 64  scopeid 0x20<link>
    inet 10.209.3.67  netmask 255.255.240.0  broadcast 10.209.15.255
    inet6 fe80::a19c:314a:d355:cf32  prefixlen 64  scopeid 0x20<link>
```

ちなみに、実験テキスト[^1]には無線LANのインターフェースがwlan0になっているはずと記述されているが、実際にはそうとは限らない。

### IP通信ができるための条件
IP通信ができるための条件は以下の3つである。
1. IPアドレスが割り当てられていること
2. ホストが属するサブネットの情報が設定されていること
3. default gatewayが正しく設定されていること

### UDPソケットを用いたデータ送信
選択課題6.2で、UDPソケットを用いてデータ送信をするスクリプトを実装した。
```c=
//本課題6.1 のclient_recv.cを改良した
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
    int s = socket(PF_INET, SOCK_DGRAM, 0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    addr.sin_port = htons(atoi(argv[2]));
    socklen_t addrlen;
    int ret = connect(s, (struct sockaddr *)&addr, sizeof(addr));
    if (ret != 0) {
        fprintf(stderr, "connection failed!!\n");
        exit(0);
    }
    
    char send_data[1];
    int n;
    send_data[0] = 'a';
    while ((n = read(
        0,
        send_data,
        sizeof(send_data)
    )) != 0) {
        int snd = sendto(s,
            send_data,
            sizeof(send_data),
            0,
            (struct sockaddr * )&addr,
            sizeof(addr)
        );
        if (snd == -1) {
            fprintf(stderr, "send error\n");
            exit(0);
        }
    }
    
    fprintf(stderr, "datasend finished.\n");
    shutdown(s, SHUT_WR);
    
    char received_data[60];
    addrlen = sizeof(addr);
    int r;
    fprintf(stderr, "start receiving\n");
    
    while((r = recvfrom(
        s,
        received_data,
        sizeof(received_data),
        0,
        (struct sockaddr * )&addr,
        &addrlen
    )) > 0 ) {
        fprintf(stderr, "hoge\n");
        int wrt = write(
            1,
            received_data, 
            sizeof(received_data)
        );
        if (wrt == -1) {
            fprintf(stderr, "write error!\n");
            exit(0);
        }
        fprintf(stderr, "receiving\n");
    }
    fprintf(stderr, "receiving finished.\n");
    return 0;
}
```

### serv_sendの実装
本課題8.1に相当するserv_send.cの実装を行った。
```c=
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
    if (argc != 2) {
        fprintf(stderr, "please input port number\n");
        exit(0);
    }
    int port_number = atoi(argv[1]);

    int ss = socket(PF_INET, SOCK_STREAM, 0);

    // bind server socket
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port_number);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(ss, (struct sockaddr *)&addr, sizeof(addr));

    // listen
    fprintf(stderr, "started listening\n");
    listen(ss, 10);

    // accept
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(struct sockaddr_in);
    fprintf(stderr, "started accepting\n");
    int s = accept(
        ss, 
        (struct sockaddr *)&client_addr,
        &len
    );
    if (s < 0) {
        fprintf(stderr, "connection failed!!\n");
        exit(0);
    }
    fprintf(stderr, "accepted\n");
    close(ss);

    char send_data[1];
    int n;
    while ((n = read(
        0,
        send_data,
        sizeof(send_data)
    )) != 0) {
        int snd = send(s, send_data, sizeof(send_data), 0);
        if(snd == -1) {
            fprintf(stderr, "send error\n");
            exit(0);
        }
    }
    fprintf(stderr, "datasend finished.\n");
    close(s);
    
    return 0;
}
```

### serv_sendの遅延を減らす
`$ rec -t raw -b 16 -c 1 -e s -r 44100 - | ./serv_send PORT_NUMBER`で音声を送信すると、
1. ./serv_sendはacceptで待っている
2. recは既に録音を開始している

という状況が発生する。

このとき、パイプのためのバッファ領域にデータがあふれるまで、recは標準出力に音声を吐き出し続けてしまう。
これによって、`./serv_send`が受け取るデータはrec起動直後のものになり、遅延が生じてしまう。

解決策として、自分たちは __接続をacceptしてからrecを起動する__ という方針を選択した。
実装には __popen__ というライブラリ関数を用いた。

```c=
//本課題8.1に対応する
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <string.h>
#define BUF 256


int main(int argc, char* argv[]){
    if (argc != 2) {
        fprintf(stderr, "don't input port number\n");
        exit(0);
    }
    int port_number = atoi(argv[1]);
    int ss = socket(PF_INET, SOCK_STREAM, 0);
    
    // bind server socket
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port_number);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(ss, (struct sockaddr *)&addr, sizeof(addr));

    // listen
    listen(ss, 10);

    // accept
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(struct sockaddr_in);
    int s = accept(
        ss, 
        (struct sockaddr *)&client_addr,
        &len
    );

    if (s < 0) {
        fprintf(stderr, "connection failed!!\n");
        exit(0);
    }
    fprintf(stderr, "accepted\n");
    close(ss);

    // send data
    FILE *fp;
    // 実行するコマンド cmdline[]
    char *cmdline = "rec -t raw -b 16 -c 1 -e s -r 44100 -";
    // popenでcmdlineを実行する
    if ((fp = popen(cmdline, "r")) == NULL) {
        err(EXIT_FAILURE, "%s", cmdline);
    }

   char send_data[1];
   int n;
   // popenで開いたファイルポインタfpからデータを読み取る
   while ((n = fread(
       send_data, 
       sizeof(char),
       1, 
       fp
   )) != 0) {
       int snd = send(
           s,
           send_data, 
           sizeof(send_data),
           0
       );
       if(snd == -1) {
           fprintf(stderr, "send error\n");
           exit(0);
       }
   }
   fprintf(stderr, "datasend finished.\n");
   close(s);
   return 0;
}
```

### 基本的なインターネット電話
双方向通信を実現するため、送信と受信を交互に少しずつ繰り返すというアイデアで実装を行った。
```c=
//本課題8.1に対応する
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <string.h>
#include <stdbool.h>
#define BUF 4096

int main(int argc, char* argv[]){
    // build socket
    int s;
    if (argc == 2) {
        int ss = socket(PF_INET, SOCK_STREAM, 0);
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(atoi(argv[1]));
        addr.sin_addr.s_addr = INADDR_ANY;
        bind(ss, (struct sockaddr *)&addr, sizeof(addr));
        listen(ss, 10);
        struct sockaddr_in client_addr;
        socklen_t len = sizeof(struct sockaddr_in);
        s = accept(ss, (struct sockaddr *)&client_addr, &len);
        if (s < 0) {
            fprintf(stderr, "connection failed!!\n");
            exit(0);
        }
        close(ss);
    } else if (argc == 3) {
        s = socket(PF_INET, SOCK_STREAM, 0);
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(argv[1]);
        addr.sin_port = htons(atoi(argv[2]));
        int ret = connect(
            s, 
            (struct sockaddr *)&addr,
            sizeof(addr)
        );
        if (ret != 0) {
            fprintf(stderr, "connection failed !!!\n");
        }
    } else {
        fprintf(stderr, "please check args\n");
    }

    char send_data[BUF];
    char recieved_data[BUF];
    int n, r;
    while (true) {
        // sending sizeof(send_data) byte
        n = read(0, send_data, sizeof(send_data));
        if (n > 0) {
            int snd = send(
                s,
                send_data,
                sizeof(send_data),
                0
        );
            if(snd == -1) {
                fprintf(stderr, "send error\n");
                exit(0);
            }
        }
        // recieving sizeof(send_data) byte
        r = recv(s, recieved_data, sizeof(recieved_data), 0);
        if (r > 0) {
            int wrt = write(
                1,
                recieved_data,
                sizeof(recieved_data)
            );
            if(wrt == -1){
                fprintf(stderr, "write error!\n");
                exit(0);
            }
        }
    }
    fprintf(stderr, "datasend finished.\n");
    close(s);
    return 0;
}
```




## 参考文献
[^1]: 東京大学工学部 「電気電子情報（前期）実験テキスト」 2019年4月