IOS instant messaging, from entry to “give up”?

IOS instant messaging, from entry to
Preface
  • This article will use an example of the way, the various IM iOS programs are simple to achieve again. And provide some selection, implementation details and recommendations for optimization.
  • Note: all the code examples in the text, there are demo:
    iOS instant messaging in GitHub, from entry to “give up”? (Demo)
    can open the project preview effect, read the contrast.

Well, first we sum up our way to achieve IM

The first way to use third party IM services

For fast track, can use third party SDK to achieve. There are a lot of domestic IM third party service providers, similar to the cloud letter, ring letter, cloud, LeanCloud, of course, there are a lot of other, here is not an example of the interest of small partners can look at the next.

  • Third party service provider IM underlying protocol is basically TCP. Their IM program is very mature, with them, we do not even need to build their own IM background, nothing to consider.
    if you are lazy, and even do not need to do their own UI, these third parties have their own set of IM UI, can be used directly. 3 minute integration…
  • But the shortcomings are obvious, the degree of customization is too high, a lot of things we can not control. Of course, there is one of the most important point is that too expensive… As the real social APP, this alone is enough to make us flinch. Of course, if the IM is only an auxiliary function for APP, then the third party service is understandable.
Another way, we do it ourselves

We also have a lot of choices to achieve their own:
1) is the first choice of the transmission protocol, TCP or UDP?
2) we need to choose what kind of chat protocol:

  • Based on Scoket or WebScoket or other private protocol,
  • MQTT
  • Or widely criticized XMPP?

3) we are based on their own OS based on the Socket package or in the framework of the third party packaging?
4) data format, we are using Json, or XML, or Google launched ProtocolBuffer?
5) we have some details need to be considered, such as how to keep the TCP long connection, heartbeat mechanism, Qos mechanism, linkage mechanism and so on… Of course, in addition, we have some security issues need to be considered.

First, the choice of transmission protocol

Next we may need to consider their own implementation of IM, first of all, from the transport layer protocol, we have two options: TCP or UDP?

IOS instant messaging, from entry to

this issue has been discussed many times, the deep details of the interested friends can look at this article:

  • Mobile terminal IM/ push system protocol selection: UDP or TCP? Here we go straight to the conclusion: for small companies or companies with less sophisticated technology, IM must use TCP to implement, because if you want to use UDP, you need to do too much. Of course, QQ is the use of the UDP protocol, of course, not just UDP, Tencent also used its own private agreement to ensure the reliability of the transmission, to prevent the UDP under a variety of data packet loss, disorder, and so a series of problems.
    in a word, if you think the team is very mature technology, then you also use UDP line, or else use TCP as a good.
Two, we look at a variety of chat protocol

First of all, we achieve the way to cut, basically have the following four ways to achieve:

  1. Native based on Scoket: representative framework CocoaAsyncSocket.
  2. Based on WebScoket: representative framework SocketRocket.
  3. Based on MQTT: representative framework MQTTKit.
  4. Based on XMPP: representative framework XMPPFramework.

Of course, the above four ways we can not use the framework of the third party, directly based on the underlying Scoket OS to achieve our custom packaging. Below I will give a Scoket based on the use of the framework without the example, for your reference.

First of all, it is necessary to make clear that the MQTT and XMPP for the chat protocol, they are the top layer of the protocol, and WebScoket is the transmission protocol, which is based on Socket protocol. And we usually say that Tencent IM private agreement, is based on WebScoket or Scoket native package a chat protocol.

The comparison of these 3 chat protocols is as follows:

IOS instant messaging, from entry to
protocol comparison.Png

So in the final analysis, iOS to do a real IM products, are generally based on Scoket or WebScoket, and then plus some private agreements to ensure that.

1 we do not use any framework, directly with the OS Socket to achieve a simple IM.

Our client is also very simple to implement ideas, create Socket, and the server’s Socket docking, and then began to transfer data on it.

  • We have c/c++ or Java of these languages, we know that often any tutorial, the last chapter is about Socket programming, and what is Socket, simply speaking, is a set of programming interfaces we use TCP/IP or UDP/IP protocol. As shown below: IOS instant messaging, from entry to
    we are in the application layer, using socket, easy to achieve the communication between the processes (across the network). Think about it, if there is no socket, we have to face the TCP/IP protocol, we need to write a lot of tedious and repetitive code. If there is still confusion about the concept of socket, you can look at this article:
    look at the essence of the problem, socket in the end is what?.
    but this article on the number of concurrent connections is wrong, the correct understanding of this article can be seen:
    single server concurrent TCP connection number in the end how much

We can then begin to implement IM, first of all, we do not rely on any framework, directly to call the underlying OS – based C Socket to achieve, it provides a set of interfaces:

//socket creates and initializes socket, returns the file descriptor for the socket, if the descriptor is -1, indicates that the creation failed. Int socket (int addressFamily, int type, int protocol socket int close) / / close connection (int / socketFileDescriptor) to bind the socket to the specific host address and port number, successfully bound to return 0, failed to return -1. Int bind (int socketFileDescriptor, SOCKADDR *addressToBind, int addressStructLength) / / accept client requests and the network address of the client information saved to clientAddress. Int accept (int socketFileDescriptor, SOCKADDR *clientAddress, int clientAddressStructLength) / / client connection requests sent to a specific network address of the server, the connection is successful return 0, failed to return -1. Int connect (int socketFileDescriptor, SOCKADDR *serverAddress, int serverAddressLength) / / use DNS to find the specific host name corresponding to the IP address. Returns NULL if the corresponding IP address is not found. Hostent* gethostbyname (char *hostname) / / send data through the socket, the number of bytes sent back successfully sent, otherwise it returns -1. Int send (int socketFileDescriptor, char *buffer, int bufferLength, int flags) / / data read from the socket, read the successful return the number of bytes read on success, otherwise it returns -1. Int receive (int socketFileDescriptor, char *buffer, int bufferLength, int flags) by UDP socket / / to send data to a specific network address, the number of bytes sent back successfully sent, otherwise it returns -1. Int SendTo (int socketFileDescriptor, char *buffer, int bufferLength, int flags, SOCKADDR *destinationAddress, int destinationAddressLength) / / read data from the UDP socket, and save the network address of the sender of the message, read the number of bytes read from the successful return of success, otherwise it returns -1. Int recvfrom (int socketFileDescriptor, char *buffer, int bufferLength, int flags, SOCKADDR *fromAddress, int *fromAddressLength)

