iso socket基础2
iPhone socket 编程之BSD Socket篇 收藏
在进行iPhone网络通讯程序的开发中,不可避免的要利用Socket套接字。iPhone提供了Socket网络编程的接口CFSocket,不过笔者更喜欢使用BSD Socket。
(IBAction)calculaeTip:(id)sender
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
iso socket基础2的更多相关文章
- C# Socket基础(一)之启动异步服务监听
本文主要是以代码为主..NET技术交流群 199281001 .欢迎加入. //通知一个或多个正在等待的线程已发生事件. ManualResetEvent manager = new ManualRe ...
- php Socket基础
◆ Socket 基础PHP使用Berkley的socket库来创建它的连接.socket只不过是一个数据结构.你使用这个socket数据结构去开始一个客户端和服务器之间的会话.这个服务器是一直在监听 ...
- 【转】Windows socket基础
转自:http://blog.csdn.net/ithzhang/article/details/8448655 Windows socket 基础 Windows socket是一套在Windows ...
- 从零开始的Python学习Episode 21——socket基础
socket基础 网络通信要素: A:IP地址 (1) 用来标识网络上一台独立的主机 (2) IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段.主机号:用于识别该网络中的 ...
- Python学习 :socket基础
socket基础 什么是socket? - socket为接口通道,内部封装了IP地址.端口.协议等信息:我们可以看作是以前的通过电话机拨号上网的年代,socket即为电话线 socket通信流程 我 ...
- Python socket 基础(Server) - Foundations of Python Socket
Python socket 基础 Server - Foundations of Python Socket 通过 python socket 模块建立一个提供 TCP 链接服务的 server 可分 ...
- Python socket 基础(Client) - Foundations of Python Socket
Python socket 基础- Foundations of Python Socket 建立socket - establish socket import socket s = socket. ...
- Java IO学习笔记四:Socket基础
作者:Grey 原文地址:Java IO学习笔记四:Socket基础 准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为: io1实例:192.168.205.138 io ...
- socket基础(二)
Microsoft.Net Framework为应用程序访问Internet提供了分层的.可扩展的以及受管辖的网络服务,其名字空间System.Net和System.Net.Sockets包含丰富的类 ...
随机推荐
- Object C学习笔记13-Dictionary字典
通过Array数组和Set集合的学习和理解,可以想象得到Dictionary也分为两种情况了,那就是可变和不可变两种类型的.的确如此,在Object C中提供了两个字典类,分别为NSDictionar ...
- SQL删除重复的记录(只保留一条)
首先新建表: --创建示例表 CREATE TABLE t ( id ,) PRIMARY KEY, a ), b ) ) --插入数据 INSERT INTO t SELECT 'aa','bb' ...
- Javascript写入txt和读取txt文件示例
1. 写入 FileSystemObject可以将文件翻译成文件流. 第一步: 例: 复制代码 代码如下: Var fso=new ActiveXObject(Scripting.FileSystem ...
- NABCD模型进行竞争性需求分析
确定项目:教室管理系统 负责人:李凤娇,高德建 选择比努力更重要.一个项目成功自然离不开组员们的努力.但是,光努力是不够的.还需要用户有需求,能快速实现. 这些东西,看似很虚,却能让我们少走不少弯路. ...
- Java Web中将oracle的数据库内容以表格形式展现到页面中(分页展示)
分页SQL语句: ----分页显示 select * from (select rownum as r,t.* from () ; 查询的结果如下: 这个SQL,使用了三层嵌套的查询方式: 1)最内层 ...
- Oracle中新增表代码
create table userinfo ( id varchar2(36) primary key, username varchar2(50) not null, password varcha ...
- 理解jar
对于大多数 Java 开发人员来说,JAR 文件及其 “近亲” WAR 和 EAR 都只不过是漫长的 Ant 或 Maven 流程的最终结果.标准步骤是将一个 JAR 复制到服务器(或者,少数情况下是 ...
- Fluent Ribbon项目出现“命名空间“clr-namespace:Fluent;assembly=Fluent”中不存在“RibbonWindow”名称”的解决方法
之前在学习@aganqin的Fluent Ribbon项目(http://www.cnblogs.com/aganqin/p/3269384.html).但是一直都有引用了Fluent.dll后仍旧出 ...
- Java-优化技术
常用的: 1.优化循环.通过重新组织重复的子表达式来提高循环体的运行性能. 2减少使用对象的数量来提高运行性能. 3.缩减网络传输数据来缩短等待时间. 其他: 1.采用对象池技术,提高对象的利用效率. ...
- MVC模式的学生信息增删改查
准备:建一个名为 userdb的数据库.建一个student表,有stuid,stuname,gender三个字段.其中stuid为主键.j加入相应的驱动包,相应的JSTL标签 先看目录结构 代码: ...