70208


[こちらの記事](https://qiita.com/sensuikan1973/items/76c0bd861564859ebe4c)を参考に、SwiftでTCPクライアントを作成するコードを試しました。

[code]

    import UIKit
    
    class ViewController: UIViewController
    {
        var Connection1 = Connection()
        
        override func viewDidLoad()
        {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            
            if Connection1.Connected==false
            {
                Connection1.connect()
            }
            while true
            {
                Connection1.sendCommand(command: "0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200\n")
            }
        }
        override func didReceiveMemoryWarning()
        {
            super.didReceiveMemoryWarning()
        }
    }
    
    class Connection: NSObject, StreamDelegate {
        var ServerAddress: CFString =  NSString(string: "192.168.1.5") //IPアドレスを指定
        let serverPort: UInt32 = 50013 //開放するポートを指定
    
        private var inputStream : InputStream!
        private var outputStream: OutputStream!
        var Connected : Bool = false
    
        //**
        /* @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: RunLoop.Mode.default)
            self.outputStream.schedule(in: RunLoop.current, forMode: RunLoop.Mode.default)
    
            self.inputStream.open()
            self.outputStream.open()
    
            print("connect success!!")
            self.Connected=true
        }
    
        //**
        /* @brief inputStream/outputStreamに何かしらのイベントが起きたら起動してくれる関数
         *        今回の場合では、同期型なのでoutputStreamの時しか起動してくれない
         */
        func stream(_ stream:Stream, handle eventCode : Stream.Event){
            //print(stream)
        }
    
        //**
        /* @brief サーバーにコマンド文字列を送信する関数
         */
        func sendCommand(command: String)
        {
            self.outputStream.write(command, maxLength: command.utf8.count)
        }
    }
    
[/code]

上記はアプリを起動すると、IPアドレスの192.168.1.5の50013番ポートに送信するコードです。  
送信自体はうまく行き、サーバーで受け取ることはできました。  
しかし、何回かに1回の頻度でメッセージが途中で2分割されて送信されてしまうようです。

例えば、`0123456789`というメッセージを送信すると  
`012345`と`6789`というメッセージの2つに分割されてサーバー側に表示されてしまいます。

常に分割されるというわけではなく、”たまに分割される”程度の頻度です。  
短いメッセージなら分割されず、少し長めのメッセージだと問題が発生するようです。  
何が原因でメッセージが分割されてしまうのでしょうか？

以下はPC側で受け取る際に使用したPythonサーバーのコードです。

[code]

    import socket
    
    # AF = IPv4 という意味
    # TCP/IP の場合は、SOCK_STREAM を使う
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    　　# IPアドレスとポートを指定
    　　s.bind(('', 50013))
    　　# 1 接続
    　　s.listen(1)
    　　# connection するまで待つ
    　　while True:
    　　　　# 誰かがアクセスしてきたら、コネクションとアドレスを入れる
    　　　　conn, addr = s.accept()
    　　　　with conn:
    　　　　while True:
    　　　　# データを受け取る
    　　　　　　data = conn.recv(30000)
    　　　　　　if not data:
    　　　　　　　break
    　　　　　　print(data.decode('utf-8')+"\n")
    
[/code]


CC BY-SA 4.0
2020-09-07T14:40:27.987
70203
2020-09-07T19:32:07.003
36446
question
python swift network
TCPクライアントでメッセージを送信するとコードが時々途中で分割される


kunif
さんのコメントにもありますが、あなたがサーバ側のコードで使用している`conn.recv(30000)`と言うコードでは、(例え送信されたメッセージのバイト長が30000以下でも)送信メッセージ全体を確実に全部受信できると言う保証はありません。

TCP通信では、送信されるメッセージは通信方式やネットワーク状況などで決まる最大パケットサイズで分割されて送信されます。  
(「TCP パケットサイズ」辺りで検索すれば、いろいろな解説記事が見つかると思います。例えばGoogleではこんな記事がトップに表示されました。「[第16回
信頼性のある通信を実現するTCPプロトコル（3）
(2/4)](https://www.atmarkit.co.jp/ait/articles/0402/13/news096_2.html)」)

上記の記事では具体例にイーサネットを想定していますが、TCP/IPの下層に使用されている通信方式によっては、もっと短いパケットサイズになる可能性もあります。

生のTCPソケットを扱うのであれば、「どこまでがひとまとまりのメッセージ」であるのかは、送信側と受信側で約束事として決めておかないといけません。例えばあなたの現在のSwift側コードのように、「メッセージの末尾には必ず改行コード(`\n`)を付ける」と言う約束にするのであれば、受信側のPythonコードでは「`\n`が受信されるまで、複数回にわたる`recv`の結果をつなぎ合わせる」と言った処理が必要です。

* * *

本題とは離れますが、あなたが参考にされた記事は、Swiftのコーディングルールに則っていないだけでなく、
**不必要で冗長な処理の書き方をしている部分が多数ある** 、 **ポインターの使い方に致命的な誤りがある**
など、「そのまま使ったり真似したりしちゃダメ」なコード例になっています。

これからTCP通信のコードを書いてみようと思うのであれば、Networkフレームワークを使用した方が良いでしょう。(もう登場して数年になるフレームワークなのに、日本語の両記事が見つかりませんでしたが…。)


CC BY-SA 4.0
2020-09-07T19:32:07.003
70208
2020-09-07T19:32:07.003
13972
70203
answer