從基本的知識(shí)開始講解iPhone BSD Socket編程
出處:hui7744 發(fā)布于:2011-08-25 16:54:06
1 初始化一個(gè)Socket
socket的英文原義是“孔”或“插座”。作為4BDS UNIX的進(jìn)程通信機(jī)制,取后一種意思。通常也稱作“套接字”,用于描述IP地址和端口,是一個(gè)通信鏈的句柄。在Internet上的主機(jī)一般運(yùn)行了多個(gè)服務(wù)軟件,同時(shí)提供幾種服務(wù)。每種服務(wù)都打開一個(gè)Socket,并綁定到一個(gè)端口上,不同的端口對(duì)應(yīng)于不同的服務(wù)。Socket正如其英文原意那樣,象一個(gè)多孔插座。一臺(tái)主機(jī)猶如布滿各種插座的房間,每個(gè)插座有一個(gè)編號(hào),有的插座提供220伏交流電, 有的提供110伏交流電,有的則提供有線電視節(jié)目。 客戶軟件將插頭插到不同編號(hào)的插座,就可以得到不同的服務(wù)。
首先,Socket是進(jìn)行程序間通訊(IPC, Internet Process CONnection)的BSD方法,這意味著Socket是用來讓一個(gè)進(jìn)程和其他的進(jìn)程互相通訊的,就像我們用電話來和其他人交流一樣。
既然說Socket像個(gè)電話,那么如果要打電話首先就要安裝一部電話,“安裝電話”這個(gè)動(dòng)作對(duì)BSD Socket來說就是初始化一個(gè)Socket,方法如下:
int socket(int, int, int);
個(gè)int參數(shù)為Socket的地址方式,既然要“安裝電話”,那么就要首先確認(rèn)所要安裝的電話是音頻的還是脈沖的。而如果要給BSD Socket安裝電話,有兩種類型可供讀者選擇:AF_UNIX和AF_INET,它們代表Socket的地址格式。如果選擇AF_UNIX,意味著需要為Socket提供一個(gè)類似Unix路徑的名稱,這個(gè)選項(xiàng)主要用于本地程序之間的socket通訊。
第二個(gè)int參數(shù)為Socket的類型,“安裝電話”需要首先確定是裝有線的還是裝無線的,安裝Socket也一樣,在Socket中提供了兩種類型:SOCK_STREAM和SOCK_DGRAM。
第三個(gè)int參數(shù)為所使用的協(xié)議,本文里使用0即可。
“安裝電話”的代碼如下:
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror(“socket”);
exit(1);
}
2 BSD Socket扮演“客戶端”角色的操作
到現(xiàn)在為止,怎么安裝電話已經(jīng)清楚了。因?yàn)楸疚闹饕菔救绾卧趇Phone上使用BSD Socket獲取內(nèi)容,更多的功能是“打電話”而不是“接電話”,所以下面主要講解BSD Socket扮演“客戶端”角色的操作。
既然要“打電話”,那么首先要有打電話的對(duì)象,更確切的說需要一個(gè)“電話號(hào)碼”,BSD Socket中的“電話號(hào)碼”就是IP地址。更糟糕的情況是,如果只知道聯(lián)系人的名字而不知道電話號(hào)碼,那么還需要程序查找相應(yīng)聯(lián)系人的電話號(hào)碼,根據(jù)聯(lián)系人姓名查找電話號(hào)碼的過程在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是個(gè)結(jié)構(gòu)體,使用它需要#import <netdb.h>,通過這個(gè)方法得到theHost域名的個(gè)有效的IP地址并返回。
正確的“找到電話號(hào)碼”后,就需要“撥打電話”了,代碼如下:
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進(jìn)行主機(jī)域名的解析,奈何iPhone的這個(gè)類為private的,在application的開發(fā)中不可使用。
如果“電話”能順利的接通,那么就可以進(jìn)行“講話”了,反之則會(huì)斷開“電話連接
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(),這個(gè)兩個(gè)函數(shù)的原型如下:
int send(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd,void *buf,int len,unsigned int flags);
可以看出,這兩個(gè)函數(shù)的參數(shù)基本相同。
個(gè)參數(shù)為套接字的句柄。
第二個(gè)參數(shù)為數(shù)據(jù)緩沖區(qū)。
第三個(gè)參數(shù)為數(shù)據(jù)長(zhǎng)度。
一個(gè)參數(shù)有點(diǎn)特殊,這個(gè)參數(shù)是為了讓BSD Socket能支持“帶外數(shù)據(jù)”,何謂“帶外數(shù)據(jù)”?顧名思義,就是“帶內(nèi)以外的數(shù)據(jù)”,而帶內(nèi)數(shù)據(jù)就是常規(guī)的按照Socket字節(jié)流順序進(jìn)行傳遞的數(shù)據(jù)。通常情況下,數(shù)據(jù)由連接的一端流到接收的一端,并且認(rèn)為數(shù)據(jù)的所有字節(jié)都是排序的,晚寫入的字節(jié)絕不會(huì)早于先寫入的字節(jié)到達(dá)。請(qǐng)求帶外數(shù)據(jù)傳送需要把標(biāo)識(shí)位置為MSG_OOB,如下:
char buf[64];
int len;
int s;
…
send(s,buf,len,MSG_OOB);
3 調(diào)用close(sockfd)“掛斷電話”
至此,一個(gè)完整的“通話過程”已經(jīng)結(jié)束,別忘記調(diào)用close(sockfd)“掛斷電話”。
下面筆者嘗試請(qǐng)求www.baidu.com的,并把請(qǐng)求的頁面內(nèi)容打印到控制臺(tái),所以需要對(duì)請(qǐng)求進(jìn)行封裝,以支持HTTP協(xié)議。很簡(jiǎn)單,只需要在請(qǐng)求的內(nèi)容前面加上相應(yīng)的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頭信息,對(duì)HTTP不熟悉的同學(xué)可以先熟悉熟悉HTTP的格式,請(qǐng)求到的內(nèi)容打印如下:
[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!
4 頭文件
為了造福大家,筆者附上完整的代碼,頭文件如下:
//
// 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
實(shí)現(xiàn)文件如下:
//
// 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
版權(quán)與免責(zé)聲明
凡本網(wǎng)注明“出處:維庫(kù)電子市場(chǎng)網(wǎng)”的所有作品,版權(quán)均屬于維庫(kù)電子市場(chǎng)網(wǎng),轉(zhuǎn)載請(qǐng)必須注明維庫(kù)電子市場(chǎng)網(wǎng),http://hbjingang.com,違反者本網(wǎng)將追究相關(guān)法律責(zé)任。
本網(wǎng)轉(zhuǎn)載并注明自其它出處的作品,目的在于傳遞更多信息,并不代表本網(wǎng)贊同其觀點(diǎn)或證實(shí)其內(nèi)容的真實(shí)性,不承擔(dān)此類作品侵權(quán)行為的直接責(zé)任及連帶責(zé)任。其他媒體、網(wǎng)站或個(gè)人從本網(wǎng)轉(zhuǎn)載時(shí),必須保留本網(wǎng)注明的作品出處,并自負(fù)版權(quán)等法律責(zé)任。
如涉及作品內(nèi)容、版權(quán)等問題,請(qǐng)?jiān)谧髌钒l(fā)表之日起一周內(nèi)與本網(wǎng)聯(lián)系,否則視為放棄相關(guān)權(quán)利。
- 數(shù)字電源控制與傳統(tǒng)模擬控制的深度對(duì)比2026/2/2 11:06:56
- 模擬信號(hào)調(diào)理電路技術(shù)設(shè)計(jì)與選型運(yùn)維指南2025/12/30 10:08:16
- 運(yùn)算放大器壓擺率的核心要點(diǎn)2025/9/5 16:27:55
- 深度剖析放大器穩(wěn)定系數(shù) K 與 Mu 的差異2025/9/2 16:44:05
- 什么是運(yùn)算放大器失調(diào)電流2025/9/1 17:01:22
- 高速PCB阻抗控制核心實(shí)操規(guī)范
- 高速數(shù)字系統(tǒng)(如DDR、SerDes)中的信號(hào)完整性濾波
- MOSFET在UPS電源中的應(yīng)用解析
- 電源管理IC在物聯(lián)網(wǎng)設(shè)備中的應(yīng)用
- SMT連接器焊接缺陷分析
- MOSFET在汽車電子中的應(yīng)用要求
- 通信設(shè)備電源管理IC應(yīng)用解析
- 通信設(shè)備連接器選型與設(shè)計(jì)
- PCB電磁兼容性(EMC)設(shè)計(jì)核心實(shí)操規(guī)范
- 物聯(lián)網(wǎng)節(jié)點(diǎn)低功耗設(shè)計(jì):信號(hào)鏈中的濾波與功耗管理









