iOS -> Socket自搭服務器通信

心跳包.jpg

何為 socket ?

  • 網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個socket。
    建立網絡通信連接至少要一對端口號(socket)。socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網絡開發所用的接口,這就是Socket編程接口;HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通信的能力。

流程

  • 1、服務器綁定 ip 地址和端口號
  • 2、服務器監聽端口號請求,隨時接受客戶端發來的請求鏈接 (這個時候還沒辦法鏈接)
  • 3、客戶端創建 socket
  • 4、客戶端打開 socket 根據 ip 和端口號 嘗試鏈接服務器的 socket
  • 5、服務器 socket 接受客戶端 socket 請求,接受客戶端的請求,等客戶端返回鏈接信息,然后進入阻塞狀態(即 acept 方法 等客戶端返回鏈接信息 才返回,開始接收下一個客戶端請求)
  • 6、客戶端 socket 鏈接成功,向服務器 socket 發送狀態信息
  • 7、服務器返回信息,鏈接成功
  • 8、客戶端 向 服務器 寫入信息
  • 9、服務器接收信息 再像客戶端發送信息(客戶端讀取信息)
  • 10、 客戶端 和 服務端關閉

我這邊用的 Python 3.x 的版本 來寫的服務器 socekt ,里面方法如圖

Python Socket.png

服務端一次接收和發送 代碼如下

import socket  # 導入socket模塊

sk = socket.socket()  # 創建socket對象
sk.bind(("127.0.0.1", 6768))  # 綁定端口,“127.0.0.1”代表本機地址,8888為設置鏈接的端口地址
sk.listen(5)  # 設置監聽,最多可有5個客戶端進行排隊
conn, addr = sk.accept()  # 阻塞狀態,被動等待客戶端的連接
print(conn)  # conn可以理解客戶端的socket對象
# <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9005), raddr=('127.0.0.1', 36694)>
print(addr)  # addr為客戶端的端口地址
# ('127.0.0.1', 40966)
accept_data = conn.recv(1024)  # conn.recv()接收客戶端的內容,接收到的是bytes類型數據,
accept_data2 = str(accept_data, encoding="utf8")  # str(data,encoding="utf8")用“utf8”進行解碼
print("".join(("接收內容:", accept_data2, "    客戶端口:", str(addr[1]))))
send_data = input("輸入發送內容:")
conn.sendall(bytes(send_data, encoding="utf8"))  # 發送內容必須為bytes類型數據,bytes(data, encoding="utf8")用“utf8”格式進行編碼
conn.close()

簡單的并發 服務端

import socketserver  # 導入socketserver模塊


class MyServer(socketserver.BaseRequestHandler):  # 創建一個類,繼承自socketserver模塊下的BaseRequestHandler類
    def handle(self):  # 要想實現并發效果必須重寫父類中的handler方法,在此方法中實現服務端的邏輯代碼(不用再寫連接準備,包括bind()、listen()、accept()方法)
        while 1:
            conn = self.request
            addr = self.client_address
            # 上面兩行代碼,等于 conn,addr = socket.accept(),只不過在socketserver模塊中已經替我們包裝好了,還替我們包裝了包括bind()、listen()、accept()方法
            while 1:
                accept_data = str(conn.recv(1024), encoding="utf8")
                print(accept_data)
                if accept_data == "byebye":
                    break
                send_data = bytes(input(">>>>>"), encoding="utf8")
                conn.sendall(send_data)
            conn.close()


if __name__ == '__main__':
    sever = socketserver.ThreadingTCPServer(("127.0.0.1", 6768),
                                            MyServer)  # 傳入 端口地址 和 我們新建的繼承自socketserver模塊下的BaseRequestHandler類  實例化對象

    sever.serve_forever()  # 通過調用對象的serve_forever()方法來激活服務端

想在 Python 建立客戶端 代碼如下

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 6768))  # 主動初始化與服務器端的連接
while True:
    send_data = input("輸入發送內容:")
    sk.sendall(bytes(send_data, encoding="utf8"))
    if send_data == "byebye":
        break
    accept_data = str(sk.recv(1024), encoding="utf8")
    print("".join(("接收內容:", accept_data)))
