1. 程式人生 > >iphone開發之輕鬆搞定原生socket 程式設計,阻塞與非阻塞,收發自如

iphone開發之輕鬆搞定原生socket 程式設計,阻塞與非阻塞,收發自如

iphone socket 開發

在iphone的平臺下,要進行socket開發其實有很多種的方法,開源的庫Asyncsocket,官方的CFSocket,還有BSD的socket。

這裡要做一個簡單的socket普及,這裡包含在socket的設定非阻塞喝超時的控制邏輯,心跳包和執行緒的啟動時間同步的控制。

這裡都是標準的linux的流程

先建立一個socket

- (int)CSocket

{

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

    {

      perror("socket");

      exit(errno);    

    }

    return sockfd;

}

然後是連結

//////////////////

- (BOOL)ConnectToServer:(NSString*)addr port:(int)port

{

    their_addr.sin_family = AF_INET;

    their_addr.sin_addr.s_addr = inet_addr([addr UTF8String]);

    their_addr.sin_port = htons(port);

    bzero(&(their_addr.sin_zero), 8);

    int conn = connect(sockfd, (struct

 sockaddr*)&their_addr, sizeof(struct sockaddr));

    NSLog(@"Connect error no is %d:",conn);

    return misConnect;

}

這樣子的連結是阻塞的,這樣子就比較不好,可以設定成非阻塞的方式來控制超時

    /***************************************************/

    //connect之前,設成非阻塞模式

    int flags = fcntl(sockfd, F_GETFL,0);

    fcntl(sockfd,F_SETFL, flags | O_NONBLOCK);

    /***************************************************

//這是另外一種設定成非阻塞的方式

     int flags;

     if((flags = fcntl(sockfd, F_GETFL)) < 0 )

     {

     perror("fcntl F_SETFL");

     }

     flags |= O_NONBLOCK;

     if(fcntl(sockfd, F_SETFL,flags) < 0)

     {

     perror("fcntl");

     }

    ****************************************************/

設定connect後可以設定用select設定超時

/***************************************************/

    //設定超時

    fd_set          fdwrite;

    struct timeval  tvSelect;

    FD_ZERO(&fdwrite);

    FD_SET(sockfd, &fdwrite);

    tvSelect.tv_sec = 2;

    tvSelect.tv_usec = 0;

    int retval = select(sockfd + 1,NULL, &fdwrite, NULL, &tvSelect);

    if(retval < 0)

    {

      if ( errno == EINTR )

      {

        NSLog(@"select error");

      }

      else

      {

        NSLog(@"error");

        close(sockfd);

      }

    }

    else if(retval == 0)

    {

      NSLog(@"select timeout........");

    }

    else if(retval > 0)

    {

      misConnect = YES;

    }

    /***************************************************/

    //connect成功之後,設成阻塞模式

    flags = fcntl(sockfd, F_GETFL,0);

    flags &= ~ O_NONBLOCK;

    fcntl(sockfd,F_SETFL, flags);

    /***************************************************/

    //設定不被SIGPIPE訊號中斷,物理鏈路損壞時才不會導致程式直接被Terminate

//在網路異常的時候如果程式收到SIGPIRE是會直接被退出的。

    struct sigaction sa;

    sa.sa_handler = SIG_IGN;

    sigaction( SIGPIPE, &sa, 0 );

    /***************************************************/

然後就可以收發資料了

send,write兩種方法都可以,你需要自己維護一個佇列,控制時間等等

    NSString *str = [SendCmdArray objectAtIndex:0];

    NSData *data = [str dataUsingEncoding:NSISOLatin1StringEncoding];

//  ssize_t datalen = send(sockfd,[data bytes],[data length],0);

    ssize_t datalen = write(sockfd, [data bytes], [data length]);

    if(datalen == [data length])

    {

      NSLog(@"Send str:%@",str);

    }

如何接收資料,read和recv都可以,這是方法,你需要自己維護一個佇列,控制時間等等。

    char readBuffer[512] = {0};

    NSString* readString = nil;

    int br = 0

    while (br = read(sockfd, readBuffer, sizeof(readBuffer)) < sizeof(readBuffer))

//  while((br = recv(sockfd, readBuffer, sizeof(readBuffer), 0)) < sizeof(readBuffer))

    {

      NSLog(@"Received CMD:%s",readBuffer);

      readString = [NSString stringWithUTF8String:readBuffer];

      memset(readBuffer,0,sizeof(readBuffer));

    }

    NSLog(@"br is %d,receive exit.",br);

獲取時間後就可以進行時間同步了,具體的時間同步協議要根據自己平臺來設計

      time_t timep;

      struct tm *p;

      time(&timep);

      p = localtime(&timep);

      int wday = -1;//return num is (0,6),the weekday range is (1,7)

      if(p->tm_wday == 0)

        wday = 7;

      else

        wday = p->tm_wday;

      char data[256] = {0};

      sprintf(data,"0E4007%02x%02x%02x%02x%02x%02x%02x",(1900+p->tm_year)%100,(1+p->tm_mon),p->tm_mday,p->tm_hour,p->tm_min,p->tm_sec,wday);

      NSString *msgtime = [NSString stringWithUTF8String:data];

可以開一個執行緒來進行收發,處理相關的操作,想要多執行緒控制需要注意這個socket必須是全域性可用的,因為新執行緒已經不在主迴圈了

還有如果有介面更新也需要在主執行緒更新

[NSThread detachNewThreadSelector:@selector(OnNewThread) toTarget:self withObject:nil];

可以用timer做一個心跳包維持通訊

timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(OnHeartBeatTimer:) userInfo:nil repeats:YES];

結束的時候記得關掉定時器和socket

[timer invalidate];

close(sockfd);