用戶端我們使用iPhone應用程式,畫面比較簡單。點擊發送按鈕,給伺服器發送一些字串過去。點擊接收按鈕就會從伺服器讀取一些字串,並且顯示在畫面上。
有關用戶端應用的UI部分不再介紹了,我們直接看代碼部分,Socket用戶端可以採用CFStream或NSStream實現。為了給讀者介紹更多的知識,本例我們採用NSStream實現。NSStream實現採用Objective-C語言,一些物件導向的類。
下面我們看看用戶端視圖控制器ViewController.h
[cpp]
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 9000
@interface ViewController : UIViewController<NSStreamDelegate>
{
int flag ; //操作標誌 0為發送 1為接收
}
@property (nonatomic, retain) NSInputStream *inputStream;
@property (nonatomic, retain) NSOutputStream *outputStream;
@property (weak, nonatomic) IBOutlet UILabel *message;
- (IBAction)sendData:(id)sender;
- (IBAction)receiveData:(id)sender;
@end
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 9000
@interface ViewController : UIViewController<NSStreamDelegate>
{
int flag ; //操作標誌 0為發送 1為接收
}
@property (nonatomic, retain) NSInputStream *inputStream;
@property (nonatomic, retain) NSOutputStream *outputStream;
@property (weak, nonatomic) IBOutlet UILabel *message;
- (IBAction)sendData:(id)sender;
- (IBAction)receiveData:(id)sender;
@end
定義屬性inputStream和outputStream,它們輸入資料流NSInputStream和輸出資料流NSOutputStream類。它們與伺服器CFStream實現中的輸入資料流CFReadStreamRef和輸出資料流CFWriteStreamRef對應的。
視圖控制器ViewController.m的初始化網路方法initNetworkCommunication代碼:
[cpp]
(void)initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@”192.168.1.103″, 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];
}
- (void)initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL,
(CFStringRef)@”192.168.1.103″, 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];
}
它們都調用initNetworkCommunication方法,並設定作業標識flag,如果flag為0發送資料,flag為1接收資料。
流的狀態的變化觸發很多事件,並回調NSStreamDelegate協議中定義的方法stream:handleEvent:,其代碼如下:
[cpp]
-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSString *event;
switch (streamEvent) {
case NSStreamEventNone:
event = @”NSStreamEventNone”;
break;
case NSStreamEventOpenCompleted:
event = @”NSStreamEventOpenCompleted”;
break;
case NSStreamEventHasBytesAvailable:
event = @”NSStreamEventHasBytesAvailable”;
if (flag ==1 && theStream == _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:NSUTF8StringEncoding];
NSLog(@”接收:%@”,resultstring);
_message.text = resultstring;
}
break;
case NSStreamEventHasSpaceAvailable:
event = @”NSStreamEventHasSpaceAvailable”;
if (flag ==0 && theStream == _outputStream) {
//輸出
UInt8 buff[] = ”Hello Server!”; ④
[_outputStream write:buff maxLength: strlen((const char*)buff)+1]; ⑤
//關閉輸出資料流
[_outputStream close];
}
break;
case NSStreamEventErrorOccurred:
event = @”NSStreamEventErrorOccurred”;
[self close]; ⑥
break;
case NSStreamEventEndEncountered:
event = @”NSStreamEventEndEncountered”;
NSLog(@”Error:%d:%@”,[[theStream streamError] code],
[[theStream streamError] localizedDescription]);
break;
default:
[self close]; ⑦
event = @”Unknown”;
break;
}
NSLog(@”event——%@”,event);
}
-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSString *event;
switch (streamEvent) {
case NSStreamEventNone:
event = @”NSStreamEventNone”;
break;
case NSStreamEventOpenCompleted:
event = @”NSStreamEventOpenCompleted”;
break;
case NSStreamEventHasBytesAvailable:
event = @”NSStreamEventHasBytesAvailable”;
if (flag ==1 && theStream == _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:NSUTF8StringEncoding];
NSLog(@”接收:%@”,resultstring);
_message.text = resultstring;
}
break;
case NSStreamEventHasSpaceAvailable:
event = @”NSStreamEventHasSpaceAvailable”;
if (flag ==0 && theStream == _outputStream) {
//輸出
UInt8 buff[] = ”Hello Server!”; ④
[_outputStream write:buff maxLength: strlen((const char*)buff)+1]; ⑤
//關閉輸出資料流
[_outputStream close];
}
break;
case NSStreamEventErrorOccurred:
event = @”NSStreamEventErrorOccurred”;
[self close]; ⑥
break;
case NSStreamEventEndEncountered:
event = @”NSStreamEventEndEncountered”;
NSLog(@”Error:%d:%@”,[[theStream streamError] code],
[[theStream streamError] localizedDescription]);
break;
default:
[self close]; ⑦
event = @”Unknown”;
break;
}
NSLog(@”event——%@”,event);
}
在讀取資料分支(NSStreamEventHasBytesAvailable)中,代碼第①行為讀取資料準備緩衝區,本例中設定的是1024個位元組,這個大小會對流的讀取有很多的影響。第②行代碼使用hasBytesAvailable方法判斷是否流有資料可以讀,如果有可讀資料就進行迴圈讀取。第③行代碼使用流的read:maxLength:方法讀取資料到緩衝區,第1個參數是緩衝區對象buffer,第2個參數是讀取的緩衝區的位元組長度。
在寫入資料分支(NSStreamEventHasSpaceAvailable)中,代碼第④行是要寫入的資料,第⑤行代碼[_outputStream write:buff maxLength: strlen((const char*)buff)+1]是寫如資料方法。
第⑥和第⑦行代碼[self close]調用close方法關閉,close方法代碼如下:
[cpp]
-(void)close
{
[_outputStream close];
[_outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[_outputStream setDelegate:nil];
[_inputStream close];
[_inputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[_inputStream setDelegate:nil];
}