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 = { , 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 -; /* 设置是否重新绑定标志 */
int yes = ;
/* 设置socket属性 SOL_SOCKET是设置tcp SO_REUSEADDR是重新绑定,yes 是否重新绑定*/
setsockopt(CFSocketGetNative(sserver), SOL_SOCKET, SO_REUSEADDR,
(void *)&yes, sizeof(yes)); /* 设置端口和地址 */
struct sockaddr_in addr;
memset(&addr, , 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 -;
} /* 创建一个Run Loop Socket源 */
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sserver, );
/* 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 = {, 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[];
CFReadStreamRef inputStream = stream; if(NULL != inputStream)
{
CFReadStreamRead(stream, buff, );
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)+);
//关闭输出流
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 = ;
[self initNetworkCommunication]; } - (IBAction)receiveData:(id)sender {
flag = ;
[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 == && theStream == _inputStream) {
NSMutableData *input = [[NSMutableData alloc] init];
uint8_t buffer[];
NSInteger len;
while([_inputStream hasBytesAvailable])
{
len = [_inputStream read:buffer maxLength:sizeof(buffer)];
if (len > )
{
[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 == && theStream == _outputStream) {
//输出
UInt8 buff[] = "Hello Server!";
[_outputStream write:buff maxLength: strlen((const char*)buff)+];
//必须关闭输出流否则,服务器端一直读取不会停止,
_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网络编程与云端应用最佳实践》
iOS网络编程笔记——Socket编程的更多相关文章
- 网络协议 11 - Socket 编程(下):眼见为实耳听为虚
系列文章传送门: 网络协议 1 - 概述 网络协议 2 - IP 是怎么来,又是怎么没的? 网络协议 3 - 从物理层到 MAC 层 网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校 网 ...
- [深入浅出Cocoa]iOS网络编程之Socket
http://blog.csdn.net/kesalin/article/details/8798039 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] [深入浅出Co ...
- 第九章:Python高级编程-Python socket编程
第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...
- 多线程编程以及socket编程_Linux程序设计4chapter15
看了Linux程序设计4中文版,学习了多线程编程和socket编程.本文的程序参考自Linux程序设计4的第15章. 设计了一个客户端程序,一个服务端程序.使用TCP协议进行数据传输. 客户端进程创建 ...
- iOS项目开发之Socket编程
有一段时间没有认真总结和写博客了 前段时间找工作.进入工作阶段.比较少静下来认真总结,现在静下心来总结一下最近的一些心得 前言 AsyncSocket介绍 AsyncSocket详解 AsyncSoc ...
- 关于网络协议和socket编程基本概念
TCP协议可以说已经是IT人耳熟能详的协议,最近在学习socket网络编程时后重新温习一下这个协议,针对一些问题做了一些总结,很多理解可能还不是很准确. 1. 协议是什么?所谓的各种网络协议无非是一种 ...
- 网络协议 10 - Socket 编程(上):实践是检验真理的唯一标准
系列文章传送门: 网络协议 1 - 概述 网络协议 2 - IP 是怎么来,又是怎么没的? 网络协议 3 - 从物理层到 MAC 层 网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校 网 ...
- java网络编程之Socket编程
概念 网络编程分为BIO(传统IO).NIO.AIO.Socket编程属于BIO这种传统IO. InetAddress java.net.InetAddress是JAVA中管理IP地址的类,常用 pu ...
- C#网络编程:Socket编程
套接字简介:套接字最早是Unix的,window是借鉴过来的.TCP/IP协议族提供三种套接字:流式.数据报式.原始套接字.其中原始套接字允许对底层协议直接访问,一般用于检验新协议或者新设备问题,很少 ...
随机推荐
- CodeForces 446B
DZY Loves Modification time limit per test 2 seconds memory limit per test 256 megabytes input stand ...
- esri-leaflet入门教程(2)-地图的HelloWorld
esri-leaflet入门教程(2)-地图的HelloWorld by 李远祥 常言道"君子性非异也,善假于物也".这句话在IT界同样也适用,只不过IT界有更为时髦的说法:&qu ...
- shell变量数学运算
shell变量数学运算 #!/bin/sh # 本脚本说明shell脚本中变量运算的用法 # 错误的用法var=1var=$var+1echo $var 输出:1+1 # 第一种用法,letvar=1 ...
- java socket tcp(服务器循环检测)
刚才看了下以前写了的代码,tcp通信,发现太简单了,直接又摘抄了一个,运行 博客:http://blog.csdn.net/zhy_cheng/article/details/7819659 优点是服 ...
- Apache常见功能实战详解
Apache 是一款使用量排名第一的 web 服务器,LAMP 中的 A 指的就是它.由于其开源.稳定.安全等特性而被广泛使用.前边的一篇文章中已经记录过如何搭建 LAMP 架构,搭建仅是第一步,其中 ...
- Android系统之灯光系统--通知灯深入分析
Android通知灯的深入分析 通知的类别 声音 振动 闪灯 APP如何发出通知灯请求 getSystemService(通知服务) 构造notification 类别 其他参数(颜色,onMS,of ...
- 树莓派上搭建arduino开发环境
-------------还是博客园上面的格式看这舒服,不去新浪了------------- 为什么要在树莓派上开发arduino呢?总要把树莓派用起来嘛,不然老吃灰. 树莓派使用SSH时没有图形界面 ...
- Codeforce 水题报告
最近做了好多CF的题的说,很多cf的题都很有启发性觉得很有必要总结一下,再加上上次写题解因为太简单被老师骂了,所以这次决定总结一下,也发表一下停课一星期的感想= = Codeforces 261E M ...
- ionic接入广告
一.获取Admob phonegap 广告插件(cordova 广告平台插件) 在cordova 和phonegap等html5手机应用里面展示Admob,百度移动联盟,广点通广告需要Cordova ...
- 谈 jquery中.band() .live() .delegate() .on()的区别
bind(type,[data],fn) 为每个匹配元素的特定事件绑定事件处理函数 $("a").bind("click",function(){alert(& ...