hoshimure-47’s blog

プログラミング修業中の人

Pythonでローレンツアトラクタをかいたよ

Pythonの練習のためにローレンツアトラクタを描きました。

ローレンツアトラクタ(Lorenz attractor)とは? >> ローレンツ方程式 - Wikipedia

カオスの教科書の一番最初に登場するやつです。めっちゃ単純な方程式なのにパラメータによってめちゃくちゃ解の挙動が変わるところが面白いです。

前にC++で描いたことがあるんですけど、PythonだとMatplotlibとかいう描画ライブラリを使えば数値計算とグラフ描画を同時にやってくれるのがとてもありがたいですね。

パラメータ \displaystyle (p, r, b) = (10, 28, 8/3)、初期値 \displaystyle (x_0, y_0, z_0) = (1, 1, 1)でRungeKutta法を使って解いたときの結果です。

f:id:hoshimure-47:20170801072843p:plain


Pythonのコードです。まだCっぽい書き方になっちゃってます。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# const
p = 10
r = 28
b = 8/3
dt = 0.01
t_0 = 0
t_1 = 50
X_0 = np.array([1, 1, 1])


# function
def RungeKutta(t, X):
  k_1 = LorenzEquation(t, X)
  k_2 = LorenzEquation(t + dt/2, X + k_1*dt/2)
  k_3 = LorenzEquation(t + dt/2, X + k_2*dt/2)
  k_4 = LorenzEquation(t + dt, X + k_3*dt)
  X_next = X + dt/6*(k_1 + 2*k_2 + 2*k_3 + k_4)

  return X_next

def LorenzEquation(t, X):
  x = X[0]
  y = X[1]
  z = X[2]

  return np.array([-p*x + p*y, -x*z + r*x - y, x*y - b*z])

# main process
t = t_0
X = X_0
data = np.r_[X]

while t < t_1:
  X = RungeKutta(t, X)
  t += dt
  data = np.c_[data, X]

print(data)
fig = plt.figure()
ax = Axes3D(fig)

ax.plot(data[0,:], data[1,:], data[2,:])
plt.show()

比較参考までにEigenライブラリを使ったC++のときのコードです。(Lorenzのつづりが間違ってる・・・)

#include <iostream>
#include <fstream>
#include <Eigen/Core>
using namespace Eigen;

Vector3f RungeKutta(float t, Vector3f X);
Vector3f LorentzEquation(float t, Vector3f X);

const float p = 10;
const float r = 28;
const float b = 8/3;
const float dt = 0.01;
const float t_0 = 0;
const float t_1 = 10;
const Vector3f X_0(1, 1, 1);

int main() {
  float t = t_0; 
  Vector3f X = X_0;

  std::ofstream ofs;
  ofs.open("lorentz_attractor.txt", std::ios::out);
  ofs << t << " " << X.transpose() << std::endl; 

  do {
    X = RungeKutta(t, X);
    t += dt;
    ofs << t << " " << X.transpose() << std::endl; 
  } while(t < t_1);

  return 0; 
}

Vector3f RungeKutta(float t, Vector3f X) {
  Vector3f k_1 = LorentzEquation(t, X);
  Vector3f k_2 = LorentzEquation(t + dt/2, X + dt/2*k_1);
  Vector3f k_3 = LorentzEquation(t + dt/2, X + dt/2*k_2);
  Vector3f k_4 = LorentzEquation(t + dt, X + dt*k_3);
  Vector3f X_next = X + dt/6*(k_1 + 2*k_2 + 2*k_3 + k_4);

  return X_next;
}
Vector3f LorentzEquation(float t, Vector3f X) { 
  float x = X[0];
  float y = X[1];
  float z = X[2];

  Vector3f val(-p*x + p*y, -x*z + r*x - y, x*y - b*z);

  return val;  
}


今度は時刻 tによってパラメータを動的に変えたときの描画とか他のアトラクタも描いてみます。

attractorって日本語でなんて訳すのかな。

Webアプリ開発の本を買ったらソケットプログラミングさせられた話

情報系の大学院に行きたいなあと思っている今日この頃です。

タイトルは少々語弊がありますが、記事の内容はタイトルの通りです。ふと「Webアプリ作ろう!」と思って本を買いました。

この本ではWebアプリケーションの作り方を理解するために、Webサーバを作るとこから始めてくれます。”Webサーバを作る”といってもLinuxマシンにApacheをインストールしてという意味ではなく、ApacheのようなWebサーバのプログラムを作ることで、Webの動作原理から丁寧に解説してくれます。そのためにまず、TCP通信の基礎であるソケットプログラミングからはじめようといった流れになっています。

いやいや遠回りしすぎなんじゃと思う方もいらっしゃるでしょうが、実はそんなこともなくて、実際僕がこの本を買おうと思ったのもLINEのAPIを使って遊ぼうと思っていたらHTTPの処理あたりで詰まってしまってどうにもならなくなってしまったからです。今は素晴らしい時代で、簡単そうなフレームワークやツールを使えば単純なWebアプリケーションくらいなら誰でもすぐ作れますが、少しややこしいことをしようとすると上手くいかなくなって、根本を理解していないと自分一人では抜け出せないという状況に陥りがちです。

そうならないためにも、基礎からしっかりお勉強しておきたいところです。

今回の記事の流れはこんな感じになっています。

まず実際にTCPの規約に沿ったサーバとクライアントのプログラムを作って、それらのプログラムを使ってHTTPリクエストとHTTPレスポンスでは何が吐き出されているのかを確認していきたいと思います。

