iOS网络编程笔记——Socket编程

一、什么是Socket通信:

Socket是网络上的两个程序,通过一个双向的通信连接,实现数据的交换。这个双向连路的一端称为socket。socket通常用来实现客户方和服务方的连接。socket是TCP/IP协议的一个十分流行的编程接口。一个socket由一个IP地址和一个端口号唯一确定。TCP/IP协议的传输层又有两种协议:TCP(传输控制协议)和UDP(用户数据报协议)。TCP是基于连接的,而UDP是无连接的;TCP对系统资源的要求较多,而UDP少;TCP保证数据的正确性而UDP可能丢包;TCP保证数据顺序而UDP不保证。

二、Socket编程:

2.1  服务器端监听某个端口是否有连接请求。服务器端程序处于堵塞状态,直到客户端像服务器端发出连接请求,服务器端接受请求才能向下运行。一旦连接建立起来,通过socket可以获得输入输出流对象。借助于输入输出流对象就可以实现与客户端的通信,最后不要忘记关闭socket和释放一些资源(包括关闭输入/输出流)。

2.2  客户端流程是先指定要通信的服务器IP地址、端口和采用的传输协议(TCP/UDP),向服务器发出连接请求,服务器有应答请求之后,就会建立连接。之后与服务器端一样。

三、 iOS网络编程的层次结构:

(1)Foundation:提供了 NSStream,Bonjour,GameKit,基于Core Foundation框架实现。

(2)Core Foundation:提供了CFStream,CFNetServices,面向C语言实现。

(3)BSD Socket :面向C实现,完全使用C编写。

四、实例:NSStream&CFStream实现TCP Socket服务器端

#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 8080

void AcceptCallBack(CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);

void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void *);

void ReadStreamClientCallBack (CFReadStreamRef stream, CFStreamEventType eventType,void *);

int main(int argc, const char * argv[])
{
    /* 定义一个Server Socket引用 */
    CFSocketRef sserver;

    /* 创建socket context */
    CFSocketContext CTX = { 0, NULL, NULL, NULL, NULL };

    /* 创建server socket  TCP IPv4 设置回调函数 */
    sserver = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP,
                             kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &CTX);
    if (sserver == NULL)
        return -1;

    /* 设置是否重新绑定标志 */
    int yes = 1;
    /* 设置socket属性 SOL_SOCKET是设置tcp SO_REUSEADDR是重新绑定,yes 是否重新绑定*/
    setsockopt(CFSocketGetNative(sserver), SOL_SOCKET, SO_REUSEADDR,
               (void *)&yes, sizeof(yes));

    /* 设置端口和地址 */
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));             //memset函数对指定的地址进行内存拷贝
    addr.sin_len = sizeof(addr);
    addr.sin_family = AF_INET;                  //AF_INET是设置 IPv4
    addr.sin_port = htons(PORT);                //htons函数 无符号短整型数转换成“网络字节序”
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   //INADDR_ANY有内核分配,htonl函数 无符号长整型数转换成“网络字节序”

    /* 从指定字节缓冲区复制,一个不可变的CFData对象*/
    CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr, sizeof(addr));

    /* 设置Socket*/
    if (CFSocketSetAddress(sserver, (CFDataRef)address) != kCFSocketSuccess) {
        fprintf(stderr, "Socket绑定失败\n");
        CFRelease(sserver);
        return -1;
    }

    /* 创建一个Run Loop Socket源 */
    CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sserver, 0);
    /* Socket源添加到Run Loop中 */
    CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes);
    CFRelease(sourceRef);

    printf("Socket listening on port %d\n", PORT);
    /* 运行Loop */
    CFRunLoopRun();

}

/* 接收客户端请求后,回调函数  */
void AcceptCallBack(
                    CFSocketRef socket,
                    CFSocketCallBackType type,
                    CFDataRef address,
                    const void *data,
                    void *info)
{
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;

    /* data 参数涵义是,如果是kCFSocketAcceptCallBack类型,data是CFSocketNativeHandle类型的指针 */
    CFSocketNativeHandle sock = *(CFSocketNativeHandle *) data;

    /* 创建读写Socket流 */
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, sock,
                                 &readStream, &writeStream);

    if (!readStream || !writeStream) {
        close(sock);
        fprintf(stderr, "CFStreamCreatePairWithSocket() 失败\n");
        return;
    }

    CFStreamClientContext streamCtxt = {0, NULL, NULL, NULL, NULL};
    // 注册两种回调函数
    CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, ReadStreamClientCallBack, &streamCtxt);
    CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, WriteStreamClientCallBack, &streamCtxt);

    //加入到循环当中
    CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
    CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);

    CFReadStreamOpen(readStream);
    CFWriteStreamOpen(writeStream);

}

