標籤:
Server端: 用CFSocketCreate()建立socket; 再用CFSocketSetAddress()設定地址; 再用CFSocketCreateRunLoopSource()把socket添加到runloop; 在socket的回調方法中用CFStreamCreatePairWithSocket()建立綁定到socket的CFReadStreamReff流,再將CFReadStreamRef流轉換成NSInputStream類型,設定好delegate,添加到runloop,最後open; 在- (void)stream:(NSStream*)aStream handleEvent:(NSStreamEvent)eventCode方法中處理NSStreamEventHasBytesAvailable事件讀取流中的資料。Client端: 用CFSocketCreate()建立socket; 再用CFSocketConnectToAddress()串連到指定地址; 再用CFSocketCreateRunLoopSource()把socket添加到runloop; 在socket的回調方法中用CFStreamCreatePairWithSocket()建立綁定到socket的CFWriteStreamRef流,再將CFReadStreamRef流轉換成NSInputStream類型,設定好delegate,添加到runloop,最後open; 在- (void)stream:(NSStream*)aStream handleEvent:(NSStreamEvent)eventCode方法中處理NSStreamEventHasSpaceAvailable事件向流中寫資料。 如果我跳過建立socket,直接使用CFStreamCreatePairWithSocketToHost建立CFWriteStreamRef流,就不會出問題,這是為什嗎?請高手指教。
Client:
-(void)CreateConnect:(NSString*)strAddress
{
CFSocketContext sockContext = {0, // 結構體的版本,必須為0
self,
NULL, // 一個定義在上面指標中的retain的回調, 可以為NULL
NULL,
NULL};
_socket = CFSocketCreate(kCFAllocatorDefault, // 為新對象分配記憶體,可以為nil
PF_INET, // 協議族,如果為0或者負數,則預設為PF_INET
SOCK_STREAM, // 通訊端類型,如果協議族為PF_INET,則它會預設為SOCK_STREAM
IPPROTO_TCP, // 通訊端協議,如果協議族是PF_INET且協議是0或者負數,它會預設為IPPROTO_TCP
kCFSocketConnectCallBack, // 觸發回呼函數的socket訊息類型,具體見Callback Types
TCPClientConnectCallBack, // 上面情況下觸發的回呼函數
&sockContext // 一個持有CFSocket結構資訊的對象,可以為nil
);
if(_socket != NULL)
{
struct sockaddr_in addr4; // IPV4
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8888);
addr4.sin_addr.s_addr = inet_addr([strAddress UTF8String]); // 把字串的地址轉換為機器可識別的網路地址
// 把sockaddr_in結構體中的地址轉換為Data
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
CFSocketConnectToAddress(_socket, // 串連的socket
address, // CFDataRef類型的包含上面socket的遠程地址的對象
-1 // 連線逾時時間,如果為負,則不嘗試串連,而是把串連放在後台進行,如果_socket訊息類型為kCFSocketConnectCallBack,將會在串連成功或失敗的時候在後台觸發回呼函數
);
CFRunLoopRef cRunRef = CFRunLoopGetCurrent(); // 擷取當前線程的迴圈
// 建立一個迴圈,但並沒有真正加如到迴圈中,需要調用CFRunLoopAddSource
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
CFRunLoopAddSource(cRunRef, // 運行迴圈
sourceRef, // 增加的運行迴圈源, 它會被retain一次
kCFRunLoopCommonModes // 增加的運行迴圈源的模式
);
CFRelease(sourceRef);
NSLog(@"connect ok");
}
}
// socket回呼函數,同用戶端
static void TCPClientConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
ViewController *client = (ViewController *)info;
if (data != NULL)
{
NSLog(@"串連失敗");
[client.TextView insertText:@"串連失敗\n"];
return;
}
else
{
NSLog(@"串連成功");
[client.TextView insertText:@"串連成功\n"];
// 讀取接收的資料
g_viewPage = client;
[client StartReadThread];
}
}
-(void)StartReadThread
{
NSThread *InitThread = [[NSThread alloc]initWithTarget:self selector:@selector(InitThreadFunc:) object:self];
[InitThread start];
}
-(void)InitThreadFunc:(id)sender
{
while (1) {
[self readStream];
}
}
// 讀取接收的資料
-(void)readStream
{
char buffer[1024];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *str = @"伺服器發來資料:";
recv(CFSocketGetNative(_socket), buffer, sizeof(buffer), 0);
{
str = [str stringByAppendingString:[NSString stringWithUTF8String:buffer]];
}
NSLog(str);
//回介面顯示資訊
[self performSelectorOnMainThread:@selector(ShowMsg:) withObject:str waitUntilDone:NO];
[pool release];
}
-(void)ShowMsg:(id)sender
{
NSString *str = sender;
str = [str stringByAppendingString:@"\n"];
[self.TextView insertText:str];
}
// 發送資料
- (void)sendMessage {
NSLog(@"hello Server");
char *data = "hello Server";
send(CFSocketGetNative(_socket), data, strlen(data) + 1, 0);
}
- (IBAction)SendMessageTouch:(id)sender {
[self sendMessage];
}
- (IBAction)TouchConnectServer:(id)sender {
NSString *serverIp = self.textField.text;
[self CreateConnect:serverIp];
}
- (void)dealloc {
[_TextView release];
[_textField release];
[super dealloc];
}
CFWriteStreamRef outputStream;
Sever:
@implementation SocketServer
-(int)setupSocket
{
CFSocketContext sockContext = {0, // 結構體的版本,必須為0
self,
NULL, // 一個定義在上面指標中的retain的回調, 可以為NULL
NULL,
NULL};
_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, TCPServerAcceptCallBack, &sockContext);
if (NULL == _socket) {
NSLog(@"Cannot create socket!");
return 0;
}
int optval = 1;
setsockopt(CFSocketGetNative(_socket), SOL_SOCKET, SO_REUSEADDR, // 允許重用本地地址和連接埠
(void *)&optval, sizeof(optval));
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8888);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
if (kCFSocketSuccess != CFSocketSetAddress(_socket, address))
{
NSLog(@"Bind to address failed!");
if (_socket)
CFRelease(_socket);
_socket = NULL;
return 0;
}
CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
CFRunLoopAddSource(cfRunLoop, source, kCFRunLoopCommonModes);
CFRelease(source);
return 1;
}
-(void) SendMessage
{
char *str = "你好 Client";
uint8_t * uin8b = (uint8_t *)str;
if (outputStream != NULL)
{
CFWriteStreamWrite(outputStream, uin8b, strlen(str) + 1);
}
else {
NSLog(@"Cannot send data!");
}
}
// 開闢一個線程線程函數中
-(void) StartServer
{
int res = [self setupSocket];
if (!res) {
exit(1);
}
NSLog(@"運行當前線程的CFRunLoop對象");
CFRunLoopRun(); // 運行當前線程的CFRunLoop對象
}
-(void)ShowMsgOnMainPage:(NSString*)strMsg
{
[self.delegate ShowMsg:strMsg];
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// socket回呼函數
static void TCPServerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
if (kCFSocketAcceptCallBack == type)
{
// 本地通訊端控制代碼
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t nameLen = sizeof(name);
if (0 != getpeername(nativeSocketHandle, (struct sockaddr *)name, &nameLen)) {
NSLog(@"error");
exit(1);
}
CFReadStreamRef iStream;
CFWriteStreamRef oStream;
// 建立一個可讀寫的socket串連
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &iStream, &oStream);
if (iStream && oStream)
{
CFStreamClientContext streamContext = {0, info, NULL, NULL};
if (!CFReadStreamSetClient(iStream, kCFStreamEventHasBytesAvailable,readStream, &streamContext))
{
exit(1);
}
if (!CFWriteStreamSetClient(oStream, kCFStreamEventCanAcceptBytes, writeStream, &streamContext))
{
exit(1);
}
CFReadStreamScheduleWithRunLoop(iStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFWriteStreamScheduleWithRunLoop(oStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFReadStreamOpen(iStream);
CFWriteStreamOpen(oStream);
} else
{
close(nativeSocketHandle);
}
}
}
// 讀取資料
void readStream(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
{
UInt8 buff[255];
CFReadStreamRead(stream, buff, 255);
///根據delegate顯示到主介面去
NSString *strMsg = [[NSString alloc]initWithFormat:@"用戶端傳來訊息:%s",buff];
SocketServer *info = (SocketServer*)clientCallBackInfo;
[info ShowMsgOnMainPage:strMsg];
}
void writeStream (CFWriteStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
{
outputStream = stream;
}
ios Socket通訊端編程(轉)