TCPサーバ/クライアントを作る

Webサーバ動作の理解のためにまず、ブラウザ(クライアント)とWebサーバをつなぐネットワークについて学びます。今回はインターネットで一般的に用いられているTCPプロトコルで通信を行うサーバのプログラムとクライアントのプログラムを作ります。

プロトコルとは、TCPとはという方にはまずこちらをご一読
【初心者向けに大体わかる】TCP/IPとは?

クライアントサーバシステムについてよく知らない人は検索してみてください。

この本は基本的にJavaを使いますが、僕は馴染みのあるC言語で書きました。一応、この本にもC言語のサンプルプログラムは載っていますが、解説やコメントがあまりなかったので記事に載せることにしました。

実際に行う動作の概念図がこちらです。
f:id:hoshimure-47:20170705015358p:plain
f:id:hoshimure-47:20170705015409p:plain

TCPの通信方式は電話に例えられるように、お互いの接続を確認してから通信を行うコネクション型という通信方式です。

サーバとクライアントを介する仮想的な窓口としてソケットというものを使います。ソケットという言葉は、豆電球のカポってはめる部分を指すことが多いですが、イメージとしては「統一された規格の部品をはめ込むもの」という認識でいいと思います。詳しくはその他の文献を参照してください。


プログラムのコードはこんな感じです。まずはサーバの動きをする方です。
今回、ポート番号を8001で指定しています。他のサービスと被っていなければどんな番号でもいいんですけど、本に合わせて設定しました。

// tcp_server.c
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/uio.h>

int main(int argc, char **argv)
{
  int sock;
  struct sockaddr_in addr;
  int fd;
  FILE *socket_fp;
  FILE *file_out_fp;
  FILE *file_in_fp;
  int ch;

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8001);
  addr.sin_addr.s_addr = htonl(INADDR_ANY);

  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("socket_error");
    return -1;
  }
  else {
    printf("ソケットを生成しました\n");
  }

  if((ch = bind(sock, (struct sockaddr*)&addr, sizeof(addr))) < 0){
    perror("bind_error");
    return -1;
  }
  else {
    printf("サーバの準備ができました\n");
  }

  if((ch = listen(sock, 5)) < 0){
    perror("listen_error");
    return -1;
  }
  else {
    printf("クライアントからの接続を待っています\n");
  }

  if((fd = accept(sock, NULL, NULL)) < 0)
  {
    perror("accept_error");
    return -1;
  }
  else {
    printf("クライアントを接続しました\n");
  }

  socket_fp = fdopen(fd, "r+");
  file_out_fp = fopen("server_recv.txt", "w");
  while((ch = fgetc(socket_fp)) != 0) {
    fputc(ch, file_out_fp);
  }
  fclose(file_out_fp);
  printf("クライアントからのデータを受け取りました\n");

  file_in_fp = fopen("server_send.txt", "r");
  while((ch = fgetc(file_in_fp)) != EOF){
    fputc(ch, socket_fp);
  }
  fclose(file_in_fp);
  fclose(socket_fp);
  printf("クライアントにデータを送信しました\n");
  printf("通信が正常に終了しました\n");

  return 0;
}

クライアントから受け取るファイルでは、EOFという概念が使えないみたいなので、クライアントはファイルの末尾に0を送るようにしています。

次にクライアントの動きをする方です。

// tcp_client.c
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/uio.h>
#include <netdb.h>

int main(int argc, char **argv)
{
  int sock;
  struct sockaddr_in addr;
  struct hostent *host;
  FILE *socket_fp;
  FILE *file_out_fp;
  FILE *file_in_fp;
  int ch;

  memset(&addr, 9, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8001);

  host = gethostbyname("localhost");
  memcpy(&addr.sin_addr, host->h_addr_list[0], sizeof(addr.sin_addr));

  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("socket_error");
    return -1;
  }
  else {
    printf("ソケットを生成しました\n");
  }

  if(connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){
    perror("connect_error");
    return -1;
  }
  else {
    printf("サーバと接続されました\n");
  }

  socket_fp = fdopen(sock, "r+");
  file_in_fp = fopen("client_send.txt", "r");
  while ((ch = fgetc(file_in_fp)) != EOF) {
    fputc(ch, socket_fp);
  }
  fclose(file_in_fp);
  fputc(0, socket_fp);
  printf("サーバにデータを送信しました\n");

  file_out_fp = fopen("client_recv.txt", "w");
  while ((ch = fgetc(socket_fp)) != EOF) {
    fputc(ch, file_out_fp);
  }
  fclose(file_out_fp);
  printf("サーバからのデータを受け取りました\n");

  return 0;
}

重要な手続きを抜き出すと下の図のような流れとなっております。クライアントの方は2ステップでいけるみたいですねー。

f:id:hoshimure-47:20170705015413p:plain

いやこれだけじゃ何のことかわからんぜって方のために、気持ち悪いくらいコメントついてる僕の勉強用ソースも末尾に載せておきます。

実行する前にtcp_server.cのあるフォルダにserver_send.txtという名前のファイル、tcp_client.cのあるフォルダにclient_sendという名前の送信用ファイルをそれぞれ置いておきます。ファイルがないとエラーがでます。

それから、tcp_server.cの実行中にtcp_client.cを実行すると
f:id:hoshimure-47:20170704023125p:plain
と、通信しているような感じが出てます。

