iOS 开发技巧大全

iOS在App和Server间创建socket教程

2017-07-21  本文已影响208人  ShawnDu

原文地址

iOS在App和Server间创建socket教程

在大多数场景下,使用HTTP能满足我们的网络请求,但是有一些情况下,需要更低层的通信方式,如基于tcp的socket通信。
关于socket的基础知识,可以参考这篇文章iOS Socket传输基本理论 网络层协议.
提到socket,一定会有app和server,下面先看在server中搭建一个环境。

socket server篇

需要先安装python,安装python后,再安装twisted:

sudo yum install twisted

Twisted是用python写的,需要会一点python,它是一个基于事件的引擎,很容易信用编写出基于TCP, UDP, SSH, IRC, 或者FTP的web应用。
Twisted的设计模式为ractor patten,很像iOS中的runloop,启动一个循环,等待事件,做出响应:

reactor-loop-socket

写一个简单的server

from twisted.internet.protocol import Factory
from twisted.internet import reactor
 
factory = Factory()
reactor.listenTCP(6222, factory)
reactor.run()

保存成server.py, 然后运行:

python server.py

监听端口写成6222,是随便开启的一个,最好不要80吧,因为80在你的server上可能已经被占用了。
传输协议定义成如下:

from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor

class IphoneChat(Protocol):
    def connectionMade(self):
        #self.transport.write("""connected""")
        self.factory.clients.append(self)
        print "clients are ", self.factory.clients

    def connectionLost(self, reason):
        self.factory.clients.remove(self)

    def dataReceived(self, data):
        #print "data is ", data
        a = data.split(':')
        if len(a) > 1:
            command = a[0]
            content = a[1]

            msg = ""
            if command == "iam":
                self.name = content
                msg = self.name + " has joined"

            elif command == "msg":
                msg = self.name + ": " + content

            print msg

            for c in self.factory.clients:
                c.message(msg)

    def message(self, message):
        self.transport.write(message + '\n')


factory = Factory()
factory.protocol = IphoneChat
factory.clients = []

reactor.listenTCP(6222, factory)
print "Iphone Chat server started"
reactor.run()

iOS Client篇

客户端主要需要做三件事:

使用Stream

在iOS中,我们用stream来建立socket连接。
stream是发送接收数据的抽象,数据可以是文件中的,C buffer中或者是网络连接中的。steam中有代理方法,可以处理一些事件,如连接打开时,数据接收了时,连接断开时。
NSStream是父类,NSInputStream和NSOutputStream是NSStream的子类,前者是用来读取输入流,后者是输出。上述的类都是基于CFStream的。
在ChatClientViewController.m中加入以下方法:

- (void)initNetworkCommunication {
    
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"your host", 6222, &readStream, &writeStream);
    
    self.inputStream = (__bridge NSInputStream *)readStream;
    self.outputStream = (__bridge NSOutputStream *)writeStream;
}

这个强大的函数CFStreamCreatePairWithSocketToHost把两个streams绑定到主机和端口上,调用完后,就很容易从CFStreams到NSStreams。
再设置上代理,以便于在代理方法里面做一些处理:

 [self.inputStream setDelegate:self];
 [self.outputStream setDelegate:self];

同时需要遵从协议:

@interface ChatClientViewController ()<NSStreamDelegate>

我们不希望输入流和输出流断掉,并且想在有数据流时做出处理,没有时,能去响应别的事件,而不想被阻塞。所以需要把把这两个流加入到runloop中:

[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

最后再打开:

[self.inputStream open];
[self.outputStream open];

需要在viewDidLoad中加入:

[self initNetworkCommunication];

加入聊天

发送协议中规定的加入聊天的格式:

- (IBAction)joinChat:(id)sender {
    [self.view bringSubviewToFront:self.chatView];
    NSString *response  = [NSString stringWithFormat:@"iam:%@", self.inputNameField.text];
    NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSUTF8StringEncoding]];
    [self.outputStream write:[data bytes] maxLength:[data length]];
}

发送消息

发送协议中规定的发送消息的格式:

- (IBAction)sendMessage:(id)sender {
    NSString *response  = [NSString stringWithFormat:@"msg:%@", self.inputMessageField.text];
    NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSUTF8StringEncoding]];
    [self.outputStream write:[data bytes] maxLength:[data length]];
    self.inputMessageField.text = @"";
}

接收消息

在代理方法在做处理:

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent

NSStreamEvent有以下几种情况:

typedef NS_OPTIONS(NSUInteger, NSStreamEvent) {
    NSStreamEventNone = 0,
    NSStreamEventOpenCompleted = 1UL << 0,
    NSStreamEventHasBytesAvailable = 1UL << 1,
    NSStreamEventHasSpaceAvailable = 1UL << 2,
    NSStreamEventErrorOccurred = 1UL << 3,
    NSStreamEventEndEncountered = 1UL << 4
};

NSStreamEventHasBytesAvailable中对接收的数据做处理:

case NSStreamEventHasBytesAvailable:
            
            if (theStream == self.inputStream) {
                
                uint8_t buffer[1024];
                NSUInteger len;
                
                while ([self.inputStream hasBytesAvailable]) {
                    len = [self.inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0) {
                        
                        NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
                        
                        if (nil != output) {
                            
                            NSLog(@"server said: %@", output);
                            [self messageReceived:output];
                            
                        }
                    }
                }
            }

messageReceived函数在把接收到的string显示出来:

- (void)messageReceived:(NSString *)message {
    [self.messages addObject:message];
    [self.displayMessageTableView reloadData];
    NSIndexPath *topIndexPath = [NSIndexPath indexPathForRow:self.messages.count-1
                                                   inSection:0];
    [self.displayMessageTableView scrollToRowAtIndexPath:topIndexPath
                      atScrollPosition:UITableViewScrollPositionMiddle
                              animated:YES];
}

最终的效果是这样:


iOS在App和Server间创建socket教程-登录界面iOS在App和Server间创建socket教程-登录界面
iOS在App和Server间创建socket教程-消息界面iOS在App和Server间创建socket教程-消息界面
iOS在App和Server间创建socket教程-server界面iOS在App和Server间创建socket教程-server界面

github地址

原文地址

上一篇下一篇

猜你喜欢

热点阅读