ios Socket通訊端編程

來源:互聯網
上載者:User

標籤:

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通訊端編程(轉)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.