実際にやってみると「TCPがコネクション型のプロトコルってそういう意味だったのか」とか、「今までネットワークって難しく思ってたけど案外単純」みたいな気持ちになりました。

とりあえずクライアントとサーバはこんな感じで表現します。意外とC言語でもサーバ実装とかできるんですね。そうですよね、元々はほとんどCで書かれてたんですから当然ですよね。

Webブラウザから、TCPサーバにアクセスしてHTTPリクエストを見る

私たちが実際に使っているWebブラウザは、Webサーバに対して一体どのようなリクエストを送っているのかというところを見てみます。

具体的には、先程作ったTCPサーバのプログラム実行中に普段使っているWebブラウザ(Google ChromeIEFirefoxなど)から

http://localhost:8001/index.html

にアクセスして、Webサーバに送られてきたserver_recv.txtの中身を見るという感じです。

送られてくるHTTPリクエストヘッダと呼ばれるファイルは、ファイルの最後が2回の改行が続くので、終端を示すためにtcp_server.cの該当部分を変更します。
HTTPでは改行コードがCR+LFという形式らしいので、少し力技っぽいですが10,13という値が2回続いたら終了するようにしました。

  socket_fp = fdopen(fd, "r+");
  file_out_fp = fopen("server_recv.txt", "w");
  int ch2 = 0, ch3 = 0, ch4 = 0;
  while((ch = fgetc(socket_fp)) != EOF) {
    fputc(ch, file_out_fp);
    if(ch == 10 && ch2 == 13 && ch3 == 10 && ch4 == 13){
      printf("HTTPレスポンスヘッダの終端です\n");
      break;
    }
    ch4 = ch3;
    ch3 = ch2;
    ch2 = ch;
  }
  fclose(file_out_fp);
  printf("クライアントからのデータを受け取りました\n");

tcp_server.cを書き換えて、実行して、Webブラウザからhttp://localhost:8001/index.htmlに接続を試みると、server_recv.txtにHTTPリクエストの内容が表示されています。

GET /index.html HTTP/1.1
Host: localhost:8001
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.84 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: ja,en-US;q=0.8,en;q=0.6

Webブラウザがサーバに対してどういうリクエストを投げかけているかが分かりました。

逆にApacheなどのWebサーバプログラムはどういった処理をしているのか見てみます。

TCPクライアントからWebサーバにアクセスしてHTTPレスポンスを見る

先にApacheをインストールしておきます。

先程server_recv.txtとして受け取ったファイルを、そのままclient_send.txtにコピーしてWebサーバ(Apache)に投げてみよう、ということをします。

まずserver_recv.txtでは8001となっていた箇所を、client_send.txtではHTTPデフォルトのポート番号の80に変更します。

で、クライアント側のプログラムもそれに対応する部分を変更します。また終了を表すことにしていた0もApache相手には必要ないのでコメントアウトしておきます。

  addr.sin_port = htons(8001);
  → addr.sin_port = htons(80);

  fputc(0, socket_fp);
  → //fputc(0, socket_fp);

このプログラムを実行すると

HTTP/1.1 400 Bad Request
Date: Mon, 03 Jul 2017 12:25:29 GMT
Server: Apache/2.4.10 (Raspbian)
Content-Length: 303
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.10 (Raspbian) Server at 127.0.0.1 Port 80</address>
</body></html>

WebサーバからのHTTPレスポンスが返されます。なんか400 Bad Requestでクライアントエラーになってますが、今回は表示させることが目的なのでスルーすることにします。

分かったこと

  • TCPの具体的な手続きとソケットの概念
  • HTTPレスポンス、リクエストの内容

Webアプリ開発のための第一歩です。頑張ります。

ソースコード(コメント付き)

勉強用のコメント付きコードです。いないとは思いますがやりたい人がいたら参考までに。

// tcp_server.c

// まずPOSIXは、UNIXをはじめとする異なるOS実装に共通のAPIを定め、移植性の高いソフトウェア開発を簡易化することを目的としてIEEEが策定したAPI規格
// 内容は、カーネルへのC言語のインターフェイスであるシステムコールや、プロセス環境、ファイルとディレクトリ、システムデータベース、アーカイブフォーマットなど
// つまりPOSIXはOSの規格

// If you define this macro to a value greater than or equal to 1,
// then the functionality from the 1990 edition of the POSIX.1 standard (IEEE Standard 1003.1-1990) is made available.
// つまり_POSIX_C_SOURCEを1以上と定義することでPOSIX.1に準拠したプログラムを作れる
// このプログラムでは、glibcでfdopenを使うために定義されている(らしい)
#define _POSIX_C_SOURCE 1
// 標準入出力ライブラリ
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/uio.h>

