iPhone開發(fā)中BSD Socket輕松學習教程
iPhone開發(fā)中BSD Socket輕松學習是本文要介紹的內(nèi)容,主要是來學習iphone開發(fā)中線程的學習。在進行iphone開發(fā)通訊程序中,不可避免的要利用socket通訊,除了iPhone開發(fā)中自帶的CFSocket類接口之外,我們還可以基于BSD Socket來進行通訊程式的開發(fā),而這篇文章簡單介紹了BSD Socket的知識,筆者認為是一篇不錯的入門文章,故轉到blog上進行共享:
當你進入 UNIX 的神秘世界后,立刻會發(fā)現(xiàn)越來越多的東西難以理解。對于大多數(shù)人來說,BSD (Berkeley Software Distribution,伯克利軟件套件是Unix的衍生系統(tǒng),在1977至1995年間由加州大學伯克利分校開發(fā)和發(fā)布的) socket 的概念就是其中一個。這是一個很短的教程來解釋他們是什么、他們?nèi)绾喂ぷ鞑⒔o出一些簡單的代碼來解釋如何使用他們。
類比 (什么是 socket ?)
socket 是進行程序間通訊(IPC,Internet Process Connection)的 BSD 方法。這意味著 socket 用來讓一個進程和其他的進程互通信息,就象我們用電話來和其他的人交流一樣。
用電話來比喻是很恰當?shù)模覀冊诤竺鎸⒁恢庇秒娫掃@個概念來描敘 socket 。
裝上你的新電話(怎樣偵聽?)
一個人要能夠收到別人打給他的電話,首先他要裝上一門電話。同樣,你必須先建立 socket 以偵聽線路。這個過程包含幾個步驟。首先,你要建立一個新的 socket,就象先裝上電話一樣。socket() 命令就完成這個工作。
因為 sockets 有幾種類型,你要注明你要建立什么類型的。你要做一個選擇是 socket 的地址格式。如同電話有音頻和脈沖兩種形式一樣,socket 有兩個最重要的選項是 AF_UNIX 和 IAF_INET。AF_UNIX 就象 UNIX 路徑名一樣識別 sockets。這種形式對于在同一臺機器上的 IPC 很有用。而 AF_INET 使用象 192.9.200.10 這樣被點號隔開的四個十進制數(shù)字的地址格式。除了機器地址以外,還可以利用端口號來允許每臺機器上的多個 AF_INET socket。我們這里將著重于 AF_INET 方式,因為他很有用并廣泛使用。
另外一個你必須提供的參數(shù)是 socket 的類型。兩個重要的類型是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM 表明數(shù)據(jù)象字符流一樣通過 socket 。而 SOCK_DGRAM 則表明數(shù)據(jù)將是數(shù)據(jù)報(datagrams)的形式。我們將講解 SOCK_STREAM sockets,他很常見并易于使用。
在建立 socket 后,我們就要提供 socket 偵聽的地址了。就象你還要個電話號碼來接電話一樣。bind() 函數(shù)來處理這件事情。
SOCK_STREAM sockets 讓連接請求形成一個隊列。如果你忙于處理一個連接,別的連接請求將一直等待到該連接處理完畢。listen() 函數(shù)用來設置最大不被拒絕的請求數(shù)(一般為5個)。一般最好不要使用 listen() 函數(shù)。
下面的代碼說明如何利用 socket()、 bind() 和 listen() 函數(shù)建立連接并可以接受數(shù)據(jù)。
- int establish(unsigned short portnum)
- { char myname[MAXHOSTNAME+1];
- int s;
- struct sockaddr_in sa;
- struct hostent *hp;
- memset(&sa, 0, sizeof(struct sockaddr_in));
- gethostname(myname, MAXHOSTNAME);
- hp= gethostbyname(myname);
- if (hp == NULL)
- return(-1);
- sa.sin_family= hp->h_addrtype;
- sa.sin_port= htons(portnum);
- if ((s= socket(AF_INET, SOCK_STREAM, 0)) < 0)
- return(-1);
- if (bind(s,&sa,sizeof(struct sockaddr_in)) < 0) {
- close(s);
- return(-1);
- }
- listen(s, 3);
- return(s);
- }
在建立完 socket 后,你要等待對該 socket 的調(diào)用了。accept() 函數(shù)為此目的而來。調(diào)用 accept() 如同在電話鈴響后提起電話一樣。Accept() 返回一個新的連接到調(diào)用方的 socket 。
下面的代碼演示使用是個演示。
- int get_connection(int s)
- { int t;
- if ((t = accept(s,NULL,NULL)) < 0)
- return(-1);
- return(t);
- }
和電話不同的是,在你處理先前的連接的時候,你還可以接受調(diào)用。為此,一般用 fork 來處理每個連接。下面的代碼演示如何使用 establish() 和 get_connection() 來處理多個連接。
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define PORTNUM 50000
- void fireman(void);
- void do_something(int);
- main()
- { int s, t;
- if ((s= establish(PORTNUM)) < 0) {
- perror("establish");
- exit(1);
- }
- signal(SIGCHLD, fireman);
- for (;;) {
- if ((t= get_connection(s)) < 0) {
- if (errno == EINTR)
- continue;
- perror("accept");
- exit(1);
- }
- switch(fork()) {
- case -1 :
- perror("fork");
- close(s);
- close(t);
- exit(1);
- case 0 :
- close(s);
- do_something(t);
- exit(0);
- default :
- close(t);
- continue;
- }
- }
- }
- void fireman(void)
- {
- while (waitpid(-1, NULL, WNOHANG) > 0)
- ;
- }
- void do_something(int s)
- {
- }
撥號 (如何調(diào)用 socket)
現(xiàn)在你應該知道如何建立 socket 來接受調(diào)用了。那么如何調(diào)用呢?和電話一樣,你要先有個電話。用 socket() 函數(shù)來完成這件事情,就象建立偵聽的 socket 一樣。
在給 socket 地址后,你可以用 connect() 函數(shù)來連接偵聽的 socket 了。下面是一段代碼。
- int call_socket(char *hostname, unsigned short portnum)
- { struct sockaddr_in sa;
- struct hostent *hp;
- int a, s;
- if ((hp= gethostbyname(hostname)) == NULL) {
- errno= ECONNREFUSED;
- return(-1);
- }
- memset(&sa,0,sizeof(sa));
- memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length);
- sa.sin_family= hp->h_addrtype;
- sa.sin_port= htons((u_short)portnum);
- if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0)
- return(-1);
- if (connect(s,&sa,sizeof sa) < 0) {
- close(s);
- return(-1);
- }
- return(s);
- }
這個函數(shù)返回一個可以流過數(shù)據(jù)的 socket 。
談話(如何通過 sockets 交談)
好了,你在要傳輸數(shù)據(jù)的雙方建立連接了,現(xiàn)在該傳輸數(shù)據(jù)了。read() 和 write() 函數(shù)來處理吧。除了在 socket 讀寫和文件讀寫中的一個區(qū)別外,和處理一般的文件一樣。區(qū)別是你一般不能得到你所要的數(shù)目的數(shù)據(jù)。所以你要一直循環(huán)到你需要的數(shù)據(jù)的到來。一個簡單的例子:將一定的數(shù)據(jù)讀到緩存。
- int read_data(int s,
- char *buf,
- int n
- )
- { int bcount;
- int br;
- bcount= 0;
- br= 0;
- while (bcount < n) {
- if ((br= read(s,buf,n-bcount)) > 0) {
- bcount += br;
- buf += br;
- }
- else if (br < 0)
- return(-1);
- }
- return(bcount);
- }
相同的函數(shù)也可以寫數(shù)據(jù),留給我們的讀者吧。
掛起(結束)
和你通過電話和某人交談后一樣,你要在 socket 間關閉連接。一般 close() 函數(shù)用來關閉每邊的 socket 連接。如果一邊的已經(jīng)關閉,而另外一邊卻在向他寫數(shù)據(jù),則返回一個錯誤代碼。
世界語(交流的語言很重要)
現(xiàn)在你可以在機器間聯(lián)絡了,可是要小心你所說的話。許多機器有自己的方言,如 ASCII 和 EBCDIC。更常見的問題是字節(jié)順序問題。除非你一直傳輸?shù)亩际俏谋?,否則你一定要注意這個問題。幸運的是,人們找出了解決的辦法。
在很久以前,人們爭論哪種順序更“正確”?,F(xiàn)在必要時有相應的函數(shù)來轉換。其中有 htons()、ntohs()、htonl() 和 ntohl()。在傳輸一個整型數(shù)據(jù)前,先轉換一下。
- i= htonl(i);
- write_data(s, &i, sizeof(i));
在讀數(shù)據(jù)后,再變回來。
- read_data(s, &i, sizeof(i));
- i= ntohl(i);
如果你一直堅持這個習慣,你將比別人少出錯的機會。
未來在你的掌握了(下一步?)
就用我們剛才討論的東西,你就可以寫自己的通訊程序了。和對待所有的新生事物一樣, 最好還是看看別人已經(jīng)做了些什么。這里有許多關于 BSD socket 的東西可以參考。
請注意,例子中沒有錯誤檢查,這在“真實”的程序中是很重要的。你應該對此充分重視。
小結:關于iPhone開發(fā)中BSD Socket輕松學習教程的內(nèi)容介紹完了,希望通過本文的學習能對你有所幫助!