socket

2016-10-31  本文已影响54人  nothing_c

7层: 应用层, 表示层,会话层, 传输层, 网络层(协议), 数据链路层(联网),物理层(硬件)

socket是传输层 : http是处于应用层

用OC里的C写的 双向 即时

//网络字节序指的是大端模式

//大端高位在低地址,低位在高地址

//小端高位在高地址,低位在低地址

//字节序

//导入需要的系统头文件

#import

#import

#import

#import

#import

//配置文件

#import"LPSocket.h"

//*

1.创建socket对象

1.1创建CFSocketContext对象

1.2 实现socket对象初始化中的TCPServerConnectCallBack的回调方法

1.2.1判断回调是否成功

1.2.2创建self对象 开辟分线程,实现线程方法

1.2.3self对象调用登录方法

1.3判断socket对象是否创建成功

1.4配置socket的地址信息

1.4.1设置地址

1.4.2设置消息循环

1.4.3将消息源添加到消息循环中

1.4.3对于创建的对象进行释放

2.解析终端登录包

2.1计算字节长度

2.2除去长度的字节长度

2.3设置标志,命令号

2.4发送请求

3.解析心跳包

3.1解析读取心跳包的数据

3.2再解析 发送请求

- (void)viewDidLoad {

[super viewDidLoad];

[self connectToServer];

}

//打开socket连接,长连接双向的

- (void) connectToServer {

//self一个任意指针的数据,可以用在创建CFSocket对象时相关联。这个指针被传递给所有的上下文中定义的回调。

//创建一个结构体

//在MRC环境下的开发

CFSocketContext CTX = {0,self,NULL,NULL,NULL};

//kCFAllocatorDefault一都使用默认的

//PF_INET使用ipv4 :  PF_INET6使用ipv6

//TCPServerConnectCallBack回调的方法名需要实现回调方法点两下

//kCFSocketConnectCallBack指定当连接到服务器的时候回调

// &CTX提前设置的结构体

_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketConnectCallBack, TCPServerConnectCallBack, &CTX);

//判断对象是否创建成功

if(NULL==_socket){

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"创建套接字失败" delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];

[alert show];

[alert release];

return;

}

//sockaddr_in配置socket的地址信息

structsockaddr_inaddr4;

//初始化addr4的内存

//mem == memory内存

//sizeof(addr4)计算addr4在内存中占用空间(几个字节)

memset(&addr4, 0,sizeof(addr4));

//让内存对齐按一个字节对齐防止出现片段编码

bzero(&addr4,sizeof(addr4));

//赋值一下结构体本身的长度

addr4.sin_len = sizeof(addr4);

//AF_INET  ipv4

addr4.sin_family = AF_INET;

//将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)

addr4.sin_port = htons(SOCKET_PORT);

//将一个点分十进制的IP转换成一个长整数型数INET_ADDR()。

//[SOCKET_IP UTF8String] OC字符串转C字符串

addr4.sin_addr.s_addr = inet_addr([SOCKET_IPUTF8String]);

//把sockaddr_in结构体中的地址转换为Data,CFDataRef == NSData

CFDataRefaddress = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr4,sizeof(addr4));

// address CFDataRef类型的包含上面socket的远程地址的对象

CFSocketConnectToAddress(_socket, address, 30);

//获取当前线程的消息循环

CFRunLoopRefcfrl = CFRunLoopGetCurrent();

//创建一个消息源

CFRunLoopSourceRefsource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);

//把消息源添加到消息循环中

CFRunLoopAddSource(cfrl, source,kCFRunLoopCommonModes);

//CFXXXXXRef创建的对象需要释放

CFRelease(source);

CFRelease(address);

}

//static类似于OC的加号方法copy过来进行修改

staticvoidTCPServerConnectCallBack(CFSocketRefsocket,CFSocketCallBackTypetype,CFDataRefaddress,constvoid*error,void*info){

NSLog(@"连接到socket服务器");

//当socket为kCFSocketConnectCallBack时,失败时回调会返回一个错误代码指针(data),其他情况返回NULL

if(error != NULL){

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:@"连接失败" delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];

[alert show];

[alert release];

return;

}