/* 读取流操作 客户端有数据过来时候调用 */
void ReadStreamClientCallBack(CFReadStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo){

    UInt8 buff[255];
    CFReadStreamRef inputStream = stream;

    if(NULL != inputStream)
    {
        CFReadStreamRead(stream, buff, 255);
        printf("接受到数据:%s\n",buff);
        CFReadStreamClose(inputStream);
        CFReadStreamUnscheduleFromRunLoop(inputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
        inputStream = NULL;
    }
}

/* 写入流操作 客户端在读取数据时候调用 */
void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType, void* clientCallBackInfo)
{
    CFWriteStreamRef    outputStream = stream;
    //输出
    UInt8 buff[] = "Hello Client!";
    if(NULL != outputStream)
    {
        CFWriteStreamWrite(outputStream, buff, strlen((const char*)buff)+1);
        //关闭输出流
        CFWriteStreamClose(outputStream);
        CFWriteStreamUnscheduleFromRunLoop(outputStream, CFRunLoopGetCurrent(),kCFRunLoopCommonModes);
        outputStream = NULL;
    }
}

NSStream&CFSteam实现TCP Socket客户端

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

- (void)initNetworkCommunication
{

    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.1.112", PORT, &readStream, &writeStream);

    _inputStream = (__bridge_transfer NSInputStream *)readStream;
    _outputStream = (__bridge_transfer NSOutputStream
                     *)writeStream;
    [_inputStream setDelegate:self];
    [_outputStream setDelegate:self];
    [_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                            forMode:NSDefaultRunLoopMode];
    [_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                             forMode:NSDefaultRunLoopMode];
    [_inputStream open];
    [_outputStream open];

}
-(void)close
{
    [_outputStream close];
    [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [_outputStream setDelegate:nil];
    [_inputStream close];
    [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [_inputStream setDelegate:nil];
}

- (IBAction)sendData:(id)sender {
    flag = 0;
    [self initNetworkCommunication];

}

- (IBAction)receiveData:(id)sender {
    flag = 1;
    [self initNetworkCommunication];

}

-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
    NSString *event;
    switch (streamEvent) {
        case NSStreamEventNone:
            event = @"NSStreamEventNone";
            break;
        case NSStreamEventOpenCompleted:
            event = @"NSStreamEventOpenCompleted";
            break;
        case NSStreamEventHasBytesAvailable:
            event = @"NSStreamEventHasBytesAvailable";
            if (flag ==1 && theStream == _inputStream) {
                NSMutableData *input = [[NSMutableData alloc] init];
                uint8_t buffer[1024];
                NSInteger len;
                while([_inputStream hasBytesAvailable])
                {
                    len = [_inputStream read:buffer maxLength:sizeof(buffer)];
                    if (len > 0)
                    {
                        [input appendBytes:buffer length:len];
                    }
                }
                NSString *resultstring = [[NSString alloc] initWithData:input encoding:NSUTF8StringEncoding];
                NSLog(@"接收:%@",resultstring);
                _message.text = resultstring;
            }
            break;
        case NSStreamEventHasSpaceAvailable:
            event = @"NSStreamEventHasSpaceAvailable";
            if (flag ==0 && theStream == _outputStream) {
                //输出
                UInt8 buff[] = "Hello Server!";
                [_outputStream write:buff maxLength: strlen((const char*)buff)+1];
                 //必须关闭输出流否则,服务器端一直读取不会停止,
                _tipMessage.text = @"发送成功!";
                [_outputStream close];
            }
            break;
        case NSStreamEventErrorOccurred:
            event = @"NSStreamEventErrorOccurred";
            [self close];
            break;
        case NSStreamEventEndEncountered:
            event = @"NSStreamEventEndEncountered";
            NSLog(@"Error:%ld:%@",[[theStream streamError] code], [[theStream streamError] localizedDescription]);
            break;
        default:
            [self close];
            event = @"Unknown";
            break;
    }
    NSLog(@"event------%@",event);
}

实际上,socket编程是一种网络编程的标准,客户端和服务器端可以不受编程语言的限制完全自由的通信。客户端可以是oc编写的iOS程序,服务端可以是java编写的程序,通信双方定义好数据交互格式就可以了。

五、最后说一下Socket连接与http连接

由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中,客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致Socket 连接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。

而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户端。

注:源码参考自《iOS网络编程与云端应用最佳实践》

时间: 2024-10-06 00:10:56

iOS网络编程笔记——Socket编程的相关文章

iOS项目开发之Socket编程

有一段时间没有认真总结和写博客了 前段时间找工作.进入工作阶段.比较少静下来认真总结,现在静下心来总结一下最近的一些心得 前言 AsyncSocket介绍 AsyncSocket详解 AsyncSocket示例 一.前言 公司的项目用到了Socket编程,之前在学习的过程当中,用到的更多的还是http请求的方式.但是既然用到了就必须学习一下,所以就在网上找一些例子,然后想自己写一个demo.可是发现很多写iOS Socket的博客并没有很详细的说明,也可能是大神们觉得其他东西都浅显易懂. 自己专

多线程编程以及socket编程_Linux程序设计4chapter15

看了Linux程序设计4中文版,学习了多线程编程和socket编程.本文的程序参考自Linux程序设计4的第15章. 设计了一个客户端程序,一个服务端程序.使用TCP协议进行数据传输. 客户端进程创建了一个客户端使用的socket,一个socket地址结构体.设置这个socket地址结构体的端口和地址为要连接的服务端的端口和ip.然后使用客户端的socket尝试连接服务端(connect),如果连接失败直接退出.如果连接成功,则使用这个连接成功的socket进行数据传输(send和recv).首

1.socket编程:socket编程,网络字节序,函数介绍,IP地址转换函数,sockaddr数据结构,网络套接字函数,socket相关函数,TCP server和client

 1  Socket编程 socket这个词可以表示很多概念: 在TCP/IP协议中,"IP地址+TCP或UDP端口号"唯一标识网络通讯中的一个进程,"IP 地址+端口号"就称为socket. 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接.socket本身有"插座"的意思,因此用来描述网络连 接的一对一关系. TCP/IP协议最早在BSD UNIX上实现,

python网络编程之socket编程

一 客户端/服务器架构 即C/S架构,包括 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务) 美好的愿望: 最常用的软件服务器是 Web 服务器.一台机器里放一些网页或 Web 应用程序,然后启动 服务.这样的服务器的任务就是接受客户的请求,把网页发给客户(如用户计算机上的浏览器),然 后等待下一个客户请求.这些服务启动后的目标就是"永远运行下去".虽然它们不可能实现这样的 目标,但只要没有关机或硬件出错等外力干扰,它们就能运行非常长的一段时间. 生活中的C/S架构: 商