Let us do a variety of socket operations, first of all, we use it to write a client. To sum up, a simple IM client needs to do the following 4 things:

  1. Client calls socket (…) to create socket;
  2. The client calls connect (…) to initiate a connection request to the server to establish a connection;
  3. After the client and server are connected, you can send or receive data from the client (send) (/receive) to the client;
  4. Client call close close socket;

According to the above 4 outline, we encapsulate a single case named TYHSocketManager, to call the socket related methods:

TYHSocketManager.h

#import < Foundation/Foundation.h> @interface TYHSocketManager; NSObject + (instancetype) share; - (void) connect; - (void) disConnect; - (void) sendMsg: (NSString *) MSG; @end

TYHSocketManager.m

#import "TYHSocketManager.h" #import < sys/types.h> #import; < sys/socket.h> #import < netinet/in.h> #import; < arpa/inet.h> @interface (TYHSocketManager) @property (nonatomic, assign) int clientScoket; @end @implementation TYHSocketManager (instancetype) share static dispatch_once_t static TYHSocketManager {onceToken; *instance = nil; dispatch_once (& onceToken, instance ^{= [[self alloc]init]; [instance initScoket]; [instance pullMsg; return instance;});} - (void initScoket) {/ / each time before the connection, disconnect if (_clientScoket! = 0) {[self disConnect]; _clientScoket = 0;} / / _clientScoket = CreateClinet socket client Socket (Ip const); / / server char * server_ip= "127.0.0.1"; / / server_port=6969 / / short server port; that is equal to 0 if connection failed (ConnectionToServer (_clientScoket, server_ip, server_port) ==0) {printf ("Connect to server error/n"); return;} / / go to the connection printf ("Connect to server ok/n");} static int CreateClinetSocket (int) {ClinetSocket = 0; / / create a socket, the return value is Int. (Note: Scoket is the first parameter of type Int) / / addressFamily IPv4 (AF_INET) or IPv6 (AF_INET6). Second / parameter type is socket type, usually stream (SOCK_STREAM) or datagram data message (SOCK_DGRAM) / third parameter protocol parameter is usually set to 0, in order to let the system automatically as we choose appropriate protocol for stream socket is the TCP protocol (IPPROTO_TCP), and for datagram will be UDP protocol (IPPROTO_UDP). ClinetSocket = socket (AF_INET, SOCK_STREAM, 0); return ClinetSocket;} static int ConnectionToServer (int client_socket, const char unsigned short * server_ip, port) {/ / create a sockaddr_in type structure of struct sockaddr_in sAddr={0}; sAddr.sin_len=sizeof (sAddr); / / set IPv4 sAddr.sin_family=AF_INET; //inet_aton is a new method to be a a string of IP address into a 32 bit network address sequence IP / / if this function succeeds, the return value is nonzero, if the input incorrect address will return zero. Inet_aton (server_ip, & sAddr.sin_addr); //htons is the integer variables change from host byte order to network byte order, assign port number sAddr.sin_port=htons (port); / / Scoket and server address, initiating the connection. / / client connection sends a request to a specific network address of the server, the connection is successful return 0, failed to return -1. Note: the interface / / blocks until the server returns. If (connect (client_socket (struct, SOCKADDR * & sAddr), sizeof (sAddr) ==0)) {return client_socket}}; return 0; #pragma mark - a new thread to receive the message - (void) pullMsg NSThread [[NSThread alloc]initWithTarget: self {*thread = selector:@selector (recieveAction) object:nil]; [thread start];} - #pragma mark the external logic - (void) connect {[self initScoket];} - (void disConnect) {/ / close close connection (self.clientScoket);} / / send messages (void): sendMsg (NSString * MSG) {const char *send_Message = [msg UTF8String]; send (self.clientScoket, send_Message, strlen (send_Message) +1,0);} / / for the server to send messages (void) recieveAction{while (1) {char recv_Message[1024] = {0}; Recv (self.clientScoket, recv_Message, sizeof (recv_Message), 0); printf ("%s/n", recv_Message);}}

As shown above:

  • We call the initScoket method, using the CreateClinetSocket method of a Scoket, that is, call the socket function: ClinetSocket = socket (AF_INET, SOCK_STREAM, 0);
  • Then call the ConnectionToServer function to connect with the server, the IP address for the 127.0.0.1 is the native localhost and port 6969 connected. In this function, we bound the structure of a sockaddr_in type body, the content structure is as follows: struct sockaddr_in {__uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero[8];}; which contains a number, we need to connect the service side of the Scoket some of the basic parameters, the specific details can be assigned see note.
  • After a successful connection, we can call the send function and recv function for messaging, here, I opened up a new resident thread, call the recv function in this thread in a cycle of death to stop, so the message sent by the server, the first time can be received.

So that the client can be simple to use, and then we look at the server to achieve.

Like, we first need to do a simple summary of the work of the server:
  1. Server calls socket (…) to create socket;
  2. Server calls listen (…) to set the buffer;
  3. The server receives a client request to establish a connection via accept (…);
  4. After the server and the client establish a connection, you can send or receive data from the client through the send (…) /receive (…);
  5. Server call close close socket;
Then we can achieve the specific

The bottom of the OS function is to support our server to realize, but we don’t generally use iOS to do so (how the real application scenarios, who use iOS as a Scoket server…), if you still want to use these functions to achieve the server, can refer to this article: Socket simple network programming Cocoa-iOS.

Here I use node.js to build a simple Scoket server. Source code is as follows:

