04-WIFI通讯服务端搭建
来源:互联网 发布:在哪购买淘宝小号靠谱 编辑:程序博客网 时间:2024/06/05 16:20
WIFI通讯服务端搭建
转载请注明出处:坤小的博客点击打开链接
服务端的职责主要做两件事,一:监听socket网络中的端口 二:将某一个客户端的发送过来的消息转发给其他客户端
- 客户端是无法直接和客户端通讯的,需要经过服务端的转发
- 其他一些开发中细节笔者将会在代码注释中写明
[1.0-准备工作]
[1.1-服务端监听网络端口]
- [1.1.1-关于端口号的说明]
[1.2-服务端接收客户端消息并转发]
[1.3-完整代码及示例演示]
1.0-准备工作
- 笔者首先将上一小节的代码封装成一个wifi工具类,方便服务端和客户端快速获取自己的wifi名称和ip地址
#import "WIFITools.h"//系统配置框架(网络配置也在该框架中)#import <SystemConfiguration/CaptiveNetwork.h>#include <arpa/inet.h>#include <netdb.h>#include <net/if.h>#include <ifaddrs.h>#import <dlfcn.h>@implementation WIFITools//只对真机wifi连接有效,如果是模拟器和真机4G网络则都为nil+(NSString *)currentWifiSSID{ //1.获取网络底层监视的所有接口列表,返回的是一个BSD接口名称 NSArray *ifs = (__bridge id)CNCopySupportedInterfaces(); id info = nil; for (NSString *ifnam in ifs) { //2.通过BSD接口名称获取网络信息,返回的是一个字典。其中包含 1.wifi名称字符串 2.mac地址 3.wifi名称二进制数据 info = (__bridge id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam); if (info && [info count]) { break; } } //3.获取字典 NSDictionary *dctySSID = (NSDictionary *)info; //4.字典的SSID键对应的值就是wifi的名称 //注意: 1. 模拟器获取不到wifi名称 返回为nil 2.真机的话如果没有连接wifi而是使用4G,返回的也是nil NSString *ssid = [dctySSID objectForKey:@"SSID"];//wifi名称 // NSString *bssid = [dctySSID objectForKey:@"BSSID"];//mac地址,苹果返回的是一个无效的mac地址,与手机实际mac地址不一致,主要用于保护用户隐私 return ssid; }//获取本机在当前wifi中的ip地址,模拟器真机均可获取+(NSString *)localWiFiIPAddress{ BOOL success; struct ifaddrs * addrs; const struct ifaddrs * cursor; success = getifaddrs(&addrs) == 0; if (success) { cursor = addrs; while (cursor != NULL) { // the second test keeps from picking up the loopback address if (cursor->ifa_addr->sa_family == AF_INET && (cursor->ifa_flags & IFF_LOOPBACK) == 0) { NSString *name = [NSString stringWithUTF8String:cursor->ifa_name]; if ([name isEqualToString:@"en0"]) // Wi-Fi adapter return [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)cursor->ifa_addr)->sin_addr)]; } cursor = cursor->ifa_next; } freeifaddrs(addrs); } return nil;}@end
- 笔者搭建了一个简单的界面,主要功能就是服务端开始监听网络端口,并且将接收到的服务端的消息转发之后顺便显示在界面tableView中
1.1-服务端监听网络端口
- 1.创建服务端socket
- 2.开始监听
//服务端监听按钮点击事件- (IBAction)wifiButtonClick:(UIButton*)sender { //1.创建服务端socket if (!self.serviceSocket) { self.serviceSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; } NSError *error = nil; //2.开始监听端口 //注意:笔者这里随便写的一个端口。实际开发中,端口号不能随便乱写,硬件工程师会给我们一份wifi通讯协议文档,文档中会写明服务端的ip地址及端口号。 我这里默认情况下,ip地址用的就是mac电脑当前连接wifi的ip地址,已经在界面初始化时获取,端口号我写的是1234. [self.serviceSocket acceptOnPort:1234 error:&error]; if (error==nil) { NSLog(@"监听成功"); } else { NSLog(@"%@",error.description); } }
1.1.1-关于端口号的说明
- 端口号
- 总范围:0~65535
- 周知端口:0~1023 通常由Unix系统占用
- 注册端口:1025~49151 一般由系统自动分配
- 1024端口一般不固定分配给某个服务,在英文中的解释是“Reserved”(保留)。之前,我们曾经提到过动态端口的范围是从1024~65535,而1024正是动态端口的开始。该端口一般分配给第一个向系统发出申请的服务,在关闭服务的时候,就会释放1024端口,等待其他服务的调用
1.2-服务端接收客户端消息并转发
均在Socket代理中完成
1.服务端将监听到的客户端添加到数组,否则会因为局部变量而导致被释放
2.服务端接听到客户端加入之后,开始读取客户端的数据(该方法调用一次只能读取一次)
3.服务端接收到客户端发送过来的数据之后,转发给数组中其他的客户端
4.服务端接收到客户端数据之后,继续读取客户端数据,这样可以保证一直都能读取到客户端数据
#pragma mark - GCDAsyncSocketDelegate//服务端监听到客户端的连接- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{ //1.将连接的客户端添加到数组,防止局部变量被释放 if (!self.clientArr) { self.clientArr = [NSMutableArray array]; } [self.clientArr addObject:newSocket]; //2给客户端发送一个消息,可以让客户端在连接服务端时快速知道自己是否连接服务端成功 //注意:有的wifi通讯在这里会有一个握手的过程,也就是服务端会发给客户端一个用于验证的密文,然后客户端验证通过之后才能继续通讯,否则服务端就会踢掉客户端。主要是保证只能让自己的APP连接自己的产品,防止被其他程序连接 NSString *str = @"服务器:欢迎来到黑马程序员"; //线程延迟发送消息,防止线程阻塞 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [newSocket writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0]; }); //2.开始读取客户端的数据(类似于服务器实时监听客户端发送的数据) //注意:该行代码调用一次只能接收一次数据 [newSocket readDataWithTimeout:-1 tag:0];}//服务端接收数据成功-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"服务端接发送数据:%@",str); //添加到数组用于界面显示 [self.tableArr addObject:data]; //1.服务器的作用主要是数据的转发,所以这里我们将数据转发给其他客户端 for (GCDAsyncSocket *socket in self.clientArr) { //1.1过滤掉发送消息者本身 if (socket != sock) { //1.2给客户端发送数据 [socket writeData:data withTimeout:-1 tag:0]; } } [self.tableView reloadData]; //2.接收到客户端数据之后,继续读取客户端数据 //注意在这个方法中调用该方法可以保证一直都能读取到客户端数据,如果不调用,每一个客户端的数据就只能读取一次 [sock readDataWithTimeout:-1 tag:0]; }//服务端发送数据成功-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{ NSLog(@"服务端发送数据");}
1.3-完整代码及示例演示
- 整个项目Demo将在稍后给出下载链接
//// ServiceViewController.m// wifi通讯//// Created by 晓坤张 on 2017/8/12.// Copyright © 2017年 晓坤张. All rights reserved.//#import "ServiceViewController.h"#import "GCDAsyncSocket.h"#import "WIFITools.h"@interface ServiceViewController ()<UITableViewDelegate,UITableViewDataSource>//显示wifi名称文本@property (weak, nonatomic) IBOutlet UILabel *wifiNameLabel;//显示ip地址文本@property (weak, nonatomic) IBOutlet UILabel *ipAddressLabel;//开始连接按钮(监听端口)@property (weak, nonatomic) IBOutlet UIButton *wifiButton;//服务端socket(在socket网络通讯中,万物皆socket,类似于OC中的NSObject)@property(nonatomic,strong)GCDAsyncSocket *serviceSocket;//客户端数组(防止因为客户端是局部变量而被释放!)@property(nonatomic,strong)NSMutableArray *clientArr;//显示所有客户端的数据@property (weak, nonatomic) IBOutlet UITableView *tableView;//存放客户端的消息用于显示@property(nonatomic,strong)NSMutableArray <NSData *>*tableArr;@end@implementation ServiceViewController- (void)viewDidLoad { [super viewDidLoad]; //界面初始化操作 self.wifiNameLabel.text = [NSString stringWithFormat:@"wifi名称:%@",[WIFITools currentWifiSSID]]; self.ipAddressLabel.text = [NSString stringWithFormat:@"ip地址:%@",[WIFITools localWiFiIPAddress]]; self.tableArr = [NSMutableArray array]; // Do any additional setup after loading the view.}//服务端监听按钮点击事件- (IBAction)wifiButtonClick:(UIButton*)sender { //1.创建服务端socket if (!self.serviceSocket) { self.serviceSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; } NSError *error = nil; //2.开始监听端口 //注意:笔者这里随便写的一个端口。实际开发中,端口号不能随便乱写,硬件工程师会给我们一份wifi通讯协议文档,文档中会写明服务端的ip地址及端口号。 我这里默认情况下,ip地址用的就是mac电脑当前连接wifi的ip地址,已经在界面初始化时获取,端口号我写的是1234. [self.serviceSocket acceptOnPort:1234 error:&error]; if (error==nil) { NSLog(@"监听成功"); } else { NSLog(@"%@",error.description); } }#pragma mark - GCDAsyncSocketDelegate//服务端监听到客户端的连接- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{ //1.将连接的客户端添加到数组,防止局部变量被释放 if (!self.clientArr) { self.clientArr = [NSMutableArray array]; } [self.clientArr addObject:newSocket]; //2给客户端发送一个消息,可以让客户端在连接服务端时快速知道自己是否连接服务端成功 //注意:有的wifi通讯在这里会有一个握手的过程,也就是服务端会发给客户端一个用于验证的密文,然后客户端验证通过之后才能继续通讯,否则服务端就会踢掉客户端。主要是保证只能让自己的APP连接自己的产品,防止被其他程序连接 NSString *str = @"服务器:欢迎来到黑马程序员"; //线程延迟发送消息,防止线程阻塞 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [newSocket writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0]; }); //2.开始读取客户端的数据(类似于服务器实时监听客户端发送的数据) //注意:该行代码调用一次只能接收一次数据 [newSocket readDataWithTimeout:-1 tag:0];}//服务端接收数据成功-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"服务端接发送数据:%@",str); //添加到数组用于界面显示 [self.tableArr addObject:data]; //1.服务器的作用主要是数据的转发,所以这里我们将数据转发给其他客户端 for (GCDAsyncSocket *socket in self.clientArr) { //1.1过滤掉发送消息者本身 if (socket != sock) { //1.2给客户端发送数据 [socket writeData:data withTimeout:-1 tag:0]; } } [self.tableView reloadData]; //2.接收到客户端数据之后,继续读取客户端数据 //注意在这个方法中调用该方法可以保证一直都能读取到客户端数据,如果不调用,每一个客户端的数据就只能读取一次 [sock readDataWithTimeout:-1 tag:0]; }//服务端发送数据成功-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{ NSLog(@"服务端发送数据");}#pragma mark -UITableViewDelegate- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.tableArr.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"serviceCell"]; UILabel *nameLabel = (UILabel *)[cell.contentView viewWithTag:1]; UILabel *msgLabel = (UILabel *)[cell.contentView viewWithTag:2]; //读取数据 笔者这里定义的数据协议格式是这样 客户端的设备名称:数据 通过分割:来获取对于数据,实际中应当根据wifi协议来。另外,笔者这里演示的字符串通讯,实际中wifi协议有可能是更加底层的一些通讯格式 例如 二进制字节流 Ascii码等 NSData *data = self.tableArr[indexPath.row]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSArray *arr = [str componentsSeparatedByString:@":"]; //显示数据 nameLabel.text = [NSString stringWithFormat:@"设备名称:%@",arr[0]]; msgLabel.text = [NSString stringWithFormat:@" 数据:%@",arr[1]]; return cell; }- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}/*#pragma mark - Navigation// In a storyboard-based application, you will often want to do a little preparation before navigation- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller.}*/@end
阅读全文
0 0
- 04-WIFI通讯服务端搭建
- 05-WIFI通讯客户端搭建
- 服务端通讯类
- UDP通讯(服务端)
- TCP通讯(服务端)
- Wifi通讯架构介绍
- socket通讯 客户端和服务端
- android 客户端与服务端通讯
- 游戏服务端通讯框架实现
- Socket客户端和服务端通讯
- socket实现服务端客户端通讯
- Ubutnu16.04搭建OpenVPN服务端过程记录
- 02-Wifi通讯架构介绍
- android wifi间的通讯
- 搭建SVN服务端
- 搭建SVN服务端
- 搭建SVN服务端
- SVN服务端搭建
- webpack总结 & 个人理解
- Java 基础知识笔记
- ROS学习(2)--ROS项目试炼
- C. Star sky【前缀和预处理】
- .NET webconfig 配置地址
- 04-WIFI通讯服务端搭建
- Codeforce~545C
- TX2(1)--Jetson TX2 刷机并安装JetPack3.0
- list去除所有为null元素
- hdu 6103 Kirinriki(尺取法)
- node.js学习
- Java中出现[Ljava.lang.String的问题
- 一种不太完善的OpenStreetMap字典汉化方法
- 聊一聊分布式锁的设计