iPhone socket 编程之BSD Socket篇 收藏
在进行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

iPhone socket 编程之BSD Socket篇的更多相关文章

  1. PHP Socket 编程之9个主要函数的使用之测试案例

    php的socket编程算是比较难以理解的东西吧,不过,我们只要理解socket几个函数之间的关系,以及它们所扮演的角色,那么理解起来应该不是很难了,在笔者看来,socket编程,其实就是建立一个网络 ...

  2. [深入浅出WP8.1(Runtime)]Socket编程之UDP协议

    13.3 Socket编程之UDP协议 UDP协议和TCP协议都是Socket编程的协议,但是与TCP协议不同,UDP协议并不提供超时重传,出错重传等功能,也就是说其是不可靠的协议.UDP适用于一次只 ...

  3. 老雷socket编程之websocket实现

    老雷socket编程之websocket实现 我们主要实现私聊和群聊两个功能,要在web端实现想微信QQ那样的即时通讯的功能,我们需要了解一下websocket.websocket是一种可以双向通讯的 ...

  4. 老雷socket编程之PHP利用socket扩展实现聊天服务

    老雷socket编程之PHP利用socket扩展实现聊天服务 socket聊天服务原理 PHP有两个socket的扩展 sockets和streamssockets socket_create(AF_ ...

  5. Java并发编程之CAS第一篇-什么是CAS

    Java并发编程之CAS第一篇-什么是CAS 通过前面几篇的学习,我们对并发编程两个高频知识点了解了其中的一个—volatitl.从这一篇文章开始,我们将要学习另一个知识点—CAS.本篇是<凯哥 ...

  6. linux socket编程之TCP与UDP

    转:http://blog.csdn.net/gaoxin1076/article/details/7262482 TCP/IP协议叫做传输控制/网际协议,又叫网络通信协议 TCP/IP虽然叫传输控制 ...

  7. Socket 编程之 TCP 实现

    前几天介绍了计算机网络的一些概念,并介绍了几个协议.下面就说说 Java 中的 Socket 编程,服务器和客户端是如何通信的呢? 首先要介绍一下 Socket ,我们知道在 TCP/IP 协议簇中, ...

  8. Python socket编程之二:【struct.pack】&【struct.unpack】

    import struct """通过 socket 的 send 和 recv 只能传输 str 格式的数据""" "" ...

  9. socket编程之TCP/UDP

    目标: 1.编写TCP服务端客户端,实现客户端发送数据,服务端接收打印 2.采用OOP方式编写TCP服务端客户端,实现客户端发送数据,服务端添加时间戳,返回给客户端 3.采用OOP方式编写UDP服务端 ...

随机推荐

  1. WCF Data Service 使用小结 (一)—— 了解OData协议

    最近做了一个小项目,其中用到了 WCF Data Service,之前是叫 ADO.NET Data Service 的.关于WCF Data Service,博客园里的介绍并不多,但它确实是个很好的 ...

  2. 如何使用Iveely的数据存储引擎 Iveely Database

    Iveely 数据存储引擎是为Iveely 搜索引擎提供数据存储的机制. 适用于:频繁数据插入.数据读取.数据更改或者删除数据不适合Iveely Database,存储结构是按照搜索引擎数据存储要求( ...

  3. SequoiaDB 系列之四 :架构简析

    在本系列的第一篇中,简述了SequoiaDB的安装,以及一个(伪)集群的部署 第二篇和第三篇对SequoiaDB的集群,做了简单地操作. 在本篇中,将对SequoiaDB的架构进行简单的分析. 因为自 ...

  4. Javascript基础系列之(五)条件语句(if条件语句)

    if 是flash的常用语法之一,其格式如下 if(coditon) statement1 (else statement2) 其中,coditon可以是任何表达式,甚至不比是真正的布尔值,因为Jav ...

  5. Spring学习进阶(一)初识Spring

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  6. 从零开始设计SOA框架(二):请求/响应参数的设计

    每个接口都有请求参数.响应参数.其中请求参数分为公共参数和业务参数.响应参数分为两类:正常的响应参数.统一的错误参数   一.请求参数 1.公共参数:每个接口都有的参数,主要包含appkey.时间戳. ...

  7. 基于spring mvc的注解DEMO完整例子

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件.本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mv ...

  8. python逐行读写

    代码: fileReadObj = open("input.txt") fileWriteObj = open("output.txt", 'w') fileL ...

  9. python 类型之 set

    python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和 ...

  10. bzoj 1491 floyd

    我们用w[i][j]表示i到j的最短路的数量,dis[i][j]表示i到j的最短路,那么我们在floyd的时候,如果dis[i][k]+dis[k][j]==dis[i][j],根据乘法原理我们就w[ ...