int main(int argc, char **argv)
{
  int sock;
  // sockaddr_in構造体:<netinet/in.h>にある。
  // 簡単にいうと、プロセスが持つソケットにプロセス間通信ができるように OS上のアドレスを割り当てるための手続き書類を作成するための構造体
  // もっと簡単にいうと、Internet用のソケットのアドレスを指定したり、逆にソケットの アドレスを調べたりするときに使う
  // もともとsockaddr構造体という汎用的構造体があって、UNIX LOCALに特化したsockaddr_unとInternetに特化したsockaddr_inがある
  /* sockaddr_inの定義はこんな感じ
  struct sockaddr_in {
 	  u_char	sin_len;            // この構造体のサイズ、u_charはunsigned charの意味でその他も同様
	  u_char	sin_family;         // とりあえずAF_INET指定しておこう
	  u_short	sin_port;           // マシンのポート番号
	  struct	in_addr sin_addr;   // マシンのIPアドレス(IPv4)
	  char	sin_zero[8];          //
  };

  struct in_addr {
	  u_int32_t s_addr;
  };
  */
  /* sin_familyはこんな感じで指定する
  ちなみにAF = Address Family PF = Protocol Familyの略らしい。違いは分からん。
  1.AF_INET:ARPAインターネットプロトコル
  2.AF_UNIX:UNIXファイルシステムドメイン
  3.AF_ISO:ISO標準プロトコル
  4.AF_NS:XeroxNetworkSystemsプロトコル
  5.AF_IPX:NovellIPXプロトコル
  6.AF_APPLETALK:AppletalkDDP
  7.PF_INET:IPv4 AF_INETとほぼ同義
  8.PF_INET6:IPv6
  9.PF_IPX:IPX - Novell プロトコル
  10.PF_NETLINK:カーネル・ユーザ・デバイス
  11.PF_X25:ITU-T X.25 / ISO-8208 プロトコル
  12.PF_AX25:アマチュア無線 AX.25 プロトコル
  13.PF_ATMPVC:生の ATM PVC にアクセスする
  14.PF_APPLETALK:アップルトーク
  15.PF_PACKET:低レベルのパケットインターフェース
  */
  struct sockaddr_in addr;
  // fd = file descriptor
  int fd;
  FILE *socket_fp;
  FILE *file_out_fp;
  FILE *file_in_fp;
  int ch;

  // void * memset( void *str , int chr , size_t len ):<string.h>にある
  // strの先頭からlenバイト分だけchrをセット
  // ここではaddrの長さだけ0で初期化している
  // 古いサイトや文献だとbzeroという関数で実装されているが、これは廃止される(された?)のであまり使わない方がいい(らしい)
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  // htonsとhtonlについて
  // htons = host to network short、htonl = host to network longの略
  // 現在の多くのPCはリトルエンディアン方式で、ネットワークはインターネット黎明期の名残でビッグエンディアン方式が標準
  // この問題を解決するための関数がhtonsとhtonlで、逆(ntohs、ntohl)もある
  addr.sin_port = htons(8001);
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  // htonl内に特定のアドレスを書くと、そのアドレスの要求だけを受け付ける。INADDR_ANYでどのアドレスからの要求でも受け付けるようになる

  // int socket(int domain, int type, int protocol);
  // 第1引数はプロトコルファミリと呼ばれるやつ。結局何なのかよくわかってない
  // 第2引数は通信方式を指定。SOCK_STREAMは順双方向のバイトストリーム、TCP/IPではこれを用いる
  // UDP/IPではSOCK_DGRAM、IPではSOCK_RAW。今回はTCP/IPなのでSOCK_STREAM
  // 第3引数は使用するプロトコルで、0を指定すると自動で設定してくれるっぽい
  // 成功すると新しいソケットのファイルディスクリプタを返し、失敗すると-1を返す
  // つまり新しいソケットを作ってくれるということらしい。できなければIPPROTO_TCPなど自分で指定する
  // sock = socket(AF_INET, SOCK_STREAM, 0);
  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("socket_error");
    return -1;
  }
  else {
    printf("ソケットを生成しました\n");
  }

  // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  // bind関数の定義的に第2引数はstruct sockaddrのポインタなので、キャストする
  // この関数はサーバ側で利用するIPアドレスとポート番号を利用する準備をしてる
  // "bind"は、"結び付ける、紐つける"といった意味があることからソケットとアドレスを紐付ける役割をしてると解釈した
  // ch = bind(sock, (struct sockaddr*)&addr, sizeof(addr));

  if((ch = bind(sock, (struct sockaddr*)&addr, sizeof(addr))) < 0){
    perror("bind_error");
    return -1;
  }
  else { 
    printf("サーバの準備ができました\n");
  }

  // int listen(int sockfd, int backlog);
  // listen関数はsockfdで指定されるソケットを接続待ちソケットとして印づける
  // backlogは最大で何個のクライアントを待たせることができるか、という待ち行列の長さを表す。とりあえずお試しなので5くらいでいいでしょといった感じ
  // 成功した場合には0、失敗なら-1が返される

  if((ch = listen(sock, 5)) < 0){
    perror("listen_error");
    return -1;
  }
  else {
    printf("クライアントからの接続を待っています\n");
  }

  if((fd = accept(sock, NULL, NULL)) < 0)
  {
    perror("accept_error");
    return -1;
  }
  else { 
    printf("クライアントを接続しました\n");
  }

  socket_fp = fdopen(fd, "r+");
  file_out_fp = fopen("server_recv.txt", "w");
  while((ch = fgetc(socket_fp)) != 0) {
    fputc(ch, file_out_fp);
  }
  fclose(file_out_fp);
  printf("クライアントからのデータを受け取りました\n");

  file_in_fp = fopen("server_send.txt", "r");
  while((ch = fgetc(file_in_fp)) != EOF){
    fputc(ch, socket_fp);
  }
  fclose(file_in_fp);
  fclose(socket_fp);
  printf("クライアントにデータを送信しました\n");

  printf("通信が正常に終了しました\n");

  return 0;
}
// tcp_client.c
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/uio.h>
#include <netdb.h>

