Socket编程

一、网络各个协议:TCP/IP、SOCKET、HTTP等

网络七层由下往上分别为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

其中物理层、数据链路层和网络层通常被称作媒体层,是网络工程师所研究的对象;

传输层、会话层、表示层和应用层则被称作主机层,是用户所面向和关心的内容。

 http协议   对应于应用层 

 tcp协议    对应于传输层  

 ip协议     对应于网络层 

 三者本质上没有可比性。  何况HTTP协议是基于TCP连接的。 

 TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。

 我 们在传输数据时,可以只使用传输层(TCP/IP),但是那样的话,由于没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用应用层 协议,应用层协议很多,有HTTP、FTP、TELNET等等,也可以自己定义应用层协议。WEB使用HTTP作传输层协议,以封装HTTP文本信息,然 后使用TCP/IP做传输层协议将它发送到网络上。Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

二、Http和Socket连接区别

 相信不少初学手机联网开发的朋友都想知道Http与Socket连接究竟有什么区别,希望通过自己的浅显理解能对初学者有所帮助。

2.1、TCP连接

要想明白Socket连接,先要明白TCP连接。手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。

建立起一个TCP连接需要经过“三次握手”:

第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握 手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连 接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客 户端交互,最终确定断开)

2.2、HTTP连接

HTTP协议即超文本传送协议(HypertextTransfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。

2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由 于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的 做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客 户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

三、SOCKET原理

3.1、套接字(socket)概念

套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

应 用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应 用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。

3.2 、建立socket连接

建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。

套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

连 接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户 端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

 3.3、SOCKET连接与TCP连接

创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接。

 3.4、Socket连接与HTTP连接

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

而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。

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

这里我们使用Socket实现一个聊天室的功能,关于服务器这里的就不介绍了

一:再头文件中第一输入流和输出流和一个消息数组

 

 1 @interface ViewController ()<NSStreamDelegate,UITextFieldDelegate,UITableViewDataSource,UITableViewDelegate>{
 2     NSInputStream *_inputStream;//对应输入流
 3     NSOutputStream *_outputStream;//对应输出流
 4 }
 5 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputViewConstraint;
 6 @property (weak, nonatomic) IBOutlet UITableView *tableView;
 7 
 8 @property (nonatomic, strong) NSMutableArray *chatMsgs;//聊天消息数组
 9 
10 @end

 

懒加载这个消息数组

1 -(NSMutableArray *)chatMsgs{
2     if (!_chatMsgs) {
3         _chatMsgs = [NSMutableArray array];
4     }
5     
6     return _chatMsgs;
7 }

二:实现输入输出流的监听

 1 -(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
 2     NSLog(@"%@",[NSThread currentThread]);
 3 
 4 //    NSStreamEventOpenCompleted = 1UL << 0,//输入输出流打开完成
 5 //    NSStreamEventHasBytesAvailable = 1UL << 1,//有字节可读
 6 //    NSStreamEventHasSpaceAvailable = 1UL << 2,//可以发放字节
 7 //    NSStreamEventErrorOccurred = 1UL << 3,// 连接出现错误
 8 //    NSStreamEventEndEncountered = 1UL << 4// 连接结束
 9     switch (eventCode) {
10         case NSStreamEventOpenCompleted:
11             NSLog(@"输入输出流打开完成");
12             break;
13         case NSStreamEventHasBytesAvailable:
14             NSLog(@"有字节可读");
15             [self readData];
16             break;
17         case NSStreamEventHasSpaceAvailable:
18             NSLog(@"可以发送字节");
19             break;
20         case NSStreamEventErrorOccurred:
21                         NSLog(@" 连接出现错误");
22             break;
23         case NSStreamEventEndEncountered:
24              NSLog(@"连接结束");
25             
26             // 关闭输入输出流
27             [_inputStream close];
28             [_outputStream close];
29             
30             // 从主运行循环移除
31             [_inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
32             [_outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
33             break;
34         default:
35             break;
36     }
37     
38 }

三:链接服务器

 1 - (IBAction)connectToHost:(id)sender {
 2     // 1.建立连接
 3     NSString *host = @"127.0.0.1";
 4     int port = 12345;
 5     
 6     // 定义C语言输入输出流
 7     CFReadStreamRef readStream;
 8     CFWriteStreamRef writeStream;
 9     CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
10     
11     // 把C语言的输入输出流转化成OC对象
12     _inputStream = (__bridge NSInputStream *)(readStream);
13     _outputStream = (__bridge NSOutputStream *)(writeStream);
14     
15     
16     // 设置代理
17     _inputStream.delegate = self;
18     _outputStream.delegate = self;
19     
20     
21     // 把输入输入流添加到主运行循环
22     // 不添加主运行循环 代理有可能不工作
23     [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
24     [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
25     
26     // 打开输入输出流
27     [_inputStream open];
28     [_outputStream open];
29 }

四:登陆

 1 - (IBAction)loginBtnClick:(id)sender {
 2     
 3     // 登录
 4     // 发送用户名和密码
 5     // 在这里做的时候,只发用户名,密码就不用发送
 6     
 7     // 如果要登录,发送的数据格式为 "iam:zhangsan";
 8     // 如果要发送聊天消息,数据格式为 "msg:did you have dinner";
 9     
10     //登录的指令
11     NSString *loginStr = @"iam:zhangsan";
12     
13     //把Str转成NSData
14     NSData *data = [loginStr dataUsingEncoding:NSUTF8StringEncoding];
15     
16     
17     [_outputStream write:data.bytes maxLength:data.length];
18 }

五:读取服务器数据

 1 #pragma mark 读了服务器返回的数据
 2 -(void)readData{
 3     
 4     //建立一个缓冲区 可以放1024个字节
 5     uint8_t buf[1024];
 6     
 7     // 返回实际装的字节数
 8     NSInteger len = [_inputStream read:buf maxLength:sizeof(buf)];
 9 
10     // 把字节数组转化成字符串
11     NSData *data = [NSData dataWithBytes:buf length:len];
12     
13     // 从服务器接收到的数据
14     NSString *recStr =  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
15     
16     NSLog(@"%@",recStr);
17     
18     [self reloadDataWithText:recStr];
19     
20 }

六:发送数据

 1 -(BOOL)textFieldShouldReturn:(UITextField *)textField{
 2     
 3     NSString *text = textField.text;
 4     
 5     NSLog(@"%@",text);
 6     // 聊天信息
 7     NSString *msgStr = [NSString stringWithFormat:@"msg:%@",text];
 8     
 9     //把Str转成NSData
10     NSData *data = [msgStr dataUsingEncoding:NSUTF8StringEncoding];
11     
12     // 刷新表格
13     [self reloadDataWithText:msgStr];
14     
15     // 发送数据
16     [_outputStream write:data.bytes maxLength:data.length];
17     
18     // 发送完数据,清空textField
19     textField.text = nil;
20     
21     return YES;
22 }

七:实现数据的显示,并且每发送一次消息都会滚动到对应的位置

 1 -(void)reloadDataWithText:(NSString *)text{
 2     [self.chatMsgs addObject:text];
 3     
 4     [self.tableView reloadData];
 5     
 6     // 数据多,应该往上滚动
 7     NSIndexPath *lastPath = [NSIndexPath indexPathForRow:self.chatMsgs.count - 1 inSection:0];
 8     [self.tableView scrollToRowAtIndexPath:lastPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
 9 }
10 
11 #pragma mark 表格的数据源
12 
13 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
14     return self.chatMsgs.count;
15 }
16 
17 
18 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
19 {
20     static NSString *ID = @"Cell";
21     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
22    
23     cell.textLabel.text = self.chatMsgs[indexPath.row];
24     
25     return cell;
26 }
27 
28 -(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
29     [self.view endEditing:YES];
30 }

八:监听键盘的改变

 1     // 监听键盘
 2     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(kbFrmWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
 3 }
 4 
 5 
 6 -(void)kbFrmWillChange:(NSNotification *)noti{
 7     NSLog(@"%@",noti.userInfo);
 8     
 9     // 获取窗口的高度
10     
11     CGFloat windowH = [UIScreen mainScreen].bounds.size.height;
12     
13    
14     
15     // 键盘结束的Frm
16     CGRect kbEndFrm = [noti.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
17      // 获取键盘结束的y值
18     CGFloat kbEndY = kbEndFrm.origin.y;
19     
20     
21     self.inputViewConstraint.constant = windowH - kbEndY;
22 }
你不知道的SAM V系列MCU:SAM V MCU基于 ARM Cortex:trade_mark:-M7 的微控制器系列可提供最佳的连接接口组合,包括以太网 AVB、MediaLB、USB 和 CAN-FD,以及可提供高达 1500 CoreMark 的高性能 ARM Cortex-M 内核。SAM V MCU专注于音频放大器、汽车通信控制单元或车头单元的车载信息娱乐链接。 关键特性: 高性能—由于 Cortex-M7 能够以 300 MHz 的频率运行,外加前所未有的 DSP 性能,可实现高达 1500 CoreMark 先进的存储器架构— 高达 384 KB 的多端口存储器,其中高达 256 KB 可分配作为紧密耦合存储器(数据和指令),实现以 300 MHz 频率运行时零等待 以太网 AVB— SAM V71 在以太网 MAC 中嵌入了对音频视频桥接 (AVB) 的特定硬件支持。在 MAC 的硬件中实现了基于信用的流量整形,从而无需 CPU 干预 高速 USB— 主机和器件模式高速 USB 控制器集成了 PHY 以降低 BOM 成本 MOST 连接— MediaLB 3 线接口可无缝集成到 MOST 总线 音频接口— 灵活的 TDM/I2S 接口可以连接音频源、编解码器或 DSP CAN-FD— 最新的 CAN 2.0 和 CAN 灵活数据速率 (FD) 控制器可实现更高带宽 汽车等级— 通过 AEC-Q100 等级 2 (-40C/105C) 认证 2015年最新评估开发板——SAM V71 Xplained Atmel年初刚刚推出了基于SAM V71Xplained评估套件,目前已在官网发布出售。 开发板资源: 处理器:ATSAMV71Q21(ATSAMV71Q21数据手册) 用户:一个电源开关按钮、一个机械复位按钮、两个用户按钮、两个黄色LED指示灯 存储:2MB SDRAM、2MB QSPI Flash、256KByte EEPROM 网口:IEEE 802.3az 10Base-T/100Base-TX Ethernet RMII PHY 媒体接口:立体音频编解码器、相机接口、耳机和麦克风接口 SDIO接口SD连接器、CAN收发器 MediaLB接口 接口:外部调试连接器接口、一个扩展LCD接口、两个外部扩展接口、调试接口、虚拟COM口、USB接口 电源:外部电源输入、USB供电 用到的主要芯片: 基于 ARM Cortex:trade_mark:-M7 的微控制器:ATSAMV71Q21 AVR 32-bit RISC MCU:AT32UC3A4256J CAN收发器:ATA6561-GBQW 超低功耗、便携式音频编解码器:WM8904 IEEE 802.3az 10Base-T/100Base-TX Ethernet RMII PHY:KSZ8061RNBVA 2kbit I2C EEPROM:AT24MAC402-MAHM-T
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值