iOS开发教程Socket的原理和使用.docx
《iOS开发教程Socket的原理和使用.docx》由会员分享,可在线阅读,更多相关《iOS开发教程Socket的原理和使用.docx(15页珍藏版)》请在冰豆网上搜索。
iOS开发教程Socket的原理和使用
iOS开发教程Socket的原理和使用
1.iOS网络编程层次结构
iOS网络编程层次结构分为三层,从上往下依次为:
-Cocoa层:
NSURL,Bonjour,GameKit,WebKit
-CoreFoundation层:
基于C的CFNetwork和CFNetServices
-OS层:
基于C的BSDSocket
Cocoa层:
是最上层的基于Objective-C的API,比如URL访问,NSStream,Bonjour,GameKit等,这是大多数情况下我们常用的API。
Cocoa层是基于CoreFoundation实现的。
CoreFoundation层:
因为直接使用socket需要更多的编程工作,所以苹果对OS层的socket进行简单的封装以简化编程任务。
该层提供了CFNetwork和CFNetServices,其中CFNetwork又是基于CFStream和CFSocket。
OS层:
最底层的BSDSocket提供了对网络编程最大程度的控制,但是编程工作也是最多的。
因此,苹果建议我们使用CoreFoundation及以上层的API进行编程。
本文将介绍如何在iOS系统下使用最底层的Socket进行编程。
2.什么是Socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
2.1TCP和UDP的区别
TCP:
面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。
UDP:
面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快。
关于TCP是一种流模式的协议,UDP是一种数据包模式的协议,这里要说明一下,TCP是面向连接的,也就是说,在连接持续的过程中,Socket中收到的数据都是由同一台主机发出的(劫持什么的不考虑),因此,知道保证数据是有序的到达就行了,至于每次读取多少数据自己看着办。
而UDP是无连接的协议,也就是说,只要知道接收端的IP和端口,且网络是可达的,任何主机都可以向接收端发送数据。
这时候,如果一次能读取超过一个报文的数据,则会乱套。
比如,主机A向发送了报文P1,主机B发送了报文P2,如果能够读取超过一个报文的数据,那么就会将P1和P2的数据合并在了一起,这样的数据是没有意义的。
2.2常用的Socket类型
有两种:
流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。
流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
2.2.1TCPC/S架构程序设计基本框架
2.2.2TCP三次握手
最形象理解:
>「你瞅啥?
」
>
>「瞅你咋地?
」
>
>「来咱俩唠唠。
」
>
>然后就唠上了。
2.2.3TCP四次挥手
2.2.4基于TCP的套接字代码实现
相关头文件。
#include
#include
#include
#include
#include
服务端实现代码。
-(void)socketServer
{
interr;
//1.创建socket套接字
//原型:
intsocket(intdomain,inttype,intprotocol);
//domain:
协议族type:
socket类型protocol:
协议
intfd=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
BOOLsuccess=(fd!
=-1);
if(success){
NSLog(@"Socket创建成功");
//地址结构体
structsockaddr_inaddr;
//内存清空
memset(&addr,0,sizeof(addr));
//内存大小
addr.sin_len=sizeof(addr);
//地址族,在socket编程中只能是AF_INET
addr.sin_family=AF_INET;
//端口号
addr.sin_port=htons(1024);
//按照网络字节顺序存储IP地址
addr.sin_addr.s_addr=INADDR_ANY;
//2.建立地址和套接字的联系(绑定)
//原型:
bind(sockid,localaddr,addrlen)
err=bind(fd,(conststructsockaddr*)&addr,sizeof(addr));
success=(err==0);
}
//3.服务器端侦听客户端的请求
if(success){
NSLog(@"绑定成功");
//listen(Sockid,quenlen)quenlen并发队列
err=listen(fd,5);//开始监听
success=(err==0);
}
if(success){
NSLog(@"监听成功");
//4.一直阻塞等到客户端的连接
while(true){
structsockaddr_inpeeraddr;
intpeerfd;
socklen_taddrLen;
addrLen=sizeof(peeraddr);
NSLog(@"等待客户端的连接请求");
//5.服务器端等待从编号为Sockid的Socket上接收客户端连接请求
//原型:
newsockid=accept(Sockid,Clientaddr,paddrlen)
peerfd=accept(fd,(structsockaddr*)&peeraddr,&addrLen);
success=(peerfd!
=-1);
//接收客户端请求成功
if(success){
NSLog(@"接收客户端请求成功,客户端地址:
%s,端口号:
%d",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
send(peerfd,"欢迎进入Socket聊天室",1024,0);
//6.创建新线程接收客户端发送的消息
[NSThreaddetachNewThreadSelector:
@selector(reciveMessage:
)toTarget:
selfwithObject:
@(peerfd)];
}
}
}
}
-(void)reciveMessage:
(id)peerfd
{
intfd=[peerfdintValue];
charbuf[1024];
ssize_tbufLen;
size_tlen=sizeof(buf);
//循环阻塞接收客户端发送的消息
do{
bufLen=recv(fd,buf,len,0);
//当返回值小于等于零时,表示socket异常或者socket关闭,退出循环阻塞接收消息
if(bufLen<=0){
break;
}
//接收到的信息
NSString*msg=[NSStringstringWithCString:
bufencoding:
NSUTF8StringEncoding];
NSLog(@"来自客户端,消息内容:
%@",msg);
memset(buf,0,sizeof(buf));
}while(true);
//7.关闭
close(fd);
}
客户端代码。
-(void)createSocketClient
{
interr;
//创建socket套接字
intfd=socket(AF_INET,SOCK_STREAM,0);
BOOLsuccess=(fd!
=-1);
structsockaddr_inaddr;
if(success){
NSLog(@"Socket创建成功");
memset(&addr,0,sizeof(addr));
addr.sin_len=sizeof(addr);
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=INADDR_ANY;
//建立地址和套接字的联系
err=bind(fd,(conststructsockaddr*)&addr,sizeof(addr));
success=(err==0);
}
if(success){
structsockaddr_inserveraddr;
memset(&serveraddr,0,sizeof(serveraddr));
serveraddr.sin_len=sizeof(serveraddr);
serveraddr.sin_family=AF_INET;
//服务器端口
serveraddr.sin_port=htons(1024);
//服务器的地址
serveraddr.sin_addr.s_addr=inet_addr("192.168.2.5");
socklen_taddrLen;
addrLen=sizeof(serveraddr);
NSLog(@"连接服务器中...");
err=connect(fd,(structsockaddr*)&serveraddr,addrLen);
success=(err==0);
if(success){
//getsockname是对tcp连接而言。
套接字socket必须是已连接套接字描述符。
err=getsockname(fd,(structsockaddr*)&addr,&addrLen);
success=(err==0);
if(success){
NSLog(@"连接服务器成功,本地地址:
%s,端口:
%d",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
[NSThreaddetachNewThreadSelector:
@selector(reciveMessage:
)toTarget:
selfwithObject:
@(fd)];
}
}
else{
NSLog(@"connectfailed");
}
}
}
-(void)reciveMessage:
(id)peerfd
{
intfd=[peerfdintValue];
charbuf[1024];
ssize_tbufLen;
size_tlen=sizeof(buf);
//循环阻塞接收消息
do{
bufLen=recv(fd,buf,len,0);
//当返回值小于等于零时,表示socket异常或者socket关闭,退出循环阻塞接收消息
if(bufLen<=0){
break;
}
//接收到的信息
NSString*msg=[NSStringstringWithCString:
bufencoding:
NSUTF8StringEncoding];
NSLog(@"来自服务端,消息内容:
%@",msg);
}while(true);
//7.关闭
close(fd);
}
2.2.5UDPC/S架构程序设计基本框架
2.2.6字节顺序
计算机数据表示存在两种字节顺序:
NBO与HBO
网络字节顺序NBO(NetworkByteOrder):
按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
主机字节顺序(HBO,HostByteOrder):
不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。
不同的CPU有不同的字节顺序类型,这些字节顺序类型指的是整数在内存中保存的顺序,即主机字节顺序。
常见的有两种:
英文名
中文名
描述
big-endian
大尾数顺序
地址的低位存储值的高位
little-endian
小尾数顺序
地址的低位存储值的低位
如Intelx86结构下,short型数0x1234表示为3412,int型数0x12345678表示为78563412如IBMpowerPC结构下,short型数0x1234表示为1234,int型数0x12345678表示为12345678
网络字节顺序与本地字节顺序之间的转换函数:
htonl()--"HosttoNetworkLong"
ntohl()--"NetworktoHostLong"
htons()--"HosttoNetworkShort"
ntohs()--"NetworktoHostShort"
2.2.7地址转换方法
in_addr_tinet_addr(constchar*)
将一个点间隔地址转换成一个in_addr
char*inet_ntoa(structin_addr)
将网络地址转换成“.”点隔的字符串格式。
intinet_aton(constchar*,structin_addr*)
将一个字符串IP地址转换为一个32位的网络序列IP地址。
2.2.8获取地址
-用getsockname获得本地ip和port
-用getpeername获得对端ip和port
套接字socket必须是已连接套接字描述符。
2.2.9获取本地IP地址
参考stackoverflow链接:
<
2.2.10UPD套接字实现代码
#include
-(NSString*)getIPAddress{
NSString*address=@"error";
structifaddrs*interfaces=NULL;
structifaddrs*temp_addr=NULL;
intsuccess=0;
//retrievethecurrentinterfaces-returns0onsuccess
success=getifaddrs(&interfaces);
if(success==0){
//Loopthroughlinkedlistofinterfaces
temp_addr=interfaces;
while(temp_addr!
=NULL){
if(temp_addr->ifa_addr->sa_family==AF_INET){
//Checkifinterfaceisen0whichisthewificonnectionontheiPhone
if([[NSStringstringWithUTF8String:
temp_addr->ifa_name]isEqualToString:
@"en0"]){
//GetNSStringfromCString
address=[NSStringstringWithUTF8String:
inet_ntoa(((structsockaddr_in*)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr=temp_addr->ifa_next;
}
}
//Freememory
freeifaddrs(interfaces);
returnaddress;
}
3.第三方库
CocoaAsyncSocket:
<
CocoaAsyncSocketprovideseasy-to-useandpowerfulasynchronoussocketlibrariesforMacandiOS.Theclassesaredescribedbelow.