int main(int argc, char **argv)
{
  int sock;
  struct sockaddr_in addr;
  // hostentはマシンのIPアドレスなどの情報を調べる際に使う構造体でnetdb.hにある
  /*
  struct hostent {
	   char  *h_name;            // ホストの正式名称
	   char  **h_aliases;        // 別名リスト(マシンの別名が存在すればここに入る)
	   int   h_addrtype;         // ホストアドレスのタイプ (AF_INET6 など)
	   int   h_length;           // アドレスの長さ
	   char  **h_addr_list;      // NULL で終わるアドレスのリスト(普通0番目だけ使われる)
  };
  */
  struct hostent *host;
  FILE *socket_fp;
  FILE *file_out_fp;
  FILE *file_in_fp;
  int ch;

  memset(&addr, 9, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(8001);

  // gethostbyname(name)は、ホスト名nameに対応した構造体hostentを返す
  // nameには、ホスト名の他、IPv4アドレスIPv6アドレスも指定できる(らしい)
  host = gethostbyname("localhost");
  // void *memcpy(void *buf1, const void *buf2, size_t n);
  // buf2の先頭からn文字分のアドレスをbuf1のアドレスにコピー
  // h_addr_list[0]に入ってるアドレスをsin_addrにコピーする
  // つまりマシンのアドレスをソケットに渡している
  memcpy(&addr.sin_addr, host->h_addr_list[0], sizeof(addr.sin_addr));

  if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    perror("socket_error");
    return -1;
  }
  else {
    printf("ソケットを生成しました\n");
  }

  // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  // ファイルディスクリプタsockfdが参照しているソケットをaddrで指定されたアドレスに接続する。
  // addrlen 引き数は addr の大きさを示す。
  if(connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){
    perror("connect_error");
    return -1;
  }
  else {
    printf("サーバと接続されました\n");
  }

  socket_fp = fdopen(sock, "r+");
  file_in_fp = fopen("client_send.txt", "r");
  while ((ch = fgetc(file_in_fp)) != EOF) {
    fputc(ch, socket_fp);
  }
  fclose(file_in_fp);
  fputc(0, socket_fp);
  printf("サーバにデータを送信しました\n");

  file_out_fp = fopen("client_recv.txt", "w");
  while ((ch = fgetc(socket_fp)) != EOF) {
    fputc(ch, file_out_fp);
  }
  fclose(file_out_fp);
  printf("サーバからのデータを受け取りました\n");

  return 0;
}

AngelhackOsakaに参加して知ったこと

6/17、6/18に大阪のグランフロント大阪で行われた「AngelhackOsaka」というハッカソンイベントに参加しました。
Angelhackは世界中で行われているハッカソンで、日本では昨年(2016年)の東京に続いて今回が二度目の開催でした。
Angelhackの地方大会で優勝すると「HACKcelerator」と呼ばれるシリコンバレーで行われるスタートアップ支援プロジェクトに参加することができます。

今回、人生で初めてハッカソンに参加したので、気づいたこと、感じたこと、知ったことについて少し書きます。

その1 プログラム書けない人も結構いる

参加する前までは、眼鏡かけたゴリゴリのプラグラマやエンジニア、またはそれ志望の学生がガリガリコード書いてるみたいなイメージしかありませんでした。もちろんそういう方もたくさんおられましたが、プログラムなんて書いたことないという方が結構いて驚きました。目的も人により様々で、僕みたいにプログラミングをもっと勉強したいという人もいれば、本気で世界を変えたいと思っている起業家の卵みたいな人たち、単純ににいろんな人とつながりたい人など本当にいろんな方がいました。中でも、僕は某大手企業の研究職の方と同じチームに入ることになったのですが、普段の企業説明会などではあまり聞けないようなことまでお話が聞けて非常に参考になりました。

プログラムが書けないからハッカソンは自分には敷居が高いなと思っている方でも、必ず誰かがサポートしてくれるので、参加者の話を聞くだけといった気軽な感じでもっと参加してもらえればいいなと思います。

その2 APIプログラミングについて知る

ここからは少し技術に関して思ったことを書きます。
まずハッカソンではAPIに関する知識が求められます。APIApplication Programming Interfaceの頭文字で、公式に提供されているWebサービスなどをプログラミングに利用することができます。例えば、アプリに地図を組み込みたいといった場合にはわざわざ自分で一から地図を作らなくとも、Google MapのAPIを利用することで大幅に開発期間を短縮し、なおかつクオリティの高いサービスを作ることができます。

次のリンクはハッカソンで用いられたプログラミング言語、およびAPIのランキング結果です。

jp.techcrunch.com

TwitterFacebookInstagramなど私たちに身近なSNSAPI、それからGooglePlayやSoundCloudAPIなんかもよく利用されています。また人気のあるプログラミング言語ランキング上位に位置するのもWebプログラミングが得意なJavaScriptPythonです。ハッカソンのような短い開発期間においては、既存のサービスにちょい足ししたり、組み合わせたりして新たな発想のものを作るといった考え方が好まれるようです。

なかなか学生では、プログラミングが本当に好きでもなければAPIを利用することは少ないかもしれません。ですが今はインターネットの時代なので、Webを活用できる能力が求められます。数値計算やソートアルゴリズムだけがプログラミングではありません。何よりWebは自身がよく利用しているので仕組みが分かると本当に面白いと思います。今までWebプログラミングやAPIに触れたことがないという学生の方はぜ使ってみてください。

僕もこの機会をきっかけにJavaScriptに出会うことができたので、それ関連の記事などもまた書けたらなと思います。

