玩转iPhone网络通讯之BSD Socket篇

发布时间:2016-12-9 21:28:03 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"玩转iPhone网络通讯之BSD Socket篇",主要涉及到玩转iPhone网络通讯之BSD Socket篇方面的内容,对于玩转iPhone网络通讯之BSD Socket篇感兴趣的同学可以参考一下。

转自http://blog.csdn.net/dongfengsun/article/details/4802925 玩转iPhone网络通讯之BSD Socket篇(手把手教你iphone开发 - 基础篇)   作者:孙东风 2009-11-11(请尊重作者劳动成果,转载务必注明出处)   在进行iPhone网络通讯程序的开发中,不可避免的要利用Socket套接字。iPhone提供了Socket网络编程的接口CFSocket,不过笔者更喜欢使用BSD Socket。   iPhone BSD Socket进行编程所需要的头文件基本都位于/Xcode3.1.4/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.sdk/usr/include/sys下,既然本篇文章作为基础篇,那么笔者就从最基本的知识讲解开始。   首先,Socket是进行程序间通讯(IPC, Internet Process Connection)的BSD方法,这意味着Socket是用来让一个进程和其他的进程互相通讯的,就像我们用电话来和其他人交流一样。   既然说Socket像个电话,那么如果要打电话首先就要安装一部电话,“安装电话”这个动作对BSD Socket来说就是初始化一个Socket,方法如下:   int     socket(int, int, int);   第一个int参数为Socket的地址方式,既然要“安装电话”,那么就要首先确认所要安装的电话是音频的还是脉冲的。而如果要给BSD Socket安装电话,有两种类型可供读者选择:AF_UNIX和AF_INET,它们代表Socket的地址格式。如果选择AF_UNIX,意味着需要为Socket提供一个类似Unix路径的名称,这个选项主要用于本地程序之间的socket通讯;本文主要讲解网络通讯,所以需要选择参数AF_INET。   第二个int参数为Socket的类型,“安装电话”需要首先确定是装有线的还是装无线的,安装Socket也一样,在Socket中提供了两种类型:SOCK_STREAM和SOCK_DGRAM。SOCK_STREAM表明数据像字符流一样通过Socket;而SOCK_DGRAM则表明数据以数据报(Datagrams)的形式通过Socket,本文主要讲解SOCK_STREAM,因为它的使用更为广泛。   第三个int参数为所使用的协议,本文里使用0即可。   “安装电话”的代码如下:       if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)     {         perror("socket");         exit(1);     }   到现在为止,怎么安装电话已经清楚了。因为本文主要演示如何在iPhone上使用BSD Socket获取内容,更多的功能是“打电话”而不是“接电话”,所以下面主要讲解BSD Socket扮演“客户端”角色的操作。   既然要“打电话”,那么首先要有打电话的对象,更确切的说需要一个“电话号码”,BSD Socket中的“电话号码”就是IP地址。更糟糕的情况是,如果只知道联系人的名字而不知道电话号码,那么还需要程序查找相应联系人的电话号码,根据联系人姓名查找电话号码的过程在BSD Socket中叫做DNS解析,代码如下:   - (NSString*)getIpAddressForHost:(NSString*) theHost {     struct hostent *host = gethostbyname([theHost UTF8String]);         if(!host)     {         herror("resolv");         return NULL;     }         struct in_addr **list = (struct in_addr **)host->h_addr_list;     NSString *addressString = [NSString stringWithCString:inet_ntoa(*list[0])];     return addressString; }   hostent是个结构体,使用它需要#import <netdb.h>,通过这个方法得到theHost域名的第一个有效的IP地址并返回。   正确的“找到电话号码”后,就需要“拨打电话”了,代码如下:           their_addr.sin_family = AF_INET;         their_addr.sin_addr.s_addr = inet_addr([[self getIpAddressForHost:hostName] UTF8String]);         NSLog(@"getIpAddressForHost :%@",[self getIpAddressForHost:hostName]);                 their_addr.sin_port = htons(80);         bzero(&(their_addr.sin_zero), 8);                 int conn = connect(sockfd, (struct sockaddr*)&their_addr, sizeof(struct sockaddr));                 NSLog(@"Connect errno is :%d",conn);   笔者最初试图采用NHost进行主机域名的解析,奈何iPhone的这个类为private的,在application的开发中不可使用。   如果“电话”能顺利的接通,那么就可以进行“讲话”了,反之则会断开“电话连接”,如果友好的话,最好能给个提示,诸如“您所拨打的电话不在服务区之类”:)           if(conn != -1)         {             NSLog(@"Then the conn is not -1!");                         NSMutableString* httpContent = [self makeHttpHeader:hostName];                         NSLog(@"httpCotent is :%@",httpContent);                         if(contentSended != nil)                 [httpContent appendFormat:contentSended];                         NSLog(@"Sended content is :%@",httpContent);                         NSData *data = [httpContent dataUsingEncoding:NSISOLatin1StringEncoding];             ssize_t dataSended = send(sockfd, [data bytes], [data length], 0);                         if(dataSended == [data length])             {                 NSLog(@"Datas have been sended over!");             }                         printf("send %d bytes to %s/n",dataSended,inet_ntoa(their_addr.sin_addr));               NSMutableString* readString = [[NSMutableString alloc] init];             char readBuffer[512];                         int br = 0;             while((br = recv(sockfd, readBuffer, sizeof(readBuffer), 0)) < sizeof(readBuffer))             {                 NSLog(@"read datas length is :%d",br);                                 [readString appendFormat:[NSString stringWithCString:readBuffer length:br]];                                 NSLog(@"Hava received datas is :%@",readString);             }                         close(sockfd);         }else {             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[@"Connection failed to host " stringByAppendingString:hostName] message:@"Please check the hostname in the preferences." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];             [alert show];             [alert release];         }   “讲话”通过send(),“听话”通过recv(),这个两个函数的原型如下:   int send(int sockfd, const void *msg, int len, int flags); int recv(int sockfd,void *buf,int len,unsigned int flags);   可以看出,这两个函数的参数基本相同。 第一个参数为套接字的句柄。 第二个参数为数据缓冲区。 第三个参数为数据长度。   最后一个参数有点特殊,这个参数是为了让BSD Socket能支持“带外数据”,何谓“带外数据”?顾名思义,就是“带内以外的数据”,而带内数据就是常规的按照Socket字节流顺序进行传递的数据。通常情况下,数据由连接的一端流到接收的一端,并且认为数据的所有字节都是精确排序的,晚写入的字节绝不会早于先写入的字节到达。但是如果我们“挂断了电话”,而接收方还有大量已经被接收的缓冲数据,这些数据还没被程序读取,那么接收方需要在读取这些缓冲的“带内数据”之前先读取一个标识取消的请求,这个请求就可以利用带外请求的方法进行传送。请求带外数据传送需要把标识位置为MSG_OOB,如下:   char buf[64]; int len;     int s;        … send(s,buf,len,MSG_OOB);   至此,一个完整的“通话过程”已经结束,最后别忘记调用close(sockfd)“挂断电话”。   下面笔者尝试请求www.baidu.com的首页,并把请求的页面内容打印到控制台,所以需要对请求进行封装,以支持HTTP协议。很简单,只需要在请求的内容前面加上相应的HTTP头信息即可,如下:   #define HTTPMETHOD @"GET" #define HTTPVERSION @"HTTP/1.1" #define HTTPHOST @"Host"   #define KENTER @"/r/n" #define KBLANK @" "   - (NSMutableString*) makeHttpHeader:(NSString*) hostName {     NSMutableString *header = [[NSMutableString alloc] init];         [header appendFormat:HTTPMETHOD];     [header appendFormat:KBLANK];     [header appendFormat:@"/index.html"];     [header appendFormat:KBLANK];     [header appendFormat:HTTPVERSION];     [header appendFormat:KENTER];         [header appendFormat:HTTPHOST];     [header appendFormat:@":"];     [header appendFormat:hostName];     [header appendFormat:KENTER];     [header appendFormat:KENTER];         return header; }   在上面的方法中,笔者封装了HTTP头信息,对HTTP不熟悉的同学可以先熟悉熟悉HTTP的格式,请求到的内容打印如下:   [Session started at 2009-11-12 15:40:02 +0800.] 2009-11-12 15:40:04.691 BSDHttpExample[3483:207] getIpAddressForHost :119.75.216.30 2009-11-12 15:40:04.725 BSDHttpExample[3483:207] Connect errno is :0 2009-11-12 15:40:04.727 BSDHttpExample[3483:207] Then the conn is not -1! 2009-11-12 15:40:04.735 BSDHttpExample[3483:207] httpCotent is :GET /index.html HTTP/1.1 Host:www.baidu.com 2009-11-12 15:40:04.736 BSDHttpExample[3483:207] Sended content is :GET /index.html HTTP/1.1 Host:www.baidu.com 2009-11-12 15:40:04.736 BSDHttpExample[3483:207] Datas have been sended over! send 48 bytes to 119.75.216.30 2009-11-12 15:40:04.764 BSDHttpExample[3483:207] read datas length is :363 2009-11-12 15:40:04.765 BSDHttpExample[3483:207] Hava received datas is :HTTP/1.1 200 OK Date: Thu, 12 Nov 2009 07:40:05 GMT Server: BWS/1.0 Content-Length: 3520 Content-Type: text/html;charset=gb2312 Cache-Control: private Expires: Thu, 12 Nov 2009 07:40:05 GMT Set-Cookie: BAIDUID=9B024266ADD3B52AC8367A2BDD1676E5:FG=1; expires=Thu, 12-Nov-39 07:40:05 GMT; path=/; domain=.baidu.com P3P: CP=" OTI DSP COR IVA OUR IND COM " 2009-11-12 15:40:04.766 BSDHttpExample[3483:207] view has been loaded!   最后为了造福大家,笔者附上完整的代码,头文件如下:   // //  BSDHttpExampleViewController.h //  BSDHttpExample // //  Created by sun dfsun2009 on 09-11-12. //  Copyright __MyCompanyName__ 2009. All rights reserved. //   #import <UIKit/UIKit.h>   #define MYPORT 4880 #import <stdio.h> #import <stdlib.h> #import <unistd.h> #import <arpa/inet.h> #import <sys/types.h> #import <sys/socket.h> #import <netdb.h>   @interface BSDHttpExampleViewController : UIViewController {     int sockfd;     struct sockaddr_in their_addr; }   @end   实现文件如下:   // //  BSDHttpExampleViewController.m //  BSDHttpExample // //  Created by sun dfsun2009 on 09-11-12. //  Copyright __MyCompanyName__ 2009. All rights reserved. //   #import "BSDHttpExampleViewController.h"   @implementation BSDHttpExampleViewController   #define HTTPMETHOD @"GET" #define HTTPVERSION @"HTTP/1.1" #define HTTPHOST @"Host"   #define KENTER @"/r/n" #define KBLANK @" "   /*  // The designated initializer. Override to perform setup that is required before the view is loaded.  - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {  if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {  // Custom initialization  }  return self;  }  */   /*  // Implement loadView to create a view hierarchy programmatically, without using a nib.  - (void)loadView {  }  */   void error_handle(char *errorMsg) {     fputs(errorMsg, stderr);     fputc('/n',stderr);     exit(1); }   - (NSMutableString*) makeHttpHeader:(NSString*) hostName {     NSMutableString *header = [[NSMutableString alloc] init];         [header appendFormat:HTTPMETHOD];     [header appendFormat:KBLANK];     [header appendFormat:@"/index.html"];     [header appendFormat:KBLANK];     [header appendFormat:HTTPVERSION];     [header appendFormat:KENTER];         [header appendFormat:HTTPHOST];     [header appendFormat:@":"];     [header appendFormat:hostName];     [header appendFormat:KENTER];     [header appendFormat:KENTER];         return header; }   - (NSString*)getIpAddressForHost:(NSString*) theHost {     struct hostent *host = gethostbyname([theHost UTF8String]);         if(!host)     {         herror("resolv");         return NULL;     }         struct in_addr **list = (struct in_addr **)host->h_addr_list;     NSString *addressString = [NSString stringWithCString:inet_ntoa(*list[0])];     return addressString; }   - (void)Connect:(NSString *)hostName content:(NSString *)contentSended {       if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)     {         perror("socket");         exit(1);     }         //NSHost *host = [NSHost hostWithName:hostName];         //if(host)     //{         their_addr.sin_family = AF_INET;                 //their_addr.sin_addr.s_addr = inet_addr([[host address] UTF8String]);         their_addr.sin_addr.s_addr = inet_addr([[self getIpAddressForHost:hostName] UTF8String]);         NSLog(@"getIpAddressForHost :%@",[self getIpAddressForHost:hostName]);                 their_addr.sin_port = htons(80);         bzero(&(their_addr.sin_zero), 8);                 int conn = connect(sockfd, (struct sockaddr*)&their_addr, sizeof(struct sockaddr));                 NSLog(@"Connect errno is :%d",conn);         if(conn != -1)         {             NSLog(@"Then the conn is not -1!");                         NSMutableString* httpContent = [self makeHttpHeader:hostName];                         NSLog(@"httpCotent is :%@",httpContent);                         if(contentSended != nil)                 [httpContent appendFormat:contentSended];                         NSLog(@"Sended content is :%@",httpContent);                         NSData *data = [httpContent dataUsingEncoding:NSISOLatin1StringEncoding];             ssize_t dataSended = send(sockfd, [data bytes], [data length], 0);                         if(dataSended == [data length])             {                 NSLog(@"Datas have been sended over!");             }                         printf("send %d bytes to %s/n",dataSended,inet_ntoa(their_addr.sin_addr));               NSMutableString* readString = [[NSMutableString alloc] init];             char readBuffer[512];                         int br = 0;             while((br = recv(sockfd, readBuffer, sizeof(readBuffer), 0)) < sizeof(readBuffer))             {                 NSLog(@"read datas length is :%d",br);                                 [readString appendFormat:[NSString stringWithCString:readBuffer length:br]];                                 NSLog(@"Hava received datas is :%@",readString);             }                         close(sockfd);         }else {             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[@"Connection failed to host " stringByAppendingString:hostName] message:@"Please check the hostname in the preferences." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];             [alert show];             [alert release];         }             /*     }     else     {         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[@"Could not look up host " stringByAppendingString:hostName] message:@"Please check the hostname in the preferences." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];         [alert show];         [alert release];     }      **/ }   - (void)Send:(id)sender {     char message[7] = "aaag";     send(sockfd,message,sizeof(message),0);     NSLog(@"%s",message); }   // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad {     [self Connect:@"www.baidu.com" content:nil];         [super viewDidLoad];         NSLog(@"view has been loaded!"); }     /*  // Override to allow orientations other than the default portrait orientation.  - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {  // Return YES for supported orientations  return (interfaceOrientation == UIInterfaceOrientationPortrait);  }  */   - (void)didReceiveMemoryWarning {     // Releases the view if it doesn't have a superview.     [super didReceiveMemoryWarning];         // Release any cached data, images, etc that aren't in use. }   - (void)viewDidUnload {     // Release any retained subviews of the main view.     // e.g. self.myOutlet = nil; }     - (void)dealloc {     [super dealloc]; }   @end   当然,完整的项目文件我传到自己建的QQ群里了,如果您想下载也可以加进来一起交流,QQ群号码为:84204840

上一篇:Android USB下的Accessory模式
下一篇:MFC计算器总结

相关文章

相关评论