怎么使用TCP/IP与服务器进行通信

如题所述

第1个回答  推荐于2018-04-20
使用流进行网络通信
使用socket进行网络编程的最简单方式是使用NSStream。
NSStream类对流操作进行了抽象,包括对各种流数据的读和写:内存流、网络流或文件流。当然,通过NSStream也可以与服务器进行通信。
无论是通过NSStream向服务器写数据,还是从NStream对象中读取服务器数据,都是一件简单的事情。
在Mac OS X中,使用NSHost和NSStream与服务器进行连接的代码如下:

NSInputStream *iStream;
NSOutputStream *oStream;
uint portNo = 500;
NSURL *website = [NSURLURLWithString:urlStr];
NSHost *host = [NSHost hostWithName:[websitehost]]; [NSStream getStreamsToHost:host
port:portNo
inputStream:&iStream
outputStream:&oStream];
NSStream的getStreamsToHost:port:inputStream:outputStream:方法用于连接服务器并创建一对输入输出流用于向服务器读写数据。问题是iOS中并没有这个方法。因此上述代码无法用于iPhoneapp中。

解决这个问题,需要为NSStream增加新的类别以增加
getStreamToHost:Port:inputstream:outputStream:方法。在Xcode中新建文件
NSStreamAdditions.m。然后在NSStreamAdditions.h中编写代码如下:

@interface NSStream (MyAdditions)
+ (void)getStreamsToHostNamed:(NSString*)hostName
port:(NSInteger)port
inputStream:(NSInputStream **)inputStreamPtr
outputStream:(NSOutputStream **)outputStreamPtr;
@end

在NSStreamAdditions.m文件中加入下列代码。
#import "NSStreamAdditions.h"

@implementation NSStream (MyAdditions)

+ (void)getStreamsToHostNamed:(NSString*)hostName
port:(NSInteger)port
inputStream:(NSInputStream **)inputStreamPtr
outputStream:(NSOutputStream **)outputStreamPtr
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;

assert(hostName != nil);
assert( (port > 0) && (port <65536) );
assert( (inputStreamPtr != NULL) ||(outputStreamPtr != NULL) );

readStream = NULL;
writeStream = NULL;

CFStreamCreatePairWithSocketToHost(
NULL,
(CFStringRef) hostName,
port,
((inputStreamPtr != nil) ?&readStream : NULL),
((outputStreamPtr != nil) ? &writeStream : NULL)
);

if (inputStreamPtr != NULL) {
*inputStreamPtr = [NSMakeCollectable(readStream)autorelease];
}
if (outputStreamPtr != NULL) {
*outputStreamPtr =[NSMakeCollectable(writeStream) autorelease];
}
}

@end

以上代码为NSStream类增加了一个类方法叫做:
getStreamsToHostNamed:port:inputStream:outputStream:
现在你可以在iPhone app中,使用该方法了。

作者注:该类别代码基于苹果文档 Apple’s Technical Q&A1652。
在NetworkViewController.m中,加入如下代码:

#import "NetworkViewController.h"
#import "NSStreamAdditions.h"
@implementation NetworkViewController
NSMutableData *data;
NSInputStream *iStream;
NSOutputStream *oStream;
定义connectToServerUsingStream:portNo:方法如下。在方法中我们连接了服务器并创建了一对输入/输出流:
-(void) connectToServerUsingStream:(NSString*)urlStr portNo: (uint) portNo {
if (![urlStrisEqualToString:@""]) {
NSURL *website =[NSURL URLWithString:urlStr];
if (!website) {
NSLog(@"%@ is not a valid URL");
return;
} else {
[NSStream getStreamsToHostNamed:urlStr
port:portNo
inputStream:&iStream
outputStream:&oStream];
[iStreamretain];
[oStream retain];
[iStreamsetDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStreamopen];
[iStream open];
}
}
}

方法中,我们将input和output放到了runloop中以便接收事件。这样做,是为了防止流中没有有效数据时代码产生阻塞。input和
output的委托属性都设置为self,因此我们还应该在NetworkViewController类中实现委托方法以便接收流数据。
使用 CFNetwork 进行网络通信
另一种TCP通信的方法是使用CFNetwork框架。CFNetwork属于核心服务框架(C语言库),提供了对HTTP、FTP、BSDsockets等网络协议的封装。
为了演示如何使用CFNetwork框架,在NetworkViewController.m文件中加入如下语句:

#import "NetworkViewController.h"
#import "NSStreamAdditions.h"

@implementation NetworkViewController
NSMutableData *data;
NSInputStream *iStream;
NSOutputStream *oStream;
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
定义 connectToServerUsingCFStream:portNo: 方法如下:
-(void) connectToServerUsingCFStream:(NSString *)
urlStr portNo: (uint) portNo{
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) urlStr,
portNo,
&readStream,
&writeStream);
if (readStream &&writeStream){
CFReadStreamSetProperty(readStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
iStream =(NSInputStream *)readStream;
[iStream retain];
[iStream setDelegate:self];
[iStreamscheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)writeStream;
[oStreamretain];
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoopcurrentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
}
}

先,我们使用CFStreamCreatePairWithSocketToHost()方法创建了一个到服务器的TCP/IP连接,以及一对输入输出
流。然后将它们转换为等价的O-C对象——NSInputStream和NSOutputStream。接下来跟前面一样,设置delegate属性并放
到runloop中运行。
发送数据
要想服务器发送数据,请使用NSOutputStream对象:
-(void) writeToServer:(const uint8_t *) buf {
[oStream write:bufmaxLength:strlen((char*)buf)];
}
这段代码发送了一个无符号整型数组到服务器。
读取数据
当服务器有数据到达,stream:handleEvent:方法被触发。因此我们只需在这个方法中读取数据即可。
- (void)stream:(NSStream *)streamhandleEvent:(NSStreamEvent)eventCode {

switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
if (data == nil) {
data = [[NSMutableData alloc] init];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[data appendBytes:(const void *)buf length:len];
int bytesRead;
bytesRead += len;
} else {
NSLog(@"No data.");
}

NSString *str =[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(str);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fromserver"
message:str
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];

[str release];
[data release];
data = nil;
} break;
}
}
该方法有两个参数。一个NSStream对象和一个NSStreamEvent常量。NSStreamEvent常量可能包含以下取值:
NSStreamEventNone -- 没有任何事件
NSStreamEventOpenCompleted -- 流打开成功.
NSStreamEventHasBytesAvailable -- 此时流中有字节待读取
NSStreamEventHasSpaceAvailable -- 此时可向流中写入数据
NSStreamEventErrorOccurred -- 有错误发生
NSStreamEventEndEncountered -- 到达流的末尾
对于输入流,你应当检测NSStreamEventHasBytesAvailable 常量。在这里,我们从输入流中读取了数据并显示在UIAlertView中。

stream:handleEvent:方法中,很容易检查到连接错误。在本例中,如果
connectToServerUsingStream:portNo:方法连接服务器失败,则在stream:handleEvent方法将被调用并在
NSStreamEvent参数中传递一个NSStreamEventErrorOccured错误。本回答被网友采纳
相似回答