君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

#Cでsocket通信をしてみる。ついでにnetstatでlistenしているポートを調べる。

C#でソケット通信

職場でC#を使うのと、

ソケット通信に興味があるのでちょっと練習してみた。

ソースコード

server側

using System;

public class Server
{
  public static void Main()
  {
    string ipString = "127.0.0.1";

    System.Net.IPAddress ipAdd = System.Net.IPAddress.Parse(ipString);

    int port = 2001;

    System.Net.Sockets.TcpListener listener =
      new System.Net.Sockets.TcpListener(ipAdd, port);

    listener.Start();

    Console.WriteLine("Listenを開始しました({0}:{1})。",
        ((System.Net.IPEndPoint)listener.LocalEndpoint).Address,
        ((System.Net.IPEndPoint)listener.LocalEndpoint).Port);


    // 接続要求があったら受け入れる
    System.Net.Sockets.TcpClient client = listener.AcceptTcpClient();
    Console.WriteLine("クライアント({0}:{1})と接続しました。",
        ((System.Net.IPEndPoint)client.Client.RemoteEndPoint).Address,
        ((System.Net.IPEndPoint)client.Client.RemoteEndPoint).Port);

    // NetworkStreamを取得データの流れ
    System.Net.Sockets.NetworkStream ns = client.GetStream();

    ns.ReadTimeout = 10000;
    ns.WriteTimeout = 10000;

    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    bool disconnected = false;
    System.IO.MemoryStream ms = new System.IO.MemoryStream();

    byte[] resBytes = new byte[256];
    int resSize = 0;


    do
    {
      //データの一部を受信する
      resSize = ns.Read(resBytes, 0, resBytes.Length);
      //Readが0を返した時はクライアントが切断したと判断
      if (resSize == 0)
      {
        disconnected = true;
        Console.WriteLine("クライアントが切断しました。");
        break;
      }
      //受信したデータを蓄積する
      ms.Write(resBytes, 0, resSize);
      //まだ読み取れるデータがあるか、データの最後が\nでない時は、
      // 受信を続ける

    } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');

    //受信したデータを文字列に変換
    string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    ms.Close();

    resMsg = resMsg.TrimEnd('\n');
    Console.WriteLine(resMsg);

    if (!disconnected)
    {
      //クライアントにデータを送信する
      //クライアントに送信する文字列を作成
      string sendMsg = resMsg.Length.ToString();

      //文字列をByte型配列に変換
      byte[] sendBytes = enc.GetBytes(sendMsg + '\n');

      //データを送信する
      ns.Write(sendBytes, 0, sendBytes.Length);
      Console.WriteLine(sendMsg);
    }

    ns.Close();
    client.Close();
    Console.WriteLine("クライアントとの接続を閉じました。");

    listener.Stop();
    Console.WriteLine("Listenerを閉じました。");

    Console.ReadLine();
  }

}

clinent側

using System;

public class Client
{
  public static void Main()
  {
    Console.WriteLine("入力してください");
    string sendMsg = Console.ReadLine();

    if (sendMsg == null || sendMsg.Length == 0)
    {
      return;
    }

    string ipOrHost = "127.0.0.1";
    int port = 2001;


    System.Net.Sockets.TcpClient tcp =
      new System.Net.Sockets.TcpClient(ipOrHost, port);
    Console.WriteLine("サーバー({0}:{1})と接続しました({2}:{3})。",
        ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Address,
        ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Port,
        ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Address,
        ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Port);


    System.Net.Sockets.NetworkStream ns = tcp.GetStream();

    ns.ReadTimeout = 10000;
    ns.WriteTimeout = 10000;


    System.Text.Encoding enc = System.Text.Encoding.UTF8;
    byte[] sendBytes = enc.GetBytes(sendMsg + '\n');

    ns.Write(sendBytes, 0, sendBytes.Length);
    Console.WriteLine(sendMsg);


    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    byte[] resBytes = new byte[256];
    int resSize = 0;
    do
    {
      resSize = ns.Read(resBytes, 0, resBytes.Length);

      if (resSize == 0)
      {
        Console.WriteLine("サーバーが切断しました。");
        break;
      }

      ms.Write(resBytes, 0, resSize);

    } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');

    string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    ms.Close();

    resMsg = resMsg.TrimEnd('\n');
    Console.WriteLine(resMsg);

    ns.Close();
    tcp.Close();
    Console.WriteLine("切断しました。");

    Console.ReadLine();
  }
}

コンパイル

mcs client.cs
mcs server.cs

実行

サーバの実行

$ mono server.exe
Listenを開始しました(127.0.0.1:2001)。

クライアントの実行

$ mono client.exe 
入力してください
hello world

サーバの反応

クライアント(127.0.0.1:32924)と接続しました。
hello world
11
クライアントとの接続を閉じました。
Listenerを閉じました。

まとめ

TCP通信というのは、IPアドレスとポートの組み合わせ。

だからlocalhost上でポートさえ違えば、通信ができる。

今回は

・サーバ  IP 127.0.0.1  ポート 2001

・クライアント  IP 127.0.0.1  ポート 32924

で通信を行った。

なお、クライアントのポートは通信毎に自動で割り振られる。

netstatでポートがLISTENしているかどうかを調べてみる

$ netstat -anpt
tcp        0      0 127.0.0.1:2001              0.0.0.0:*                   LISTEN      14145/mono  

monoというプロセスが、 2001ポートを使って通信していることがわかる。

-aオプション

LISTEN状態も含むすべてのソケットを表示する。

-nオプション

IPアドレス・ポート番号をそのまま表示する。

これがない場合、ドメイン名として表示されたり、sshhttpdとか表示されたりする。

-pオプション

通信しているプロセスを表示する。