有一段时间没有认真总结和写博客了

前段时间找工作、进入工作阶段。比较少静下来认真总结,现在静下心来总结一下最近的一些心得

  • 前言
  • AsyncSocket介绍
  • AsyncSocket详解
  • AsyncSocket示例

一、前言

公司的项目用到了Socket编程,之前在学习的过程当中,用到的更多的还是http请求的方式。但是既然用到了就必须学习一下,所以就在网上找一些例子,然后想自己写一个demo。可是发现很多写iOS Socket的博客并没有很详细的说明,也可能是大神们觉得其他东西都浅显易懂。

自己专研了一下,将自己的一些理解总结出来,一方面整理自己的学习思路,另一方面,为一些和我有同样困惑的小伙伴们,稍做指引。


二、AsyncSocket介绍

1⃣️iOS中Socket编程的方式有哪些?

-BSD Socket

BSD Socket 是UNIX系统中通用的网络接口,它不仅支持各种不同的网络类型,而且也是一种内部进程之间的通信机制。而iOS系统其实本质就是UNIX,所以可以用,但是比较复杂。

-CFSocket

CFSocket是苹果提供给我们的使用Socket的方式,但是用起来还是会不太顺手。当然想使用的话,可以细细研究一下。

-AsyncSocket

这次博客的主讲内容,也是我们在开发项目中经常会用到的。

2⃣️为什么选择AsyncSocket?

iphone的CFNetwork编程比较艰深。使用AsyncSocket开源库来开发相对较简单,帮助我们封装了很多东西。


三、AsyncSocket详解

1⃣️说明

在我们开发当中,我们主要的任务是开发客户端。所以详解里主要将客户端的整个连接建立过程,以及在说明时候回调哪些函数。在后面的示例代码中,也会给出服务器端的简单开发。

2⃣️过程详解

1.建立连接

- (int)connectServer:(NSString *)hostIP port:(int)hostPort

2.连接成功后,会回调的函数

- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port

3.发送数据

- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

4.接受数据

-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

5.断开连接

- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err

- (void)onSocketDidDisconnect:(AsyncSocket *)sock

主要就是上述的几个方法,只是说在真正开发当中,很可能我们在收发数据的时候,我们收发的数据并不仅仅是一个字符串包装成NSData即可,我们很可能会发送结构体等类型,这个时候我们就需要和服务器端的人员协作来开发:定义怎样的结构体。


四、AsyncSocket示例

客户端代码

#import "ViewController.h"

#define SRV_CONNECTED 0
#define SRV_CONNECT_SUC 1
#define SRV_CONNECT_FAIL 2
#define HOST_IP @"192.168.83.40"
#define HOST_PORT 8008 @interface ViewController ()
{
NSString *_content;
}
-(int) connectServer: (NSString *) hostIP port:(int) hostPort;
-(void)showMessage:(NSString *) msg;
@end @implementation ViewController @synthesize clientSocket,tbInputMsg,lblOutputMsg; #pragma mark - view lifecycle
- (void)viewDidLoad
{
[super viewDidLoad]; [self connectServer:HOST_IP port:HOST_PORT];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[clientSocket release], clientSocket = nil;
[tbInputMsg release], tbInputMsg = nil;
[lblOutputMsg release], lblOutputMsg = nil;
} - (int)connectServer:(NSString *)hostIP port:(int)hostPort
{
if (clientSocket == nil)
{
// 在需要联接地方使用connectToHost联接服务器
clientSocket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
if (![clientSocket connectToHost:hostIP onPort:hostPort error:&err])
{
NSLog(@"Error %d:%@", err.code, [err localizedDescription]); UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[@"Connection failed to host" stringByAppendingString:hostIP] message:[NSString stringWithFormat:@"%d:%@",err.code,err.localizedDescription] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
[alert release];
return SRV_CONNECT_FAIL;
} else {
NSLog(@"Connected!");
return SRV_CONNECT_SUC;
}
}
else {
return SRV_CONNECTED;
}
} #pragma mark - IBAction
// 发送数据
- (IBAction) sendMsg:(id)sender
{
NSString *inputMsgStr = tbInputMsg.text;
NSString * content = [inputMsgStr stringByAppendingString:@"\r\n"];
NSLog(@"%@",content);
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
// NSData *data = [content dataUsingEncoding:NSISOLatin1StringEncoding];
[clientSocket writeData:data withTimeout:- tag:];
}
// 连接/重新连接
- (IBAction) reconnect:(id)sender
{
int stat = [self connectServer:HOST_IP port:HOST_PORT];
switch (stat) {
case SRV_CONNECT_SUC:
[self showMessage:@"connect success"];
break;
case SRV_CONNECTED:
[self showMessage:@"It's connected,don't agian"];
break;
default:
break;
}
}
- (void)showMessage:(NSString *)msg
{
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"Alert!"
message:msg
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
}
- (IBAction)textFieldDoneEditing:(id)sender
{
[tbInputMsg resignFirstResponder];
}
- (IBAction)backgroundTouch:(id)sender
{
[tbInputMsg resignFirstResponder];
} #pragma mark socket delegate
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
[clientSocket readDataWithTimeout:- tag:];
} - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
NSLog(@"Error");
} - (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
NSString *msg = @"Sorry this connect is failure";
[self showMessage:msg];
[msg release];
clientSocket = nil;
} - (void)onSocketDidSecure:(AsyncSocket *)sock
{
} // 接收到数据(可以通过tag区分)
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString* aStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
_content = lblOutputMsg.text;
NSLog(@"Hava received datas is :%@",aStr);
NSString *newStr = [NSString stringWithFormat:@"\n%@", aStr];
lblOutputMsg.text = [_content stringByAppendingString:newStr];
[aStr release];
[clientSocket readDataWithTimeout:- tag:];
} @end

