Scoket
流程:
服务器: 客户端:
监听端口,等待访问 指定服务器ip、端口、传输协议
↓ ↓
请求
←-----------------------------
-----------------------------→
响应并建立连接
↓ ↓
IO传输
<------------------------------>
↓ ↓
关闭socket,释放资源 关闭socket,释放资源
2.Peer To Peer 点对点通信(蓝牙)
服务器端
.h
#import <Foundation/Foundation.h> #import <netinet/in.h> #import <sys/socket.h> @interface Server : NSObject<NSNetServiceDelegate> @property(nonatomic,strong)NSNetService *service; @property(nonatomic,strong)NSSocketPort *socket; @property(nonatomic,strong)NSInputStream *inputStream; @property(nonatomic,strong)NSOutputStream *outputStream; @property(nonatomic,assign)int port; @end
.m
#import "Server.h" /* 服务器接收到客户端请求后回调,它是CFSocketCallBack类型 */ void AcceptCallBack(CFSocketRef socket,CFSocketCallBackType type,CFDataRef address,const void *data,void *info); /* 客户端在socket中读取数据时调用 */ void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType,void *clientCallBackInfo); /* 客户端把数据写入socket时调用 */ void ReadStreamClientCallBack(CFReadStreamRef stream,CFStreamEventType eventType, void *clientCallBackInfo); @implementation Server -(id)init{ if (self = [super init]) { if ([self startServer]) { [self publishService]; }else{ NSLog(@"启动服务器失败"); } } return self; } -(BOOL)startServer{ /*定义一个server socket引用*/ CFSocketRef sserver; //创建socket context CFSocketContext ctx = {0,(__bridge void *)(self),NULL,NULL,NULL}; //创建server socket TCP IPv4设置回调函数 sserver = CFSocketCreate(NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)AcceptCallBack, &ctx); if (sserver == NULL) { return NO; } //设置是否重新绑定标志 int yes = 1; //设置socket的属性 SOL_SOCKET设置tcp SO_REUSEADDR是重新绑定 yes 是否重新绑定 setsockopt(CFSocketGetNative(sserver), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); //设置端口和地址 struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //memset函数对指定的地址进行内存复制 addr.sin_len = sizeof(addr); addr.sin_family = AF_INET; //设置ipv4 addr.sin_port = 0; //表示动态分配端口 addr.sin_addr.s_addr = htonl(INADDR_ANY); //从指定字节缓冲区复制,一个不可变的CFData对象 CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr, sizeof(addr)); //设置socket if (CFSocketSetAddress(sserver, (CFDataRef)address) != kCFSocketSuccess) { fprintf(stderr, "socket绑定失败!\n"); CFRelease(sserver); return NO; } //通过 Bonjour广播服务器时使用 NSData *socketAddressActualData = (__bridge NSData *)(CFSocketCopyAddress(sserver)); //转换 socket_in -> socketAddressActual struct sockaddr_in socketAddressActual; memcpy(&socketAddressActual, [socketAddressActualData bytes], [socketAddressActualData length]); self.port = ntohs(socketAddressActual.sin_port); printf("socket listening on port %d\n",self.port); //创建一个RunLoop socket源 CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, sserver, 0); //socket源添加到RunLoop中 CFRunLoopAddSource(CFRunLoopGetCurrent(), sourceRef, kCFRunLoopCommonModes); CFRelease(sourceRef); return YES; } -(BOOL)publishService{ _service = [[NSNetService alloc] initWithDomain:@"local." type:@"_tonyipp._tcp" name:@"tony" port:self.port]; //添加服务到当前的RunLoop [_service scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [_service setDelegate:self]; [_service publish]; return YES; } 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, "创建读写Socket流失败!!!\n"); return; } CFStreamClientContext streamText = {0,NULL,NULL,NULL,NULL}; //注册两种回调函数 CFReadStreamSetClient(readStream, kCFStreamEventHasBytesAvailable, ReadStreamClientCallBack, &streamText); CFWriteStreamSetClient(writeStream, kCFStreamEventCanAcceptBytes, WriteStreamClientCallBack, &streamText); //加入到循环中 CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); CFReadStreamOpen(readStream); CFWriteStreamOpen(writeStream); } void ReadStreamClientCallBack(CFReadStreamRef stream,CFStreamEventType eventType, void *clientCallBackInfo){ uint8 buff[255]; CFReadStreamRef inputStream = stream; if (NULL != inputStream) { CFReadStreamRead(stream, buff, 255); printf("接收数据:%s\n",buff); CFReadStreamUnscheduleFromRunLoop(inputStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); inputStream = NULL; } } void WriteStreamClientCallBack(CFWriteStreamRef stream, CFStreamEventType eventType,void *clientCallBackInfo){ CFWriteStreamRef outputStream = stream; //输出 UInt8 buff[] = "您好,我能够使用Socket了,哈哈哈"; if (NULL != outputStream) { CFWriteStreamWrite(outputStream, buff, strlen((const char *)buff)+1); CFWriteStreamClose(outputStream); CFWriteStreamUnscheduleFromRunLoop(outputStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); outputStream = NULL; } } #pragma mark -NetServiceDelegate- -(void)netServiceDidPublish:(NSNetService *)sender{ NSLog(@"netServiceDidPublish"); if ([@"tony" isEqualToString:sender.name]) { if (![sender getInputStream:&_inputStream outputStream:&_outputStream]) { NSLog(@"连接服务器失败"); return; } } } @end
客户端
.h
#include <sys/socket.h> #include <netinet/in.h> #define PORT 49302 @interface MViewController : UIViewController<NSStreamDelegate>{ int flag;//操作标志 0为发送 1为接收 } @property (weak, nonatomic) IBOutlet UIButton *sendBtn; @property (weak, nonatomic) IBOutlet UIButton *acceptBtn; @property (weak, nonatomic) IBOutlet UILabel *messageLabel; @property(nonatomic,retain)NSInputStream *inputStream; @property(nonatomic,retain)NSOutputStream *outputStream; -(IBAction)sendData:(id)sender; -(IBAction)receiveData:(id)sender; @end
.m
#import "MViewController.h" @interface MViewController () -(void)initNetworkCommunication; @end @implementation MViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. } -(void)initNetworkCommunication{ CFReadStreamRef readStream; CFWriteStreamRef writeStream; CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.1.24", 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]; } -(IBAction)sendData:(id)sender{ flag = 0; [self initNetworkCommunication]; } -(IBAction)receiveData:(id)sender{ flag = 1; [self initNetworkCommunication]; } #pragma mark delegate -(void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{ NSString *event; switch (eventCode) { case NSStreamEventNone: { event = @"None"; } break; case NSStreamEventOpenCompleted: { event = @"OpenCompleted"; } break; case NSStreamEventHasBytesAvailable: { event = @"HasBytesAvailable"; if (flag == 1 && aStream == _inputStream) { NSMutableData *input = [[NSMutableData alloc] init]; uint8_t buffer[1024]; int len; while ([_inputStream hasBytesAvailable]) { len = [_inputStream read:buffer maxLength:sizeof(buffer)]; if (len > 0) { [input appendBytes:buffer length:len]; } } NSString *resultString = [[NSString alloc] initWithData:input encoding:4]; _messageLabel.text = resultString; } } break; case NSStreamEventHasSpaceAvailable: { event = @"HasSpaceAvailable"; if (flag == 0 && aStream == _outputStream) { //输出 UInt8 buff[] = "您好,能收到我的信息么?"; [_outputStream write:buff maxLength:strlen((const char *)buff) +1]; [_outputStream close]; } } break; case NSStreamEventErrorOccurred: { [self close]; } break; case NSStreamEventEndEncountered: { [self close]; } break; default: { [self close]; } break; } } -(void)close{ [_outputStream close]; [_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_outputStream setDelegate:nil]; [_inputStream close]; [_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; [_inputStream setDelegate:nil]; } @end