iOS项目开发之Socket编程

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

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

  • 前言
  • 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:-1 tag:0];
}
// 连接/重新连接
- (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:-1 tag:0];
}

- (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:-1 tag:0];
}

@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:1];
    isRunning = NO;

    [logView setString:@""];
    // [portField setString:@"8080"];
}

- (IBAction)startStop:(id)sender
{
    if(!isRunning)
    {
        int port = [portField intValue];

        if(port < 0 || port > 65535)
        {
            port = 0; // 会随即取端口
        }

        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 = 0; 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:1];
    [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:1];
    [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:1];
    [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:-1 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:-1 tag:0];
}
// 接收到数据
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSData *strData = [data subdataWithRange:NSMakeRange(0, [data length] - 2)];
    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:-1 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编程,布布扣,bubuko.com

时间: 2024-08-03 19:24:12

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

Java开发之Socket编程详解

本文从3个方面对Socket编程进行详解: 一,网络编程中两个主要的问题 二,两类传输协议:TCP:UDP 三,基于Socket的java网络编程 一,网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机.而TCP层则提供面向应用的可靠(tcp)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要

小程序开发之socket编程 小程序直播答题开发的直播弹幕使用web socket编程

最近有一个项目很火,那就是直播答题的,接到公司的这个任务,开发直播答题的聊天室功能.在线的人相互聊天.之前做过类似的,当时都是使用的ajax轮询的,这种非常的耗费服务器.所以这次就开始使用socket来做,我主要负责后端开发,主要就是配合前端做一些接口. 小程序前端使用微信的空间 wx.connectSocket  做好相关的wss配置,然后我这边使用PHP来做socket 服务读写. 公司采用的是gateway worker 具体不多说,大家可以看他的文档哈,我就直接上代码了 GatewayW

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

实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数 据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地 址:https://github.com/robbiehanson/CocoaAsyncSocket,读者可以自行google或者baidu搜索 这个库的用法,网上有很多资料,而且用法不难. 在一些对Socket通信使用需求不是很高的应用中,比如需要

iOS定位开发之CoreLocation

有时候我们并不需要查看地图,而仅仅需要自己的当前位置就可以,这时候就没有必要去使用MapKit可,直接使用定位服务就好了 苹果提供了CoreLocation框架来做定位功能 首先要想项目中导入CoreLocation框架 定位服务开启之前最好查看一下定位服务是否可用,有可能用户已经拒绝或者用户手机定位模块有问题,无法定位 定位服务是由CLLocationManager来管理的 定位成功等信息也是通过代理发送给对象的,另外定位中经常需要用到地理位置的解码和反解码(其实就是经纬度和地名之间的转换)

iOS游戏开发之UIDynamic

iOS游戏开发之UIDynamic 简介 什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象重力.弹性碰撞等现象 物理引擎的价值 广泛用于游戏开发,经典成功案例是“愤怒的小鸟” 让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果 提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏 知名的2D物理引擎 Box2d Chipmunk 使用步骤 要想使用UIDynamic来实现物理仿

iOS多线程开发之GCD(下篇)

上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇)>. 言归正传,我们首先来回顾下死锁,所谓死锁: 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源(如数据源,内存等,变量不是资源)而造成的一种互相等待的现象,若无外部处理作用,它们都将无限等待下去. 死锁形成的原因: 系统资源不足 进程(线程)推进的顺序不恰当: 资源分配不当 死锁形成的条件:

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

一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强,很屌的是加入了操作依赖. 默认情况下,NSOperation单独使用时只能同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行.讲到这里,我们不难发现GCD和NSOperation实现的方式很像,其实这更像是废话,NSOperation本身就是基于GCD的

iOS开发之Socket通信实战--Request请求模块

实际上在iOS很多应用开发中,大部分用的网络通信都是http/https协议,除非有特殊的需求会用到Socket网络协议进行网络数据传输,这时候在iOS客户端就需要很好的第三方CocoaAsyncSocket来进行长连接连接和传输数据,该第三方地址:https://github.com/robbiehanson/CocoaAsyncSocket,读者可以自行google或者baidu搜索这个库的用法,网上有很多资料,而且用法不难. 在一些对Socket通信使用需求不是很高的应用中,比如需要多个i

iOS开发之socket简介

1,socket是什么? 2,socket的作用 3,socket怎么用 4,socket的扩展 —————————————————————— socket是什么? Socket这个名词现在有很多不同的意思,这里主要讲到其中2个含义.一是,套接字socket=(IP地址:端口号),是端的概念.二是,socket在应用层和传输层之间,是层的概念. 要理解socket是什么,首先要理解网络的分层结构,目前internet广泛使用的TCP/IP模型,总结起来网络模型分为四层,从上层往下依次为:应用层.