Var net = require ('net'); VAR HOST var ='127.0.0.1'; PORT = 6969; / / create a TCP server instance, call the listen function to monitor a designated port (net.createServer) / / incoming callback function as a "connection" event handler / in every "connection" event, received the callback function of the socket object is only net.createServer (function (sock) {/ / we get a connection - the connection is automatically associated with a socket object console.log ('CONNECTED: '+ sock.remoteAddress +': '+ sock.remotePort); sock.write (' server sent: successful connection '); / / add a "data" event handler the function sock.on for this socket instance ('data', function (data) {console.log ('DATA + sock.remote ' Address + data + ':'); / / return the data, the client will receive data from the server sock.write ('You said '+ data +' ');}); / / add a "close" event handler for this sock.on socket instance ('close', function (data) {console.log ('CLOSED: '+ sock.remoteAddress +' '+ sock.remotePort);});}).Listen (PORT, HOST); console.log ('Server listening on' + HOST + ',' + PORT);

See this do not understand node.js friends do not have to worry, here you can use any language c/c++/java/oc to realize the background, here node.js is just a choice of the landlord, in order to allow us to verify before writing the client Scoket effect. If you do not understand node.js also does not matter, you only need to copy the relevant code written above paste paste, if you have the node interpreter, then directly into the terminal to enter the source code file directory:

Node fileName

You can run the script (fileName to save the file name of the source code).

Let’s take a look at the operating results:

IOS instant messaging, from entry to
handle2.gif

The
server is running and listening to the 6969 port.
then we used to write the iOS example. Client print shows that the connection is successful, and we run the server also print the connection successfully. Then we sent a message, the server successfully received the message, the message is sent back to the client, a circle around the client and received this message. At this point we use OS Scoket to achieve a simple IM.

Do you think this is too simple? Of course
is simple, we only realize the Scoket connection, the sending and receiving information, in addition to what we have done, in reality, we need to do the processing far more than that, we then look down. Next, we look at how the third party framework is to achieve IM.

IOS instant messaging, from entry to
partition graph.Png
2 let’s take a look at the native Socket based on CocoaAsyncSocket:

This framework implements two transport protocols TCP and UDP, corresponding to GCDAsyncSocket and GCDAsyncUdpSocket, where we focus on GCDAsyncSocket.

Here Socket server continuation of an example, because the same is based on the framework of the native Scoket, so before the Node.js server, the case is still trial. Here we just need to encapsulate the client instance, we still create a single TYHSocketManager.

TYHSocketManager.h

#import < Foundation/Foundation.h> @interface; TYHSocketManager: NSObject + (instancetype) share - (BOOL); connect; disConnect; - (void) - (void) sendMsg: (NSString *) MSG - (void); pullTheMsg; @end

TYHSocketManager.m

#import "TYHSocketManager.h" #import "GCDAsyncSocket.h" for TCP static NSString * / / @ Khost = "127.0.0.1"; static const uint16_t Kport @interface (TYHSocketManager) = 6969; < GCDAsyncSocketDelegate> @end @implementation {GCDAsyncSocket *gcdSocket;} + {TYHSocketManager (instancetype) share static dispatch_once_t onceToken static TYHSocketManager; *instance = nil; dispatch_once (& onceToken, ^{instance = [[self alloc]init]; [instance initSocket]; return instance;};}) - (void) initSocket [[GCDAsyncSocket alloc] initWithDelegate:self {gcdSocket = delegateQueue:dispatch_get_main_queue (#pragma - mark)];} / / external some interface to establish a connection - (BOOL) connect {return [gcdSocke T connectToHost:Khost onPort:Kport error:nil];} / / disconnect - (void) disConnect {[gcdSocket disconnect];} / / message - (void) sendMsg: (NSString * MSG) {NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding]; / / second parameters, [gcdSocket writeData:data withTimeout:-1 tag:110] timeout request;} / / listen to the latest news - (void) pullTheMsg {/ / read data monitoring agent -1 will monitor, no overtime, but only received a message, so every time / receive messages have to call a [gcdSocket readDataWithTimeout:-1 tag:110] #pragma mark GCDAsyncSocketDelegate;} / / - (void) - connected successfully call socket: (GCDAsyncSocket * sock) didConnectToHost: (NSString *) host port: (uint16_t port) {NSLog (@ "connected Power, host:%@, port:%d, host, port); [self pullTheMsg];} / / heartbeat written in this... / / disconnect call connection time - (void) socketDidDisconnect: (GCDAsyncSocket * sock) withError: (nullable * NSError) err {NSLog (@ "disconnected, host:%@, port:%d, sock.localHost, sock.localPort) in this heavy break; / / write / write...} success callback - (void) socket: (GCDAsyncSocket*) sock didWriteDataWithTag: (long tag) {/ / NSLog (@" write ", tag:%ld callback, tag);} / / the message callback - (void) socket: (GCDAsyncSocket * sock * NSData (didReadData:) data) withTag: (long) tag *msg alloc]initWithData:data encoding:NSUTF8StringEncoding] {NSString = [[NSString; NSLog (@"% @ MSG, received a message: "); [self pullTheMsg];} / / segment to obtain message callback //- (VO ID) socket: (GCDAsyncSocket *) sock didReadPartialDataOfLength: (NSUInteger) partialLength tag: (long) tag //{/ / / / NSLog (@ "read callback, length:%ld, tag:%ld, partialLength, tag); / / //} / / the last set of read data (if the timeout continued when the agent is -1, never call to //-) (NSTimeInterval) socket: (GCDAsyncSocket *) sock shouldTimeoutReadWithTag: (long) tag elapsed: (NSTimeInterval) elapsed bytesDone: (NSUInteger) length / NSLog (//{@ to delay, tag:%ld, elapsed:%f, length:%ld, tag, elapsed, length); / / return 10; @end //}

This framework is also very simple to use, it is based on the Scoket up a layer of packaging, provides a OC interface to use. As for the use of methods, we look at the notes should be able to understand that the only thing that needs to be said is that this method:

[gcdSocket readDataWithTimeout:-1 tag:110];

The role of this method is to read unread messages in the current message queue. Remember, this method is not called here, and the message callback agent will never be triggered. And must be the same as tag, if the tag is different, the message received by the agent will not be punished.
we call this method once and can only trigger an agent to read the message once. If we don’t have an unread message when it is called, it will wait until the message is triggered. Once an agent is triggered, we must call this method again, otherwise the message will not be able to trigger the message we are reading. As we used in the example, we call the message every time we read it:

/ / message callback - (void) socket: (GCDAsyncSocket * sock) didReadData: (NSData *) data withTag: (long) tag *msg alloc]initWithData:data encoding:NSUTF8StringEncoding] {NSString = [[NSString; NSLog (@ "message:% @", MSG); [self pullTheMsg];} / / listen to the latest news - (void pullTheMsg) {/ / read data monitoring agent, monitoring only 10 seconds, 10 seconds after the call agent -1 always listening, not overtime, but only received a message, so every time / receive messages have a call [gcdSocket readDataWithTimeout:-1 tag:110];}

In addition, we also need to say is this timeout timeout
here if set 10 seconds, then you can only listen for 10 seconds, after the second call is continued when the proxy method:

- (NSTimeInterval) socket: (GCDAsyncSocket *) sock shouldTimeoutReadWithTag: (long) tag elapsed: (NSTimeInterval) elapsed bytesDone: (NSUInteger) length

If we choose not to continue, then 10 seconds to have not received the message, then Scoket will automatically disconnect. See here some small partners to Tucao, how a way to design so much trouble, of course, so here is the design of its application scenarios, and then we talk about the following.

We also run to see the effect:
IOS instant messaging, from entry to
handle3.gif

At this point, we also use CocoaAsyncSocket framework to achieve a simple IM.

IOS instant messaging, from entry to
partition graph.Png
3 then we continue to look at the webScoket based IM:

In this example, we will beat heartbeat, disconnection, and PingPong mechanism for a simple package, so let’s talk about these three concepts:

First of all, let’s talk about what is heartbeat

Simply put, heartbeat is used to detect whether the TCP connection is available. Then there will be someone to ask, TCP itself is not a KeepAlive mechanism?
here we need to explain is that the TCP KeepAlive mechanism can only guarantee the existence of the connection, but does not guarantee the availability of the client and server:

A server for some reason resulting in high load, CPU 100%, unable to respond to any request, but is still using the TCP probe to determine the connection state, this is a typical connection alive but dead state of the service provider.

This time the heartbeat mechanism is working:

  • Our client initiated Ping heartbeat (usually the client), if set in 10 seconds if you do not receive the callback, so that a server or client, then we need to take the initiative to disconnect.
  • The server is the same, it will maintain a heartbeat interval socket, when the agreed time, the client has not received a heartbeat, we will know that the connection has failed, and then take the initiative to disconnect.

Reference article: why do you say that the TCP based mobile IM still need to live heart beat?

In fact, do IM small partners know, we really need to feel the cause of the heartbeat mechanism is mainly due to domestic operators NAT timeout.

So what exactly is the NAT timeout?

This is because the IPV4 caused, we are likely to be on the Internet in a NAT device (wireless router, etc.).
NAT devices will modify the source / destination IP address in the IP packet through the device. For home router, use a network address and port translation (NAPT), it not only changed the IP port number also modifies TCP and UDP protocol, which allows devices share the same network IP. network for example NAPT, maintain a similar table NAT table:

IOS instant messaging, from entry to
NAT mapping

The
NAT device according to NAT table changes to go out and come in the data, for example, 192.168.0.3:8888 will send out packets into 120.132.92.21:9202, they think they are in the external communication. At the same time, NAT and 120.132.92.21:9202 devices will 120.132.92.21:9202 the received packet IP and port into 192.168.0.3:8888, and then sent to the Intranet hosts, such internal and external can the two-way communication, but if 192.168.0.3:8888 = = 120.132.92.21:9202 this mapping for some reason is NAT equipment eliminated, so the external device can not directly communicate with 192.168.0.3:8888.

Our equipment is often in the back of the NAT equipment, for example, in the university campus network, check their assigned to IP, is that we in the network IP, NAT equipment behind, if we are in the bedroom, followed by a router, then we will be more data packets sent by NAT.

Domestic mobile wireless network operators in the link over a period of time without data communication, will eliminate the NAT table of the corresponding, resulting in a link interrupt.

The domestic operators generally NAT timeout time of 5 minutes, so we usually set the heartbeat interval of 3-5 minutes.

Then we talk about the PingPong mechanism:

Many small partners may feel confused, then we are in the heartbeat interval of 3-5 minutes if the connection false online (such as in the subway elevator environment). So we can not guarantee the authenticity of the message? This is obviously unacceptable to us, so the industry’s solution is to adopt a two-way PingPong mechanism.

IOS instant messaging, from entry to

When the server sends a Ping client, did not return the response of ACK in the agreed time, is that the client is not online, then we will take the initiative to end Server disconnect Scoket, sending a message from the APNS and push the way.
is the same, when the client to send a message, because we have not received in response to the ACK Server package, is to show that the client or server is not online, we will display the message failed to send, and disconnect Scoket.

Remember what we said in the previous CocoaSyncSockt example, when the access message timeout is disconnected? In fact, it is a PingPong mechanism to achieve the client. Every time we can send a message after the success of the call to read the timeout method, if a period of time did not receive a response from the server, then the connection is not available, then disconnect the Scoket connection

Finally, the reconnection mechanism:

In theory, we take the initiative to disconnect the Scoket connection (such as exit account, APP exit to the background, etc.), do not need to reconnect. The other connections are disconnected, and we all need to disconnect. The general solution for
is to try to reconnect several times, and if it is still unable to reconnect, then it will not be repeated.
next WebScoket example, I will package a reconnection time exponential growth of a reconnection, can be used as a reference.

Well, after we finish these three concepts, we will tell a WebScoket one of the most representative of the third party SocketRocket framework.

We first look at some of its external packaging methods:

@interface SRWebSocket: NSObject < NSStreamDelegate> @property (nonatomic, weak) ID < SRWebSocketDelegate> delegate; @property (nonatomic, readonly) SRReadyState readyState; @property (nonatomic, readonly, retain) NSURL *url @property (nonatomic, readonly); CFHTTPMessageRef receivedHTTPHeaders; array of cookies / / Optional (NSHTTPCookie objects) to apply to the connections @property (nonatomic, readwrite) NSArray * requestCookies; / / This returns the negotiated protocol. will be nil until / / It after the handshake completes. @property (nonatomic, readonly, copy) NSString *protocol should be an; / / Protocols array of strings that turn into Sec-WebSocket-Protocol. (ID) initWithURLRequest: (NSURLRequest * protocols: (request) NSArray *) protocols allowsUntrustedSSLCertificates: (BOOL) allowsUntrustedSSLCertificates; (ID) - initWithURLRequest: (NSURLRequest * request) protocols: (NSArray * protocols); - (ID) initWithURLRequest: (NSURLRequest * request); / / Some helper constructors. (ID) initWithURL: (NSURL * URL) protocols: (NSArray *) protocols allowsUntrustedSSLCertificates: (BOOL) allowsUntrustedSSLCertificates; - (ID) initWithURL: (NSURL * URL) protocols: (NSArray * protocols); - (ID) initWithURL: (NSURL * URL); / / Delegate queue will be dispatch_main_queue by default. You set both OperationQueue and / cannot dispatch_queue. - (void) setDelegateOperationQueue: (NSOperationQueue*) queue; (void) - setDelegateDispatchQueue: (dispatch_queue_t) queue; it will / / By default, schedule its Elf on +[NSRunLoop SR_networkRunLoop] using defaultModes. (void) scheduleInRunLoop: (NSRunLoop * aRunLoop) forMode: (NSString * mode); - (void) unscheduleFromRunLoop: (NSRunLoop * aRunLoop) forMode: (NSString * mode); / / SRWebSockets are intended for one-time-use only. Open should be called once and only once. - (void) - void (open; close); - (void) closeWithCode: (NSInteger) code reason: (NSString * reason); / / Send a UTF8 String or Data. (void) send: (ID) data; / / Send Data (can be nil) in a ping message. (void) sendPing: (NSData * data); @end #pragma mark - SRWebSocketDelegate @protocol SRWebSocketDelegate < NSObject> will either be an / / message NSString if the server is using text or NSData if the server is / using binary. - (void) webSo Cket: (SRWebSocket *) webSocket didReceiveMessage: (ID) message; @optional (void) webSocketDidOpen: (SRWebSocket * webSocket); - (void) webSocket: (SRWebSocket * webSocket) didFailWithError: (NSError * error); - (void): webSocket (SRWebSocket *) webSocket didCloseWithCode: (NSInteger) code reason: (NSString *) reason wasClean: (wasClean; BOOL) - (void) webSocket: (SRWebSocket * webSocket) didReceivePong: (NSData * pongPayload); / / Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages.; Defaults to YES. (BOOL) webSocketShouldConvertTextFrameToString: (SRWebSocket * webSocket); @end

The method is also very simple, divided into two parts:

  • Part of the initialization of the SRWebSocket, as well as the connection, close the connection, send messages and other methods.
  • The other part is SRWebSocketDelegate, including:
    receives the message callback callback, the callback connection fails, close the connection from the pong callback, the callback, the need to convert string to data news agency method into.
Then we will give an example to achieve the following, first to encapsulate a single TYHSocketManager:

TYHSocketManager.h

#import < Foundation/Foundation.h> typedef; enum: NSUInteger {disConnectByUser, disConnectByServer}, DisConnectType; @interface: NSObject + TYHSocketManager (instancetype) - (void) share; connect; disConnect; - (void) - (void) sendMsg: (NSString *) MSG - (void); Ping; @end

TYHSocketManager.m

#import "TYHSocketManager.h" #import "SocketRocket.h" #define dispatch_main_async_safe (block) / if ([NSThread isMainThread]) {/ (block);} / else {/ dispatch_async (dispatch_get_main_queue), block (static NSString);} / * Khost = @ "127.0.0.1"; static const uint16_t Kport @interface (TYHSocketManager) = 6969; < SRWebSocketDelegate> {SRWebSocket *webSocket; NSTimer *heartBeat; NSTimeInterval reConnectTime;} @end @implementation + TYHSocketManager (instancetype) share static dispatch_once_t static TYHSocketManager {onceToken; *instance = nil; dispatch_once (& onceToken, instance ^{= [[self alloc]init]; [instance initSocket]; return instance;}}); / / early When the connection - (void) initSocket if (webSocket) {{return}; webSocket = [[SRWebSocket alloc]initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@ "ws://%@%d", Khost, Kport]]]; webSocket.delegate = self; / / queue NSOperationQueue *queue proxy thread [[NSOperationQueue = alloc]init]; queue.maxConcurrentOperationCount = 1; [webSocket setDelegateOperationQueue:queue] [webSocket; / / open] / / initialize connection;} heartbeat - (void) initHeartBeat ([self ^{{dispatch_main_async_safe destoryHeartBeat]; __weak typeof (self) weakSelf = self; / / heartbeat timeout is set to 3 minutes, usually 5 minutes NAT heartBeat = [NSTimer scheduledTimerWithTimeInterval:3*60 re Peats:YES block:^ (NSTimer * _Nonnull timer) {NSLog (@ Heart); / / server and agreed to send what is the heart logo, as much as possible reduce the heartbeat packet size [weakSelf sendMsg:@ "heart"];}]; [[NSRunLoop currentRunLoop]addTimer:heartBeat forMode:NSRunLoopCommonModes];})} / / - (void) destoryHeartBeat cancel heart {dispatch_main_async_safe (if ^{(heartBeat [heartBeat) {invalidate]; heartBeat = nil;}})} - #pragma mark external connection interface / / - (void) connect {[self initSocket]; / / when normal connection reset reconnection time reConnectTime = 0;} / / disconnect - (void) disConnect if (webSocket) {{ [webSocket close]; webSocket = nil;}} / / send messages (void) sendMsg: (NSString * MSG) {[webSocket send:msg];} / / reconnection mechanism - (void) reConnect {[self disConnect]; / / no more than a minute even so will only even 5 2^5 = 64 If (reConnectTime > 64 {return}); dispatch_after (dispatch_time (DISPATCH_TIME_NOW (int64_t) (reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue (webSocket), ^{= nil; [self initSocket];}); / / reconnection time 2 refers to the number level increase of if (reConnectTime = = 0) {reConnectTime = 2;}else{reConnectTime = 2;}} //pingPong - (void) ping{[webSocket sendPing:nil];} #pragma - SRWebSocketDelegate - mark (void) webSocket: (SRWebSocket *) webSocket didReceiveMessage: (ID message) {NSLog (@ "server receives the message:% @", message);} - (void) webSocketDidOpen: (SRWebSocket * webSocket) {NSLog (@ "connection"); / / connection success began to send heartbeat [self initHeartBeat];} call time the failure of //open - (void) webSocket: (SRWebSocket * webSocket) didFailWithError: (NSError * error) {NSLog (@../n%@, error connection failed... "); / / failed to reconnect [self reConnect];} / / network connection interruption is called - (void) webSocket: (SRWebSocket *) webSocket didCloseWithCode: (NSInteger) code reason: (NSString *) reason wasClean: (BOOL wasClean) {NSLog (@ code:%ld, reason:%@ connection is closed, wasClean:,%d, code, reason, wasClean"); / / if it is to be The user then interrupts directly disconnected, or even start if (code = = disConnectByUser) {[self disConnect];}else{[self reConnect];} / / disconnect destroyed when the heartbeat [self destoryHeartBeat]; when //sendPing}, if the network through it, you will receive the callback, but must ensure that ScoketOpen, or crash (void) webSocket: (SRWebSocket * webSocket) didReceivePong: (NSData * pongPayload) {NSLog (@ "received Pong callback");} / / will receive the message, whether you need to convert data to NSString, the message will be called each received, the default YES //- (BOOL) webSocketShouldConvertTextFrameToString: (SRWebSocket * webSocket) //{/ / NSLog (@ "webSocketShouldConvertTextFrameToString"); / / return / / NO //};

The.M file is a bit long, you can refer to GitHub demo for reading, we add some details of things, including a simple heartbeat, reconnection mechanism, there is a good package webScoket pingpong mechanism.
code is very simple, we can read with the notes, it should be easy to understand.
need to say is that the heartbeat mechanism is a time interval, often we may have a more complex implementation, such as when we are sending a message, you may not need a heartbeat. When the heart is not in the open when the heartbeat. WeChat has a higher end of the way to achieve the interest of small partners can look at:
WeChat intelligent heartbeat to achieve

There is one thing to say is this reconnection mechanism in demo, I used the exponential growth of 2, the first time immediately re connected, second times third times 2 seconds, 4 seconds, fourth seconds 8… Until more than 64 seconds longer reconnection. And any successful connection, will reset the reconnection time.

The last thing we need to say is that this framework gives us the encapsulation of the webscoket before calling its sendPing method, we must determine whether the current Scoket connection, if not connected, the program will be crash.

The realization of the client is roughly the same, then we need to implement a server, to see the actual communication effect.

WebScoket server implementation

Here we can not follow the previous node.js example, because this is not a native Scoket, this is the webScoket server, so we also need to comply with the webScoket agreement, both can achieve communication.
actually here to achieve is also very simple, I used the node.js WS module, only need to use NPM to install ws.
what is NPM? For example, NPM is equivalent to cocospod for Node.js as iOS, it is an extension of a management tool module. If you do not know how to use can look at this article: the use of NPM

We enter the current script directory, enter the terminal command, you can install the WS module:

$NPM install WS

If you are too lazy to look at NPM’s small partner does not matter, direct download GitHub WSServer.js file can be run.
the source code is as follows:

Var WebSocketServer require ('ws') =.Server, WSS = new (WebSocketServer {port: 6969}); wss.on ('connection', function (WS) {console.log ('Client connected'); ws.send ("you are a' + wss.clients.length + ''); / / receive the message callback ws.on ('message', function (message) {console.log (message); ws.send (" received:'+message);}); / / exit ('close', function chat ws.on (close) {console.log ('exit connection');});}); console.log (6969 '' start listening port);

Not a few lines of code, easy to understand.
is listening to the 6969 port of the machine, if the client is connected, print lient connected, and sent to the client: you are the first.
if the client message is received, print the message and send the message to the client.

Then we also run to see the effect:
IOS instant messaging, from entry to

run we can see that the initiative to disconnect the connection, not to re connect, while the server side off, we opened the reconnection. Interested friends can download the actual operation of demo.

IOS instant messaging, from entry to
partition graph.Png
4 let’s take a look at MQTT:

MQTT is a chat protocol, which is higher than webScoket, belonging to the application layer.
its basic model is a simple release subscription, that is, when a message is sent out, who subscribe who will receive. In fact, it is not suitable for IM scenarios, such as to achieve some simple IM scene, but requires a lot of complex processing.
is more suitable for it to subscribe for the release of this model, such as WeChat’s real-time sharing of location, the map of the cart on the move, client push and other functions.

First, we look at the framework based on the MQTT protocol -MQTTKit:
framework is written in C, some of the methods in the MQTTKit class, the external OC to call, we take a look at this class:

@interface MQTTClient: NSObject struct {mosquitto *mosq;} @property (readwrite, copy) NSString *clientID @property (readwrite, copy); NSString *host; @property (readwrite, assign) unsigned short port; @property (readwrite, copy) NSString *username @property (readwrite, copy); NSString *password; @property (readwrite, assign) unsigned short keepAlive; @property (readwrite, assign) BOOL cleanSession; @property (nonatomic, copy) MQTTMessageHandler + messageHandler (void); initialize; version; + (NSString*) - (MQTTClient*) initWithClientId: (NSString * clientId); - (void) setMessageRetry: (NSUInteger) seconds; #pragma mark Connection (void) connectWithCompletionHandler: (void (^) (MQTTConnectionReturnCode code) - completionHandler); (void) connectToHost: (NSString*) host (completionHandler: (void ^) (MQTTConnectionReturnCode code) - (void) completionHandler; disconnectWithCompletionHandler: (void) (^) (NSUInteger code) - (completionHandler); reconnect; void) - (void) setWillData: (NSData * payload) toTopic: (NSString *) willTopic withQos: (MQTTQualityOfService) willQos retain: (BOOL) retain; (void) - setWill: (NSString * payload) toTopic: (NSString *) willTopic withQos: (MQTTQualityOfService) willQos retain: (BOOL) - (void) retain; clearWill; #pragma mark Publish (void) publishData: (NSData * payload) toTopic: (NSString *) topic withQos: (MQTTQualityOfService) QoS retain: (BOOL) retain compl EtionHandler: (void (^) (int mid) - completionHandler); (void) publishString: (NSString * payload) toTopic: (NSString *) topic withQos: (MQTTQualityOfService) QoS retain: (BOOL) retain (completionHandler: (void ^) (int mid) completionHandler; #pragma mark) - Subscribe - (void) subscribe: (NSString * topic) withCompletionHandler: (MQTTSubscriptionCompletionHandler) completionHandler; (void) - subscribe: (NSString *) topic withQos: (MQTTQualityOfService) QoS completionHandler: (MQTTSubscriptionCompletionHandler) completionHandler; (void) - unsubscribe: (NSString * topic) (withCompletionHandler: (void ^) (void)) completionHandler;

This class is divided into 4 parts: initialization, connection, publish, subscribe, the role of specific methods can look at the name of the method to understand, we then use this framework to encapsulate an instance.

Similarly, we encapsulate a single instance of MQTTManager.
MQTTManager.h

#import < Foundation/Foundation.h> @interface MQTTManager; NSObject + (instancetype) share; - (void) connect; - (void) disConnect; - (void) sendMsg: (NSString *) MSG; @end

MQTTManager.m

#import "MQTTManager.h" #import "MQTTKit.h" static NSString * Khost = @ "127.0.0.1"; static const uint16_t Kport = 6969; static = NSString * KClientID @ "tuyaohui"; @interface MQTTManager (MQTTClient) {*client}; @end @ implementation + MQTTManager (instancetype) share static dispatch_once_t static MQTTManager {onceToken; *instance = nil; dispatch_once (& onceToken, instance ^{= [[self alloc]init]; return instance;}}); / / initialize connection - (void) initSocket (client) {if} {[self disConnect]; client [[MQTTClient alloc] = initWithClientId:KClientID]; client.port = Kport; [client setMessageHandler:^ (MQTTMessage *message) {/ / the message callback, before Provided is to subscribe to the NSString *msg [[NSString alloc]initWithData:message.payload (NSLog = encoding:NSUTF8StringEncoding]; @ server received message:% @ ", MSG);}]; [client connectToHost:Khost completionHandler:^ (MQTTConnectionReturnCode code) {switch (code) {case ConnectionAccepted: (@ NSLog" MQTT connection "); / / subscribe to their ID messages, it can receive the message [client subscribe:client.clientID withCompletionHandler:^ (NSArray *grantedQos callback) {NSLog (@" subscribe to tuyaohui "); break; case}]; ConnectionRefusedBadUserNameOrPassword: NSLog (@" wrong username Password "); / /... Default:. NSLog (@" MQTT "connection failed); break;}}}]; #pragma mark - some of the foreign interface connection / / - (void) connect {[self initSocket];} / / disconnect - (void) disConnect if (client) {{/ / unsubscribe [client unsubscribe:client.clientID withCompletionHandler:^{NSLog (@" unsubscribe tuyaohui success "); / /}]; disconnect the [client disconnectWithCompletionHandler:^ (NSUInteger code) {NSLog (@" disconnect MQTT success ");}]; client = nil;}} / / send messages (void) sendMsg: (NSString * MSG) {/ / send a message send them to subscribe to the theme, [client publi ShString:msg toTopic:KClientID withQos:ExactlyOnce retain:YES completionHandler:^ (int mid) {@end}}];

The realization of the code is very simple, you need to say is:
1) when we connect successfully, we need to subscribe to their clientID news, so as to receive the message sent to their own.
2) followed by this framework for us to implement a QOS mechanism, then what is QOS?

QoS (Quality of Service, quality of service) refers to a network can use all kinds of basic technology, provide better service for the network communication capacity specified, is a security mechanism of the network, is a technology used to solve the network delay and congestion.

Here, it offers three options:

Typedef enum MQTTQualityOfService: NSUInteger {AtMostOnce, AtLeastOnce, ExactlyOnce} MQTTQualityOfService;

Corresponding to send a maximum of at least once, send only once.

  • QOS (0), send a maximum: if the message has not been sent in the past, then the direct loss.
  • QOS (1), sent at least once: to ensure that the message must be sent in the past, but a few times uncertain.
  • QOS (2), send only once: it will have a very complex transmission mechanism, to ensure that the message is sent, and only once.

A more detailed look at the mechanism can be seen in this article: MQTT protocol notes message flow QOS.

The same we need a MQTT protocol to achieve the server, we still node.js to achieve, this time we still need to use NPM to add a module mosca.
we look at the server code:
MQTTServer.js

Var Mosca = require ('mosca'); VAR MqttServer = new mosca.Server ({port: 6969}); MqttServer.on ('clientConnected', function (client) {console.log ('receive client connections, connect ID:', client.id);}); / * * * MQTT theme message **/ MqttServer.on monitor ('published', function (packet, client) {var topic = packet.topic; console.log (' news','topic:'+topic+', message: packet.payload.toString (+));}); MqttServer.on ('ready'), function ({console.log ('mqtt server open, listening port 6969 ');});

Server code is not a few lines, opened a service, and listen to the native 6969 port. And monitor the client connection, such as the release of information.

Then we also run to see the effect:
IOS instant messaging, from entry to

At this point, we implemented a simple MQTT package.

5.XMPP:XMPPFramework framework

The result is that there is no XMPP… Because personal feeling XMPP for IM is really unbearable. Only as a toy demo, for everyone to practice. There are too many online XMPP content, a considerable part of the server to do with openfire, this set of things is too old. Remember that many years ago, this set of things… The landlord IM acquaintance is to use
if you are interested can take a look at this article: XMPPFramework iOS. This is not an example.

Three, on the choice of IM transmission format:

The article quoted the God Chen Yilong (iOS, a terrier program):
ProtocolBuffer Payload
to reduce the taxi drops 40%;
Ctrip before sharing, that is using Protocol Buffer +Gzip data format of the new compressed Payload size reduced by 15%-45%. Data serialization time decreased by 80%-90%.

The use of efficient and secure private protocol to support the long connection multiplexing, stable electricity saving traffic
[efficiency] to improve the success rate of network requests, the larger the message body, the probability of failure will increase.
[provincial flow] very little flow consumption, provincial traffic. A message data with the size of the Protobuf after the 1/10 is JSON, XML format 1/20, binary serialization of 1/10. Compared with XML, Protobuf performance advantage is obvious. It is stored in an efficient binary way, 3 to 10 times smaller than XML, fast from 20 to 100 times.
[power saving] [
(high efficiency heartbeat packet) heartbeat packet protocol on the IM power and traffic impact on the heartbeat packet protocol on minimalist design: only 1 Byte. [
] developers through easy to use according to the syntax definition of structured message formats to some, and then sent to a command line tool tool will automatically generate the related classes, can support Java, c++, python, Objective-C and other language environment. Through the inclusion of these classes in the project, you can easily call the relevant methods to complete the business message serialization and anti serialization. Language support: native support for c++, Java, python, Objective-C, up to more than 10 languages. 2015-08-27 Protocol v3.0.0-beta-1 released Objective-C (Alpha) version, 2016-07-28 3 Protocol Buffers V3.0.0 official release, the official support of Objective-C Buffers.
[reliable] WeChat and mobile phone QQ mainstream IM applications have long been using it (using a modified Protobuf protocol)

IOS instant messaging, from entry to

How to test and verify the high performance of Protobuf?
on the data of 100 times, 1000 times, 10000 times and 100000 times of
test, the ordinate is the completion time, in milliseconds,

bytes
serialization deserialization

IOS instant messaging, from entry to
IOS instant messaging, from entry to
IOS instant messaging, from entry to

Data sources.

IOS instant messaging, from entry to

Data from project thrift-protobuf-compare, test for the Total Time, also refers to the entire operation time of an object, including creating objects to serialize an object as a sequence of bytes in memory, the whole process and then deserialization. From the test results can be seen in Protobuf with good marks.
disadvantages:
may cause the APP package volume provided by Google script generated Model, will be very huge, a Model package, will be followed by larger volume.
if the Model is too large, may cause the volume surge of APP package, but the IM service of Model is very small, for example in ChatKit-OC only one Protobuf Model:Message object, the effect of very little volume package.
in the course of the use of a reasonable balance between the volume and the transmission efficiency of the problem, where the network is said to have to reduce the volume of the package, thereby reducing the use of Protobuf.

To sum up, we choose the format of transmission: ProtocolBuffer > Json > XML

If all of the usage of the ProtocolBuffer interested can refer to this article: two
ProtocolBuffer for Objective-C ProtocolBuffer
configuration and running environment of iOS structures and sample demo

Three, IM some other problems
Reliability of 1.IM:

We have previously mentioned in the example:
heartbeat mechanism, PingPong mechanism, disconnection reconnection mechanism, as well as what we call the QOS mechanism. These are used to ensure the availability of connections, instant messaging and accurate delivery, etc.. The contents of
to ensure the reliability of our IM services, we can actually do a lot: for example, when we use the file transfer slice upload, HTTP, second transmission technology to guarantee the transmission of the file.

2 security:

We also need some security mechanisms to ensure the safety of our IM communications.
example: to prevent DNS pollution, account security, third party server authentication, single sign on, etc.

3 some other optimizations:

Similar to WeChat, the server does not do chat records stored in the machine, only cache, which can reduce the server data request, on the one hand reduce the pressure on the server, on the other hand, reduce client traffic consumption.
we try to use HTTP connection when the upper API, similar to NSUrlSession. The network framework to use AFNetWorking3. Because these upper level network requests are HTTP/2, we can reuse them when we request them.

More optimization related content can refer to refer to this article:
IM instant messaging technology in multi application scenarios, as well as performance tuning

Four, audio and video calls

IM application of real-time audio and video technology, IM development is almost the last high wall. The reason is that the real time audio and video technology is the application of audio and video processing technology and network transmission technology.
real-time audio and video technology to achieve the main contents include: audio and video collection, coding, network transmission, decoding, playback and other links. So many simple technical applications, if not properly, will be encountered in the actual development process one after another pit.

Because of the landlord himself understand the technology is very shallow, so refers to a series of articles to give you a reference, interested friends can see:
“instant messaging audio and video development (1): video encoding and decoding theory of”
“instant messaging audio and video development (two): video coding the decoding of digital video introduced”
“instant messaging audio and video development (three): video encoding and decoding for
based encoding” instant messaging “audio and video development (four): video encoding and decoding prediction technology introduced” instant messaging “
audio and video development (five): H.264″
“instant mainstream video encoding technology the communication of audio and video development (six): how to start an audio codec technology study”
“audio and video instant messaging The development of (seven): audio encoding principle and basic entry “
” instant messaging audio and video development (eight): real-time voice communication encoding standard “common
” instant messaging audio and video development (nine): echo and echo of real-time voice communication to eliminate the “
” overview of instant messaging audio and video development (ten): real time voice echo cancellation technology. “
” instant messaging audio and video development (eleven): real time voice communication technology “
” packet loss compensation instant communication of audio and video development (twelve): multi people real-time audio and video chat on “
” architecture of instant messaging audio and video development (thirteen): the characteristics and advantages of the real-time video “H.264
” encoding audio and video instant messaging development (fourteen): the number of real-time audio and video According to the “
” protocol instant messaging, audio and video development (fifteen): the application of P2P and real-time audio and video chat “
” instant messaging audio and video development (sixteen): several suggestions “real-time audio and video development of mobile terminal
” instant communication of audio and video development (seventeen): H.264, V8 video encoding the past and present “

Write in the end:

This article is original, and only on behalf of the landlord at this stage of some of the ideas, if there is any error, please correct me!

In case someone reproduced, please indicate the source of trouble.