• iOS_SN_Socket


    转载文章,原地址:http://yimouleng.com/2015/02/04/Socket-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:@” “];
    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:@” %@“, 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:@”%@ “, 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:@”%@ “, 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:@”%@ “, 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

    界面搭建

     
     

     

     

     
  • 相关阅读:
    C/C++字符串函数之复制函数
    tesseract api C++使用例子
    error C2275: “XXX”: 将此类型用作表达式非法
    Socket通信原理探讨(C++为例)
    模拟按键,点击,滑动,在光标处输出字符
    安卓使用Root权限实现后台模拟全局按键、触屏事件方法(类似按键精灵)
    【 转】__try,__except,__finally,__leave异常模型机制
    提高VS2010运行速度的技巧
    解决VS2010子目录中的.cpp文件引用上一级目录的stdafx.h找不到定义的问题
    1009MySQL数据库InnoDB存储引擎Log漫游
  • 原文地址:https://www.cnblogs.com/zhang-kiwi/p/5219153.html
Copyright © 2020-2023  润新知