その3 UIデザインについて知る

UIはUser Interfaceの略で、どうすればサービスを実際に使用するユーザが操作しやすいかなどを考えてデザインされるものです。最近なんかもアプリ版Twitterのアイコンが四角から丸に変わっていて少し話題になりました。

ferret-plus.com

情報系の学部なんかではアプリや製品のプロトタイプやサービスなんかを学校で作る機会が多少なりあるかと思いますが、UIについて教わったり学んだりする機会はまだあまりないのではないでしょうか。ここに学校で教わることと現実で求められることのギャップを感じました。実際、ハッカソンでも学生が創作したものとプロのデザインが加えられたものとでは、クオリティに大きな差があったように感じます。本職のデザイナーとまではいかなくとも、エンジニアでも必要最低限デザインの理解ができると、ワンランク上のエンジニアになれるのかなって思いました。

最後までお読みいただきましてありがとうございました。
どこかのハッカソン等で会う機会があればぜひお声かけください!

おまけ

最近アイドルの結婚宣言が世間を賑わせているので、アイドルに関する簡単なアルゴリズムの問題を一つご紹介します。よかったら考えてみてください。

川の一方の岸に、あなたとアイドル、そのアイドルのオタク、そのアイドルの彼氏がいます。今、川の反対側の岸に3人を送り届けなければならないという謎の状況が発生しました。川を渡るための船はあなたともう1人しか乗ることができません。ただし、アイドルが襲われる危険性があるのでオタクとアイドルを二人きりにすることはできません。また、彼氏と逃亡してしまう恐れがあるのでアイドルと彼氏を二人きりにすることもできません。無事に3人を対岸に送り届けることができるような方法は存在するでしょうか?

【PiCAST】ChromeCastっぽいものを作ってみた。

"ChromeCast"ってご存知ですか。

Chromecast - Google
https://www.google.com/intl/ja_jp/chromecast/
 今の子はYouTube世代なので勿論知ってますよね。YouTubeの動画とかをTVで楽しめるやつです。ちなみに僕はでんぱ組.incのCMが好きでした。
 僕も今年から晴れて大学生になったので、オシャレ大学生目指して買っちゃおうかな~と思ってGoogleで探してみたら、
f:id:hoshimure-47:20170610024453p:plain
 え、高くね。YouTubeをTVで見るだけなのに5000円とかありえん。買うの諦めました。

作ろう。

 自分でChromeCastっぽいものを作ろうという結論に達しました。必要は発明の母とはよく言ったもんです。 
 「RaspberryPI Chromecast」で探したら、どうやらおんなじ考えを持っていた人がいましたので、今回はその方のプログラムを使ってみることにします。
github.com
 こちらのページのREADME.mdに書かれている手順に従ってやってみます。
 結果から先に言うと、YouTubeライブ配信動画はTVでキャストすることができました。 

必要なもの

手順

1. GitHubからセットアップスクリプト(setup.sh)をダウンロードして実行
$ curl -OL http://raw.github.com/lanceseidman/PiCAST/master/setup.sh
$ sudo sh setup.sh

どえらい時間かかります。我が家の貧弱なWi-fi環境では4時間くらいかかりました。気長に待ちましょう。

2. PiCASTで必要なファイルをホームディレクトリにコピー
$ sudo cp -R /root/PiCAST
$ sudo chown -R pi:pi /home/pi/PiCAST
3. 足りないモジュールを追加するのと環境変数のPATHを設定
$ sudo npm install -g express
$ export NODE_PATH=/usr/local/lib/node_modules

 
 "express"っていうのは、JavaScriptのサーバサイド実行環境である"Node.js"のフレームワークらしいです。

4. とりあえずPiCASTを実行してみる
$ cd /home/pi/PiCAST
$ ./picast_start.sh

 picast_start.shがプロセスを開始してくれるスクリプトで、picast_stop.shがプロセスを終了してくれるスクリプトです。

5. スマホからアクセスしてみる。

 GoogleChromeなどインターネットブラウザから”http://[自分のラズベリーパイIPアドレス]:3000”にアクセスしてみる。今までの手順が正しく実行されていれば、「Welcome to PiCAST 3! in the URL, type what you want to do...」と表示されます。
f:id:hoshimure-47:20170610035431j:plain

 とりあえずラズベリーパイHDMI端子でテレビにつないでみます。ここで続けて”http://[自分のラズベリーパイIPアドレス]:3000/yt-stream/[見たいYouTube動画のビデオID]”にアクセスしてみる。ビデオIDとはYouTubeの各動画のURLの末尾にある"v="より後ろの部分の英数字列です。ここで、動画はライブ動画を選ぶことに注意しましょう。正しく実行されると次のような画面になります。
f:id:hoshimure-47:20170610035434j:plain

6. テレビにキャストされる

 さて、僕はこちらのNASAの心躍る壮大なライブ映像をキャストしてみることにしました。
f:id:hoshimure-47:20170610024513p:plain

 今までの手順で一応動きます。こんな感じでテレビにキャストされます。
f:id:hoshimure-47:20170610024505j:plain


 ちなみに"picast.js"の12行目あたりを

exec("livestreamer --player='mplayer -fs' https://www.youtube.com/watch?v=" + req.params.url + " best")

に変えるとフルスクリーンで実行されます。また最後の" best"を" worst"や" 480p"とすると画質を調整できます。
 
 手順通りいったあなたはおめでとうございます。以下、この処理の内容と僕がハマった点について述べておきます。

