iOSとWindowsのソケット通信

#この記事の目的
iOS-Windows間ソケット通信の基礎的なコードをメモ
#前提
クライアント: iOS (Swift3)
サーバー: Windows (C#)
#参考
[SwiftでTCP/IP通信](http://straycatmarimo.seesaa.net/article/418410814.html)
[Apple Developer Forums](https://forums.developer.apple.com/thread/52058)
[Apple Developer Documentation](https://developer.apple.com/documentation/corefoundation/cfstream?language=objc)
#サーバー側
以下は、[SwiftでTCP/IP通信](http://straycatmarimo.seesaa.net/article/418410814.html) のC#をほぼそのまま拝借したものです。

```C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyServer
{
    class Program
    {
        static void Main(string[] args)
        {
            //ListenするIPアドレスを決める
            string host = "localhost";
            System.Net.IPAddress ipAdd
                = System.Net.Dns.GetHostEntry(host).AddressList[0];
            Console.WriteLine("ListenするIPアドレス: " + ipAdd);

            //Listenするポート番号
            int port = xxxx;

            //TcpListenerオブジェクトの生成
            System.Net.Sockets.TcpListener listener
                = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, port);

            //Listenを開始する
            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();

            //クライアントから送られたデータを受信する
            System.Text.Encoding enc = System.Text.Encoding.UTF8;
            bool is_connected = true;
            string resMsg;
            while (true)
            {
                is_connected = true;
                System.IO.MemoryStream ms = new System.IO.MemoryStream();
                byte[] resBytes = new byte[256];
                do
                {
                    //データの一部を受信する
                    int resSize = ns.Read(resBytes, 0, resBytes.Length);
                    //Readが0を返した時はクライアントが接続したと判断
                    if (resSize == 0)
                    {
                        is_connected = false;
                        Console.WriteLine("クライアントが切断しました。");
                        break;
                    }
                    //受信したデータを蓄積する
                    ms.Write(resBytes, 0, resSize);
                } while (ns.DataAvailable);
                //受信したデータを文字列に変換
                resMsg = enc.GetString(ms.ToArray());
                ms.Close();
                Console.WriteLine("受信メッセ―ジ: " + resMsg);
                if (resMsg.Contains("end"))
                {
                    break;
                }
            }

            if (is_connected)
            {
                //クライアントにデータを送信する
                //クライアントに送信する文字列を作成
                string sendMsg = resMsg.ToString();
                //文字列をByte型配列に置換
                byte[] sendBytes = enc.GetBytes(sendMsg);
                //データを送信する
                ns.Write(sendBytes, 0, sendBytes.Length);
                Console.WriteLine("送信メッセージ: " +  sendMsg);
            }

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

            //リスナを閉じる
            listener.Stop();
            Console.WriteLine("Listenerを閉じました。");

            Console.ReadLine();
        }
    }
}

```
#クライアント側
以下は、[先ほどのブログ](http://straycatmarimo.seesaa.net/article/418410814.html)をSwift3に書き改めさせていただいたものです。

```swift
import UIKit

class ViewController: UIViewController {
    
    var Connection1 = Connection()
    
    @IBOutlet weak var TB_SendCommand: UITextField!
    
    @IBAction func BT_Connect(sendr: AnyObject){
        Connection1.connect()
    }
    
    @IBAction func BT_Send(sendr: AnyObject){
        //テキストの文字を取得してsendCommand()を行う
        let sendtext = TB_SendCommand.text
        if ((sendtext! as NSString).length > 0) {
            Connection1.sendCommand(command: sendtext!)
        }
    }
    
    @IBAction func BT_End(sendr: AnyObject){
        Connection1.sendCommand(command: "end")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

class Connection: NSObject, StreamDelegate {
    let ServerAddress: CFString =  NSString(string: "xxx.xxx.xxx.xxx") //IPアドレスを指定
    let serverPort: UInt32 = xxxx //開放するポートを指定
    
    private var inputStream : InputStream!
    private var outputStream: OutputStream!
    
    //**
    /* @brief サーバーとの接続を確立する
     */
    func connect(){
        print("connecting.....")
        
        var readStream : Unmanaged<CFReadStream>?
        var writeStream: Unmanaged<CFWriteStream>?
        
        CFStreamCreatePairWithSocketToHost(nil, self.ServerAddress, self.serverPort, &readStream, &writeStream)

        self.inputStream  = readStream!.takeRetainedValue()
        self.outputStream = writeStream!.takeRetainedValue()
        
        self.inputStream.delegate  = self
        self.outputStream.delegate = self
        
        self.inputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        self.outputStream.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        
        self.inputStream.open()
        self.outputStream.open()
        
        print("connect success!!")
    }
    
    //**
    /* @brief inputStream/outputStreamに何かしらのイベントが起きたら起動してくれる関数
     *        今回の場合では、同期型なのでoutputStreamの時しか起動してくれない
     */
    func stream(_ stream:Stream, handle eventCode : Stream.Event){
        //print(stream)
    }
    
    //**
    /* @brief サーバーにコマンド文字列を送信する関数
     */
    func sendCommand(command: String){
        var ccommand = command.data(using: String.Encoding.utf8, allowLossyConversion: false)!
        let text = ccommand.withUnsafeMutableBytes{ bytes in return String(bytesNoCopy: bytes, length: ccommand.count, encoding: String.Encoding.utf8, freeWhenDone: false)!}
        self.outputStream.write(UnsafePointer(text), maxLength: text.utf8.count)
        print("Send: \(command)")
        
        //"end"を受信したら接続切断
        if (String(describing: command) == "end") {
            self.outputStream.close()
            self.outputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
            
            while(!inputStream.hasBytesAvailable){}
            let bufferSize = 1024
            var buffer = Array<UInt8>(repeating: 0, count: bufferSize)
            let bytesRead = inputStream.read(&buffer, maxLength: bufferSize)
            if (bytesRead >= 0) {
                let read = String(bytes: buffer, encoding: String.Encoding.utf8)!
                print("Receive: \(read)")
            }
            self.inputStream.close()
            self.inputStream.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
        }
    }
    
}
```