sk.close()

iOS 端主要代碼

@property (nonatomic, strong) NSInputStream *inputStream;//對應輸入流
@property (nonatomic, strong) NSOutputStream *outputStream;//對應輸出流
  • 連接服務器
// 建立連接
    NSString *host = @"127.0.0.1";
    int port = 6768;
    
    // 定義C語言輸入輸出流
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
    // 把C語言的輸入輸出流轉化成OC對象
    _inputStream = (__bridge NSInputStream *)(readStream);
    _outputStream = (__bridge NSOutputStream *)(writeStream);
    // 設置代理
    _inputStream.delegate = self;
    _outputStream.delegate = self;
#warning 缺少該步驟,代理有可能不工作
    // 把輸入輸入流添加到主運行循環
    [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    // 打開輸入輸出流
    [_inputStream open];
    [_outputStream open];
  • socket 狀態
  #pragma mark - NSStreamDelegate
  -(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
    NSLog(@"%@",[NSThread currentThread]);
    //    NSStreamEventOpenCompleted = 1UL << 0,//輸入輸出流打開完成
    //    NSStreamEventHasBytesAvailable = 1UL << 1,//有字節可讀
    //    NSStreamEventHasSpaceAvailable = 1UL << 2,//可以發放字節
    //    NSStreamEventErrorOccurred = 1UL << 3,// 連接出現錯誤
    //    NSStreamEventEndEncountered = 1UL << 4// 連接結束
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            NSLog(@"輸入輸出流打開完成");
            break;
        case NSStreamEventHasBytesAvailable:
            NSLog(@"有字節可讀");
            [self readData];
            break;
        case NSStreamEventHasSpaceAvailable:
            NSLog(@"可以發送字節");
            break;
        case NSStreamEventErrorOccurred:
            NSLog(@" 連接出現錯誤");
            break;
        case NSStreamEventEndEncountered:
            NSLog(@"連接結束");
            // 關閉輸入輸出流
            [_inputStream close];
            [_outputStream close];
            // 從主運行循環移除
            [_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
            [_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
            break;
        default:
            break;
    }
}
  • 讀取服務器返回數據
//建立一個緩沖區 可以放1024個字節
    uint8_t buf[1024];
    
    // 返回實際裝的字節數
    NSInteger len = [_inputStream read:buf maxLength:sizeof(buf)];
    
    // 把字節數組轉化成字符串
    NSData *data = [NSData dataWithBytes:buf length:len];
    
    // 從服務器接收到的數據
    NSString *recStr =  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  • 向服務器發送信息
//把Str轉成NSData
        NSData *data = [model.text dataUsingEncoding:NSUTF8StringEncoding];
        
        // 發送數據
        [weakSelf.outputStream write:data.bytes maxLength:data.length];

最后就變成這樣了,界面是隨意寫的 有點丑 別介意!

示例.gif

寫文章不易,給個喜歡可否?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 轉自http://www.mamicode.com/info-detail-877996.html 一、網絡各個協...
    在這藍色天空下閱讀 18,137評論 7 48
  • 轉自http://www.mamicode.com/info-detail-877996.html 一、網絡各個協...
    嘚嘚以嘚嘚閱讀 426評論 0 0
  • 一、網絡各個協議:TCP/IP、SOCKET、HTTP等網絡七層由下往上分別為物理層、數據鏈路層、網絡層、傳輸層、...
    北辰青閱讀 242評論 0 0
  • 一、網絡各個協議:TCP/IP、SOCKET、HTTP等 網絡七層由下往上分別為物理層、數據鏈路層、網絡層、傳輸層...
    任夢RM閱讀 358評論 0 0
  • Socket編程一、網絡各個協議:TCP/IP、SOCKET、HTTP等網絡七層由下往上分別為物理層、數據鏈路層、...
    VincentHK閱讀 300評論 0 2