服务器端代码

#import "SocketView.h"
#import "AsyncSocket.h" #define WELCOME_MSG 0
#define ECHO_MSG 1 #define FORMAT(format, ...) [NSString stringWithFormat:(format), ##__VA_ARGS__] @interface SocketView (PrivateAPI)
- (void)logError:(NSString *)msg;
- (void)logInfo:(NSString *)msg;
- (void)logMessage:(NSString *)msg;
@end @implementation SocketView // 初始化
- (void)awakeFromNib
{
listenSocket = [[AsyncSocket alloc] initWithDelegate:self];
[listenSocket setRunLoopModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; connectedSockets = [[NSMutableArray alloc] initWithCapacity:];
isRunning = NO; [logView setString:@""];
// [portField setString:@"8080"];
} - (IBAction)startStop:(id)sender
{
if(!isRunning)
{
int port = [portField intValue]; if(port < || port > )
{
port = ; // 会随即取端口
} NSError *error = nil;
if(![listenSocket acceptOnPort:port error:&error])
{
[self logError:FORMAT(@"Error starting server: %@", error)];
return;
} [self logInfo:FORMAT(@"Echo server started on port %hu", [listenSocket localPort])];
isRunning = YES; [portField setEnabled:NO];
[startStopButton setTitle:@"Stop"];
}
else
{
// Stop accepting connections
[listenSocket disconnect]; // Stop any client connections
int i;
for(i = ; i < [connectedSockets count]; i++)
{
// Call disconnect on the socket,
// which will invoke the onSocketDidDisconnect: method,
// which will remove the socket from the list.
[[connectedSockets objectAtIndex:i] disconnect];
} [self logInfo:@"Stopped Echo server"];
isRunning = false; [portField setEnabled:YES];
[startStopButton setTitle:@"Start"];
}
} - (void)scrollToBottom
{
NSScrollView *scrollView = [logView enclosingScrollView];
NSPoint newScrollOrigin; if ([[scrollView documentView] isFlipped])
newScrollOrigin = NSMakePoint(0.0, NSMaxY([[scrollView documentView] frame]));
else
newScrollOrigin = NSMakePoint(0.0, 0.0); [[scrollView documentView] scrollPoint:newScrollOrigin];
} - (void)logError:(NSString *)msg
{
NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:];
[attributes setObject:[NSColor redColor] forKey:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes];
[as autorelease]; [[logView textStorage] appendAttributedString:as];
[self scrollToBottom];
} - (void)logInfo:(NSString *)msg
{
NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:];
[attributes setObject:[NSColor purpleColor] forKey:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes];
[as autorelease]; [[logView textStorage] appendAttributedString:as];
[self scrollToBottom];
} - (void)logMessage:(NSString *)msg
{
NSString *paragraph = [NSString stringWithFormat:@"%@\n", msg]; NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:];
[attributes setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName]; NSAttributedString *as = [[NSAttributedString alloc] initWithString:paragraph attributes:attributes];
[as autorelease]; [[logView textStorage] appendAttributedString:as];
[self scrollToBottom];
} - (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
[connectedSockets addObject:newSocket];
} // 客户连接成功!
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
[self logInfo:FORMAT(@"Accepted client %@:%hu", host, port)]; NSString *welcomeMsg = @"恭喜您,已经通过scoket连接上服务器!";
NSData *welcomeData = [welcomeMsg dataUsingEncoding:NSUTF8StringEncoding]; [sock writeData:welcomeData withTimeout:- tag:WELCOME_MSG]; // We could call readDataToData:withTimeout:tag: here - that would be perfectly fine.
// If we did this, we'd want to add a check in onSocket:didWriteDataWithTag: and only
// queue another read if tag != WELCOME_MSG.
} - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
[sock readDataToData:[AsyncSocket CRLFData] withTimeout:- tag:];
}
// 接收到数据
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSData *strData = [data subdataWithRange:NSMakeRange(, [data length] - )];
NSString *recvMsg = [[[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding] autorelease];
if(recvMsg)
{
[self logMessage:recvMsg];
}
else
{
[self logError:@"Error converting received data into UTF-8 String"];
}
NSString *backStr = nil;
for (AsyncSocket *socket in connectedSockets) {
if ([sock isEqualTo:socket]) {
backStr = [NSString stringWithFormat:@"我说: %@",recvMsg];
} else {
backStr = [NSString stringWithFormat:@"他说: %@",recvMsg];
}
} // 回发数据
NSData* backData = [backStr dataUsingEncoding:NSUTF8StringEncoding];
[sock writeData:backData withTimeout:- tag:ECHO_MSG];
} - (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
{
[self logInfo:FORMAT(@"Client Disconnected: %@:%hu", [sock connectedHost], [sock connectedPort])];
} - (void)onSocketDidDisconnect:(AsyncSocket *)sock
{
[connectedSockets removeObject:sock];
} @end

界面搭建

iOS项目开发之Socket编程的更多相关文章

  1. 答题小程序开发之socket编程 微信小程序答题 直播答题开发 直播弹幕使用web socket编程

    最近有一个项目很火,那就是直播答题的,接到公司的这个任务,开发直播答题的聊天室功能.在线的人相互聊天.之前做过类似的,当时都是使用的ajax轮询的,这种非常的耗费服务器.所以这次就开始使用socket ...

  2. iOS开发之Socket通信实战--Request请求数据包编码模块

    实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncS ...

  3. 《苹果开发之Cocoa编程》挑战1 创建委托 练习

    <苹果开发之Cocoa编程>第4版 P87 新建一个单窗口应用程序,设置某对象为窗口的委托,当用户调整窗口尺寸时,确保窗口高度为宽度的2倍. 需要实现的委托方法为:-(NSSize)win ...

  4. 《苹果开发之Cocoa编程》挑战2 创建一个数据源 练习

    <苹果开发之Cocoa编程>第4版 P87 创建一个to-do list应用程序,在文本框中输入任务.当用户单击Add按钮时,添加字符串到一个变长队列,新任务就出现在list的末尾. 关键 ...

  5. iOS多线程开发之GCD(中篇)

    前文回顾: 上篇博客讲到GCD的实现是由队列和任务两部分组成,其中获取队列的方式有两种,第一种是通过GCD的API的dispatch_queue_create函数生成Dispatch Queue:第二 ...

  6. iOS多线程开发之NSOperation - 快上车,没时间解释了!

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

  7. iOS游戏开发之UIDynamic

    iOS游戏开发之UIDynamic 简介 什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象 ...

  8. iOS多线程开发之NSOperation

    一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强 ...

  9. iOS多线程开发之GCD(死锁篇)

    上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇) ...

随机推荐

  1. python--websocket数据解析

    # websocket实现原理 ''' 1.服务端开启socket,监听ip和端口 2.客户端发送连接请求(带上ip和端口) 3.服务端允许连接 4.客户端生成一个随机字符串,和magic strin ...

  2. This template requires a more recent version of the Android Eclipse plugin. Please update from versi

    新建android project的时候遇到这个错误: 解决方法:①直接修改F:\JAVA\SDK\android-sdk\tools\templates\activities (对应你的JAVA S ...

  3. 解析 Lambda 表达式

    我们先创建一个表达式树: Expression<Func<int, int, int>> expression = (a,b) => a + b; 我们的例子是一个Exp ...

  4. (23)C#XML操作

    APP.config是一个典型的XML文件 打开vs2008在项目上右键-添加-新建项 选择应用程序配置文件,默认名称为APP.config,新建打开后默认代码如下 <?xml version= ...

  5. MySql笔记之修改MySQL提示符

    首先,了解下MYSQL提示符是神马东东 就是每次登陆mysql后出现的提示符 如果我们不喜欢这个提示符呢,那我们就改成我们喜欢的样子. 系统参数提示符 举个栗子 就改成相应的提示符了,那么可否随意改名 ...

  6. JSK 11: 移除数组中的重复元素

    题目描述 给定一个升序排列的数组,去掉重复的数,并输出新的数组的长度. 例如:数组 $A = \{1, 1, 2\}$,你的程序应该输出 $2$ 即新数组的长度,新数组为 $\{1, 2\}$. 要求 ...

  7. POJ 2836 Rectangular Covering(状压DP)

    [题目链接] http://poj.org/problem?id=2836 [题目大意] 给出二维平面的一些点,现在用一些非零矩阵把它们都包起来, 要求这些矩阵的面积和最小,求这个面积和 [题解] 我 ...

  8. java.util.Arrays导入报错问题

    我的原因:项目jdk的路径没有找到引起的 解决办法:右击项目->Properties->Java build path->Libraries 下错误的jdk,remove,addLi ...

  9. NOIP 2017 赛后反思 [补档]

    首先写一下比赛的情况: D1: T1: 之前做过类似的题目, 因而知道大致的结论, 迅速完成. T2: 貌似直接模拟就可以了, 涉及到字符串信息提取, 比较麻烦, 因而想放到最后做. T3: 非常简洁 ...

  10. 程设刷题 | 程序设计实践II-2017(部分)

    目录 1165-算术题 题目描述 代码实现 1184-Tourist 1 题目描述 代码实现 1186-Tourist 2 题目描述 代码实现 1224-LOVE 题目描述 代码实现 1256-湘潭大 ...