//因为self调用不了-号方法所以就创建一个self的对象

//info和CFSocketContext CTX = {0,self,NULL,NULL,NULL};中的self对应

ZYViewController *infoObject = (ZYViewController *)info;

//其效果与NSThread的detachNewThreadSelector:toTarget:withObject:是一样的。

//开启一个分线程,调用readStream这个方法,这个方法是在分线程中进行的。

[info ObjectperformSelectorInBackground:@selector(readStream) withObject:nil];

[info ObjectuserLogin];

//测试用的

//[info Object httpTest];

}

- (void)httpTest {

NSString *str =@"GET /SocketHttpTest/TestServlet?name=wwwwww&password=12dddssd HTTP/1.1\r\n"

@"Host:localhost\r\n"

@"connection:close\r\n\r\n"

@"\r\n";

constchar *s2 = [strUTF8String];

send(CFSocketGetNative(_socket),s2,[strlength], 0);

}

//解析终端登录包

- (void)userLogin {

//网络字节序,大端模式

//一共需要23个字节(包含长度本身2个)

chardata[23];

data[0] = (21 >> 8) & 0x000000ff;//0

data[1] = 21 & 0x000000ff;//21

//使用配置文件宏定义好的内容

data[2] =CLIENT_SIDE;

data[3] =LOGIN_COMMAND;

//软件版本全为0测试

for(inti = 4; i < 12; i++) {

data[i] = 0;

}

//设备序列号测试

for(inti = 12; i < 23; i++) {

data[i] = 't';

}

//发送请求

send(CFSocketGetNative(_socket),&data,sizeof(data), 0);

}

- (void)readStream {

//将来里面放的是‘包长度(2)、哪个端(1)、命令号(1)’4个

charheader[4];

//一直读取服务器返回的包

//recv方法参数1.从哪个连接中读,2.读到的数据存到内存中的地址,3.读多长的数据

while(recv(CFSocketGetNative(_socket),&header,sizeof(header), 0)) {

//哪个端第三个

Byteside = header[2];

//命令号第四个

Bytecommand = header[3];

if(command == LOGIN_RESPONSE_COMMAND&& side == 'S') {

NSLog(@"登陆返回了");

//得到包的长度(除去长度字段本身)或运算(转换为十进制结果)

intlength = ( header[0] << 8 ) | header[1];

//这个字节数组放的是‘当前时间、结果代码、原因’

charloginResponse[length - 2];

//除去前四个字节后继续解析读取

if(recv(CFSocketGetNative(_socket),&loginResponse,sizeof(loginResponse), 0)) {

NSLog(@"年____  %d",loginResponse[0]);

NSLog(@"月____  %d",loginResponse[1]);

NSLog(@"日____  %d",loginResponse[2]);

NSLog(@"时____  %d",loginResponse[3]);

NSLog(@"分____  %d",loginResponse[4]);

NSLog(@"秒____  %d",loginResponse[5]);

BOOLfailed = loginResponse[6];

NSLog(@"登陆是否失败%d",failed);

}

} else if(command == 0x80 && side == 'S') {

NSLog(@"收到心跳包");

//if外边已经读取过4个了,只用读取剩下的两个

charinterval[2];

if(recv(CFSocketGetNative(_socket),&interval,sizeof(interval), 0)) {

//心跳时间是多字节往左移8位

int nextInterVal = (interval[0] << 8) | interval[1];

NSLog(@"下次心跳时间%d",nextInterVal);

chardata[4];

//2-->0000 0010考虑大端模式高低位互换0--0 1--2

data[0] = 0;

data[1] = 2;

data[2] = 'E';

data[3] = 0x00;

//发送数据

send(CFSocketGetNative(_socket),&data,4, 0);

}

}

}

}

@end

上一篇下一篇

猜你喜欢

热点阅读