今天发现了一个iOS Socket 很好的学习材料
原文连接:
http://www.cocoachina.com/bbs/read.php?tid=6146#
这个类使用了Singleton,因此永远只有一个实例。没有实例时会自动生成实例,可以在程序中的任何位置调用它。
一般来说,只要跟服务器建立一次连接即可,产生一对stream,分别是outStream和inStream,所有的数据都通过它们不断地发送和接收。
stream的end意味着连接中断,如果还需要访问服务器的话,得重新连接stream。(也就是重新实例化一下我这个类)
每次发送和接受的数据包大小需要自己控制,而不是等stream来告诉你这个数据包有多大,因为stream不会告诉你……
控制方法之一:通过添加一个特殊的后缀来判断,比如“<EOF>”,每次读到这个组合就认为数据读完。但是问题很明显,这个只能用于string。
控制方法之二:通过添加一个4字节的前缀来判断长度。这4个byte的byte[]数组,是当前数据包的长度信息,根据这个信息来读取一定长度的数据。
每次数据收完后,我用了一个取巧的方法来把数据返还给调用stream的函数……这个部分需要改进。
SynthesizeSingleton.h,实现singleton的类
//
// SynthesizeSingleton.h
// CocoaWithLove
//
// Created by Matt Gallagher on 20/10/08.
// Copyright 2009 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file without charge in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \
\
static classname *shared##classname = nil; \
\
+ (classname *)shared##classname \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [[self alloc] init]; \
} \
} \
\
return shared##classname; \
} \
\
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return self; \
} \
\
- (id)retain \
{ \
return self; \
} \
\
- (NSUInteger)retainCount \
{ \
return NSUIntegerMax; \
} \
\
- (void)release \
{ \
} \
\
- (id)autorelease \
{ \
return self; \
}
Stream.h
#import <Foundation/Foundation.h>
#import <CFNetwork/CFNetwork.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@interface Stream : NSObject {
NSInputStream *inStream;
NSOutputStream *outStream;
NSMutableData *dataBuffer;
BOOL _hasEstablished;
id _currentObject;
int _numCondition;
BOOL _isFirstFourBytes;
uint remainingToRead;
}
+ (Stream *)sharedStream;
-(void)requestData:(NSString *)requestString whoRequest:(id)currentObject condition:(int)numCondition;
-(void)manageData:(NSData *)receivedData;
@end
Stream.m
#import "Stream.h"
#import "SynthesizeSingleton.h"
@implementation Stream
SYNTHESIZE_SINGLETON_FOR_CLASS(Stream);
-(void)startClient
{
_hasEstablished = NO;
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
NSString *server = /*你的服务器地址,比如我公司服务器地址[url]www.javista.com[/url]*/;
//这里没有用NSStream的getStreamsToHost,是因为真机编译时有黄色提示说不存在这个函数。
//虽然真机能用,但我担心上传到APP Store时会被reject,所以就用了更底层的CFStreamCreatePairWithSocketToHost。
//其实一点都不难,一样用的~
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef)server,
1234,//服务器接收数据的端口
&readStream,
&writeStream);
if(readStream && writeStream)
{
inStream = (NSInputStream *)readStream;
outStream = (NSOutputStream *)writeStream;
}
else
{
//Error Control
}
}
-(void)closeStreams{
[[PromptView sharedPromptView] dismissPromptView];
[inStream close];
[outStream close];
[inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inStream setDelegate:nil];
[outStream setDelegate:nil];
[inStream release];
[outStream release];
inStream = nil;
outStream = nil;
}
-(void)openStreams{
[inStream retain];
[outStream retain];
[inStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey];
[outStream setProperty:NSStreamSocketSecurityLevelSSLv3 forKey:NSStreamSocketSecurityLevelKey];
//不需要SSL的话,下面这行可以去掉。
CFWriteStreamSetProperty((CFWriteStreamRef)outStream, kCFStreamPropertySSLSettings, [NSMutableDictionary dictionaryWithObjectsAndKeys:(id)kCFBooleanFalse,kCFStreamSSLValidatesCertificateChain,kCFBooleanFalse,kCFStreamSSLIsServer,nil]);
[inStream setDelegate:self];
[outStream setDelegate:self];
[inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inStream open];
[outStream open];
}
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if(_isFirstFourBytes)//读取前4个字节,算出数据包大小
{
uint8_t bufferLen[4];
if([inStream read:bufferLen maxLength:4] == 4)
{
remainingToRead = ((bufferLen[0]<<24)&0xff000000)+((bufferLen[1]<<16)&0xff0000)+((bufferLen[2]<<8)&0xff00)+(bufferLen[3] & 0xff);
_isFirstFourBytes = NO;
}
else
{
[self closeStreams];
//Error Control
}
}
else//根据数据包大小读取数据
{
int actuallyRead;
uint8_t buffer[32768];//32KB的缓冲区,缓冲区太小的话会明显影响真机上的通信速度
if (!dataBuffer) {
dataBuffer = [[NSMutableData alloc] init];
}
actuallyRead = [inStream read:buffer maxLength:sizeof(buffer)];
if(actuallyRead == -1){
[self closeStreams];
//Error Control
}else if(actuallyRead == 0){
//Do something if you want
}else{
[dataBuffer appendBytes:buffer length:actuallyRead];
remainingToRead -= actuallyRead;
}
if(remainingToRead == 0)
{
_isFirstFourBytes = YES;
[self manageData:dataBuffer];//数据接收完毕,把数据送回调用sream的函数
[dataBuffer release];
dataBuffer = nil;
}
}
break;
}
case NSStreamEventEndEncountered://连接断开或结束
{
[self closeStreams];
break;
}
case NSStreamEventErrorOccurred://无法连接或断开连接
{
if([[aStream streamError] code])//确定code不是0……有时候正常使用时会跳出code为0的错误,但其实一点问题都没有,可以继续使用,很奇怪……
{
[self closeStreams];
break;
}
}
case NSStreamEventOpenCompleted:
{
_hasEstablished = YES;
break;
}
case NSStreamEventHasSpaceAvailable:
{
break;
}
case NSStreamEventNone:
default:
break;
}
}
//判断是否能连接到服务器。这个函数用来判断网络是否连通还好,要真的判断服务器上对应的端口是否可以连接,不是很好用来着……
-(BOOL)isServerAvailable{
NSString *addressString = /*你的服务器地址,比如我公司地址[url]www.javista.com[/url]*/;
if (!addressString) {
return NO;
}
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [addressString UTF8String]);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
{
return NO;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
return (isReachable && !needsConnection) ? YES : NO;
}
-(void)requestData:(NSString *)requestString whoRequest:(id)currentObject condition:(int)numCondition
{
if(![self isServerAvailable])//如果无法连通到服务器
{
//Error Control
}
else
{
if(inStream == nil || outStream == nil)
{
[[Stream sharedStream] startClient];
[[Stream sharedStream] openStreams];
_isFirstFourBytes = YES;
}
if(inStream != nil && outStream != nil)
{
_currentObject = currentObject;//记下是谁调用了requestData(记下了它的指针)
_numCondition = numCondition;//参数,以便有时候需要区分同一个类里发来的不同请求
if(_hasEstablished)
{
NSData *requestData = [requestString dataUsingEncoding:NSUTF8StringEncoding];
int dataLength = [requestData length];
//创建前4个字节用来表示数据包长度
uint8_t len[4];
for(int i = 0;i<4;i++)
{
len[i] = (Byte)(dataLength>>8*(3-i)&0xff);
}
[/i]
//将这4个字节添加到数据的开头
NSMutableData *dataToSend = [NSMutableData dataWithBytes:len length:4];
[dataToSend appendData:requestData];
int remainingToWrite = dataLength+ 4;
void * marker = (void *)[dataToSend bytes];
int actuallyWritten;
while ([outStream hasSpaceAvailable]) {
if (remainingToWrite > 0) {
actuallyWritten = 0;
if(remainingToWrite < 32768)
actuallyWritten = [outStream write:marker maxLength:remainingToWrite];//不足32KB数据时发送剩余部分
else
actuallyWritten = [outStream write:marker maxLength:32768];//每次32KB数据
if ((actuallyWritten == -1) || (actuallyWritten == 0))
{
[self closeStreams];
//Error control
}
else
{
remainingToWrite -= actuallyWritten;
marker += actuallyWritten;
}
}
else
{
break;
}
}
}
else
{
//Error Control
}
}
}
}
-(void)manageData:(NSData *)receivedData{
[_currentObject getData:receivedData condition:_numCondition];//执行_currentObject指针所指向的类里的getData函数,并把收到的数据传递过去
}
- (void)dealloc {
[super dealloc];
}
@end
用的时候,在调用stream的类的头文件里#import这个Stream.h,并添加一个函数叫- (void)getData:(NSData *)receivedData condition:(int)numCondition;
发送时:
[[Stream SharedStream] requestData:@"login"/*需要发送的命令*/ whoRequest:self/*把自己的指针传递过去*/ condition:0/*用以区分不同功能的请求*/];
接收完毕后Stream会调用这个类里的getData函数,这个函数写法如下:
- (void)getData:(NSData *)receivedData condition:(int)numCondition{
switch(numCondition)
{
case 0:
//Do something
break;
case 1:
//Do something different
break;
default:
break;
}
}
分享到:
相关推荐
本资料“ios开发之iOSSocket开发”主要涵盖了Socket编程的基本概念、原理以及在iOS平台上的实际应用。下面我们将深入探讨这个主题。 首先,Socket是操作系统提供的一个接口,允许应用程序通过网络进行通信。它抽象...
总的来说,iOS中的Socket开发涉及到网络通信的基本原理,包括TCP连接的建立、数据流的管理和数据包的边界判断。同时,通过单例模式可以有效地管理Socket连接,确保在整个应用中只存在一个连接实例,从而简化了代码...
在iOS开发中,Socket编程是实现设备间网络通信的基础,特别是在需要实时数据交换的应用场景,如在线游戏、即时通讯等。本示例“iOS Socket 使用Demo”将介绍如何使用GCDAsyncSocket库来构建一个简单的Socket应用,并...
在iOS移动端开发中,Socket通信是一种基础且强大的网络通信方式,尤其在文件上传和下载的场景下,其灵活性和高效性被广泛运用。Socket通信基于TCP/IP协议,允许应用程序通过网络进行双向通信,实现了设备间的直接...
在iOS开发中,Socket编程是实现设备间网络通信的基础,它允许应用程序通过网络发送和接收数据。本示例代码库“iOSSnippet-master”提供了使用不同iOS Socket API进行编程的方法。以下是对这些方法的详细说明: 1. *...
在iOS开发中,实现通过Socket连接局域网打印机进行打印是一项技术性较强的任务。这个过程涉及到网络编程、设备发现和通信协议的理解。以下是对这一主题的详细解释。 首先,我们需要理解Socket编程的基本概念。...
在iOS应用开发中,TCP Socket通信是一种常见的网络通信方式,尤其适用于需要稳定、有序且可靠的数据传输场景。`HHZSocket`框架是专为iOS设计的一个简洁高效、易于使用的TCP Socket通信库。本篇文章将深入探讨该框架...
在iOS开发中,Socket编程是一种基础且重要的网络通信技术,它允许设备间的双向通信。"ios手机socket服务端源码" 提供了一种在iOS设备上实现Socket服务器的方法,这通常用于设备间的数据交换或者远程控制等场景。下面...
使用Java 语言开发Socket Server,实现和android和iOS Socket client通讯的工程代码,数据可以使用TCP或UDP协议,通过文本,XML,Json,Base64及二进制传送,支持中文。
5. **iOS Socket开发**:在iOS中,一般使用CocoaAsyncSocket或GCDAsyncSocket这样的第三方库进行Socket通信。这些库封装了底层的Socket操作,使得在Objective-C或Swift中进行Socket编程更为方便。同时,iOS应用需要...
iOS Socket编程是iOS应用开发中涉及网络通信的重要技术,它允许设备通过TCP/IP协议栈与其他设备或服务器进行数据传输。本文将深入探讨iOS中的Socket编程,包括C/S三次握手过程、iOS Socket技术的层次结构以及...
在iOS开发中,网络编程是不可或缺的一部分,而Socket编程则是实现网络通信的基础。Socket是一种低级别的、原始的网络通信机制,允许应用程序通过网络发送和接收数据。本示例"iOS网络编程SocketDemo"旨在帮助开发者...
在iOS开发中,网络通信是应用不可或缺的一部分,而Socket编程是实现网络通信的一种基础方式。本文将详细讲解如何使用Objective-C实现一个基于AsyncSocket库的单例封装,以供iOS应用进行TCP通信。 首先,我们需要...
在iOS开发中,Socket通信是一种基础且重要的网络编程技术,尤其在实时数据交换、服务器与客户端之间的双向通信等场景下应用广泛。"ios-Socket通信.zip"这个压缩包可能包含了关于如何在iOS平台上使用AsyncSocket库...
在iOS开发中,为了实现设备与打印机之间的通信,通常会使用网络编程技术,尤其是套接字(socket)通信。在给定的场景中,“iOS socket连接打印机打印小票”涉及了利用CocoaAsyncSocket库来建立与打印机的连接,并...
Socket通信在iOS开发中是一种常见的网络通信方式,它允许设备之间通过互联网进行双向通信。本文将深入探讨如何在iOS平台上使用Socket进行通信,主要基于提供的开源框架和代码示例。 一、Socket基础知识 Socket,也...
Socket编程在iOS应用开发中扮演着重要角色,它允许设备通过网络进行数据交换。本文将深入探讨iOS中的Socket通信,包括基本概念、实现步骤、示例代码以及注意事项。 Socket,通常被称为套接字,是网络通信的基础,它...
在iOS开发中,Socket编程是实现设备间网络通信的基础,常用于构建实时、低延迟的应用,如聊天应用、在线游戏等。本知识点将深入探讨如何使用第三方库AsyncSocket实现iOS中的Socket通信,以及如何通过提供的两个demo...
在iOS开发中,Socket通信是一种基础且重要的网络编程技术,它允许设备间的双向通信,常用于实现客户端与服务器之间的实时数据交互。本实例代码“IOS-Socket通信实例代码”提供了在iOS平台上使用Socket进行通信的具体...
在iOS开发中,Socket编程是实现设备间网络通信的关键技术之一。`CocoaAsyncSocket`是一个流行的开源库,专门用于Objective-C和Swift平台,提供异步的TCP和UDP Socket操作。这个压缩包“robbiehanson-...