六、网络编程(socket编程)

一.客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网处处是C/S架构 比如百度网站是服务端,浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务端提供微信服务,需要下载微信安装包安装使用才可以去聊微信. C/S架构与socket的关系: 用socket就是为了完成C/S架构的开发 server端(必须遵守的): 1.位置必须固定死,绑定一个固定的地址 2.对外一直提供服务,稳定运行 3.支持并发(让多个客户端感觉是同时被服务着) 二.OSI七层 1.引子 须

网络协议 10 - Socket 编程:实践是检验真理的唯一标准

系列文章传送门: 网络协议 1 - 概述 网络协议 2 - IP 是怎么来,又是怎么没的? 网络协议 3 - 从物理层到 MAC 层 网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校 网络协议 5 - ICMP 与 ping:投石问路的侦察兵 网络协议 6 - 路由协议:敢问路在何方? 网络协议 7 - UDP 协议:性善碰到城会玩 网络协议 8 - TCP 协议(上):性恶就要套路深 网络协议 9 - TCP协议(下):聪明反被聪明误 ????前面一直在说各种协议,偏理论方面的知

Python学习笔记——基础篇【第七周】———FTP作业(面向对象编程进阶 &amp; Socket编程基础)

FTP作业 本节内容: 面向对象高级语法部分 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 参考:http://www.cnblogs.com/wupeiqi/p/4766801.html metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好 Socket 编程 参考:http://www.cnblogs.co

02-【IOS网络编程】socket编程 - Asyncsocket

socket编程Asyncsocket iPhone的标准推荐是CFNetwork 库编程,其封装好的开源库是 cocoa AsyncSocket库,用它来简化CFNetwork的调用,它提供了异步操作 主要特性有: 队列的非阻塞的读和写,而且可选超时.你可以调用它读取和写入,它会当完成后告知你 自动的socket接收.如果你调用它接收连接,它将为每个连接启动新的实例,当然,也可以立即关闭这些连接 委托(delegate)支持.错误.连接.接收.完整的读取.完整的写入.进度以及断开连接,都可以通

[深入浅出Cocoa]iOS网络编程之Socket

本文转载至 http://blog.csdn.net/kesalin/article/details/8798039 分类: iOS 开发2013-04-13 20:51 9361人阅读 评论(13) 收藏 举报 iosnetwork 目录(?)[+] 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 更多 Cocoa 开发文章,敬请访问<深入浅出Cocoa> CSDN专栏:http://blog.csdn.net/column/details/