処理の内容

 PiCASTの処理についての簡単なイメージ図です。JavaScriptに明るい方は直接”picast.js”を読めばすぐに理解できるかと思いますが、スマホとラズパイの間で同期させてなんやかんやさせるみたいなカッコイイ処理ではなく、ただ単にブラウザに入力されたURLをサーバが読み込んで、サーバからラズパイに動画再生しろっていう命令をぶん投げてるだけです。
f:id:hoshimure-47:20170610025706j:plain

ハマったポイント

そもそもライブ配信動画にしか対応してない

 CMのみたいに、でんぱ組.incの「でんでんぱっしょん」をキャストしようと試行錯誤していたわけですが、今回ラズパイ上で実行した"livestreamer"は、YouTubeライブ配信動画しかサポートされていないので、普通の動画のビデオIDを指定するとエラー出されます。
f:id:hoshimure-47:20170610035437p:plain
 "livestreamer"の代わりに"youtube-dl"とかいうプラグインを使えば普通の動画でも行けそうな気がします。でも著作権とか怖いので実装してません。誰かできた人いたら教えてください。

picast_start.sh実行しまくってプロセスがいっぱいできちゃう
// picastを実行
$ ./picast_start.sh
// 生成されたプロセスのリストを表示
$ forever list
// logが見れる。picast.jsのエラーとかもここにでてくる
$ forever logs pycast.js

 
 いっぱいプロセスが出てきたときは

$ ./picast_stop.sh

 で消しておきましょう。


結論

なんとなくChromeCast”っぽい”のはできたかな。
でもやっぱりChromeCast欲しい。

とりあえずGitHubを使ってみる。

はじめに

プログラミングがお強い方々が使っているイメージのあるGitHub

これまでプログラミングを何年かやってきたけれど、GitHubを使ったこともなかったし、そもそもGitHubが何なのかすら知らない。

いい機会なので、プログラミング強者への第一歩として、GitHubの使い方についてチュートリアルを体験してお勉強しました。

 

GitHub is 何

qiita.com

こちらのなんとも魅力的なタイトルの記事によると、

 Git はバージョン管理ツールです、 GitHub は Git のホスティングサービスです。

ということらしい。全然わからないのでとりあえずやってみよう。

 

御託はいいからやってみる

あれこれ言うよりもチュートリアルを通して、GitHubの雰囲気をつかもう。

まずはGitHubhttps://github.com/)のページに行きます。

 

f:id:hoshimure-47:20170602033841p:plain

トップページから英語で心折れそうになります。

適当なUsernameとEmailアドレス、Passwordを設定してSign upします。

有料プランもあるようですが、とりあえずFreeプランで登録します。

 

ホイホイと進んでいくとチュートリアルhttps://guides.github.com/activities/hello-world/)らしきページにたどり着きました。

f:id:hoshimure-47:20170603140814p:plain

とりあえずコードなしで使い方を教えてくれるみたいです。

 

 

f:id:hoshimure-47:20170602034344p:plain

この”Hello Worldチュートリアルでは、

  • リポジトリ(repository)
  • ブランチ(branch)
  • コミット(commit)
  • プルリクエスト(pull request)

が学べるそうです。はー、1つもわからない。

 

ステップ1 リポジトリ作ろう

f:id:hoshimure-47:20170602131309p:plain

リポジトリとは、1つのプロジェクトのフォルダ、ファイル、画像、動画、スプレッドシート、データセットなどなんでも放り込んでおける場所のことのようです。GitHubでは、プロジェクトの説明やインストール方法などを記したファイルを”README.md”という名前で保存してリポジトリ内に配置しておけば勝手に読み込んでくれて表示してくれます。

 

リポジトリの作り方はこれだけです。

  1. 画面右上にある”+”をクリックして、「New repository」を選択。
  2. 「Repository name」に作りたいリポジトリの名前(今回はhello-world)を入力。
  3. 「Description」の入力欄には任意でリポジトリの簡単な説明を入力します。
  4. 「Initialize this repository with a README」にチェックして「Create repository」をクリックする

4のチェックがないと、訳わからない設定に飛ぶのでとりあえず初心者が新規に作るときはチェックつけとけばいいっぽいです。

 

f:id:hoshimure-47:20170602131323p:plain

これが1つのプロジェクトの管理画面になるんですね。なるほど、少し見えてきました。

 

ステップ2 ブランチ作ろう

f:id:hoshimure-47:20170602131336p:plain

ブランチは、違うバージョンのリポジトリを一時的に管理する方法とのことです。

GitHubでは、複数の作業者が同じリポジトリを編集することを想定しているので、一貫性を保つための対応がしっかりしています。OSの排他制御やデータベースのトランザクション処理を勉強したことがある方は理解するのが速いかもしれません。

 

ブランチは少し分かりづらい概念なので、GitHubさんから画像を引用させていただきます。

 

f:id:hoshimure-47:20170602131328p:plain

 

簡単に言うと、”master”と呼ばれる最強のブランチ、言わば木の幹みたいなのがあって、何かリポジトリに変更があったら枝ができます。いろいろ議論した結果、この変更で問題ないとなったら、”master”を更新しよう。みたいな感じです。みんながみんな”master”を直接更新してたらヤバいよね、って話です。ちなみに、後の手順で出てきますが、作ったブランチを”master”に結合させることをマージと呼ぶそうです。

 

