この記事の目的
iOS-Windows間ソケット通信の基礎的なコードをメモ
前提
クライアント: iOS (Swift3)
サーバー: Windows (C#)
参考
SwiftでTCP/IP通信
Apple Developer Forums
Apple Developer Documentation
サーバー側
以下は、SwiftでTCP/IP通信 の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();
}
}
}
クライアント側
以下は、先ほどのブログをSwift3に書き改めさせていただいたものです。
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)
}
}
}