ブランチの作り方も簡単です。

  1. 作成した「hello-world」リポジトリに移動
  2. 「branch:masterer」と書かれているドロップダウンを開く
  3. 入力窓に作りたいブランチの名前(今回はREADMEファイルの偏光を行うので「readme-edits」)を入力
  4. 「Create branch:readme-edits」をクリック

 

f:id:hoshimure-47:20170602142226p:plain
これでブランチを作ってくれました。「1 branch」だったのが「2 branches」になってくれています。何人かで作業しているときはもちろん、一人で作業するときも、何か変更があるときには必ずブランチを作ることを習慣にした方がいいですね。
 

ステップ3 コミットしよう

f:id:hoshimure-47:20170602131346p:plain

大したことしてないのに「Bravo!」と褒めてくれます。やはりGitHubは神。

さて、僕たちは今「readme-edits」という名前のブランチを作ったので、その中で編集をしてみましょう。変更を保存すること、または変更をリポジトリに登録することをGitHubではコミットと呼ぶそうです。

 

コミット(このチュートリアルではREADME.mdの編集)の方法については、

  1. リポジトリ画面で「README.md」ファイルをクリック
  2. 右上に編集のためのアイコン(えんぴつマーク)があるのでクリック
  3. 埋め込みエディタが出てくるので、リポジトリの内容だとかインストール方法だとかを好きなように書く
  4. 最後に、どんな編集を行ったかが分かるようにコミットメッセージを簡単に書く
  5. 「Commit changes」ボタンをクリック

でコミットできます。

 

埋め込みエディタにREADMEの内容を書いて

f:id:hoshimure-47:20170602131350p:plain

 

ページ下部にある「Commit changes」ボタンをクリックすると変更が反映されます。

f:id:hoshimure-47:20170602131356p:plain

よく見てみると、エディタ内で「#」をつけると題字になっています。

またエディタ内で1回改行をしただけでは、結果には反映されず一行で表示されてしまうみたいです。TeXと同じですね。

README.mdの書き方についても勉強する必要がありそうです。

 

ステップ4 プルリクエスト作ろう

f:id:hoshimure-47:20170602131401p:plain

プルリクエスとは、編集のリクエスト機能のようなものでコードレビューの際などに用いられるそうです。編集する人に対して、レビューする人が「ここちょっと修正してくれない?」みたいな内容を書く場所といった感じです。

このチュートリアルでは全部1人で作業しているので、その効用を体感することは難しいのですが、チームで開発する際にはとても役立つ機能になるかと思いますので、とりあえずそんなのもあるんだと覚えておくといいかもしれないです。

 

一応、チュートリアルでのプルリクエストの流れはこんな感じ(1人でやっても何しているか分かりづらい気がします)。

  1. リポジトリ画面から「Pull request」のタブを開いて、緑の「New pull request」ボタンをクリック
  2. 比較対象となるブランチ(今回は”readme-edits”)を選択
  3. もとのブランチに追加した部分が緑色、削除した部分が赤色でハイライトされます
  4. 変更が問題ないようなら緑の「Create pull request」ボタンをクリック
  5. プルリクエストのタイトルと簡単に説明を入力し、再び緑の「Create pull request」ボタンをクリック

 

リポジトリ画面から「Pull request」のタブを開いて、緑の「New pull request」ボタンをクリックして

f:id:hoshimure-47:20170603145323p:plain

”readme-edits”を選択すると

f:id:hoshimure-47:20170603145329p:plain

”master”ブランチと”readme-edit”ブランチで変更した部分などを教えてくれます。

f:id:hoshimure-47:20170603145334p:plain

問題なければ「Create pull request」ボタンをクリック。

 

適当にタイトルとコメントを入力しておいて、再び緑の「Create pull request」ボタンをクリック

f:id:hoshimure-47:20170603145340p:plain

 

あとは”readme-edits”ブランチを"master"ブランチにマージするだけです。

 

 ステップ5 マージしよう

f:id:hoshimure-47:20170602131428p:plain

 

  マージは、ブランチのところで説明したように、ブランチ同士を統合させることです。”readme-edits”ブランチを"master"ブランチにマージしてみよう。

 

「Merge pull request」ボタンをクリック。はい終わり。

f:id:hoshimure-47:20170603150935p:plain

 

チュートリアル終了

f:id:hoshimure-47:20170602131439p:plain

確認です。このチュートリアルでは

  • リポジトリ(repository)
  • ブランチ(branch)
  • コミット(commit)
  • プルリクエスト(pull request)

 というGitHubにおける4つの概念を学習しました。GitHubでどのようにしてプロジェクトのバージョン管理が行われているかといった流れは理解できたような気がします。 今は実際にコードを公開したりするのが楽しみです。

  

最後に

この記事を最後まで読んでくださったあなたも一緒にGitHubを始めてみませんか?

github.com

 

お読みいただき、ありがとうございました。 

はてなブログをはじめました。

プログラミング強い系の人になりたくなったので、はてなブログをはじめました。

プログラミングだけじゃなくて、ほかのこともおそらく書くと思います。

 

最初なので自己紹介しておきます。

さわったことのある言語はだいたいこんな感じです。

  • C,C++(授業でやった)
  • HTML,CSS(ゴミカスみたいなWebページなら作れる)
  • JavaAndroidアプリ開発ちょっとだけやりました)
  • Python(はじめたて)

 

ブログを書いてプログラミングを日々の習慣にできればいいな、といったゆるい感じでコツコツと。