【linux 編程】BSD Socket 簡易入門手冊

來源:互聯網
上載者:User

BSD Socket 簡易入門手冊

目錄
  • 介紹
  • 類比 (什麼是 socket ?)
  • 裝上你的新電話(怎樣偵聽?)
  • 撥號 (如何調用 socket)
  • 談話(如何通過 sockets 交談)
  • 掛起(結束)
  • 世界語(交流的語言很重要)
  • 未來在你的掌握了(下一步?)

 

介紹

當你進入 UNIX 的神秘世界後,立刻會發現越來越多的東西難以


理解。對於大多數人來說,BSD socket 的概念就是其中一個。這是一個很短的教程來解釋他們是什麼、他們如何工作並給出一些簡單的代碼來解釋如何使用他們。

類比 (什麼是 socket ?)

socket 是進行程式間通訊(IPC)的 BSD 方法。這意味著 socket 用來讓一個進程和其他的進程互連資訊,就象我們用電話來和其他的人交流一樣。

用電話來比喻是很恰當的,我們在後面將一直用電話這個概念來描敘 socket 。

裝上你的新電話(怎樣偵聽?)

一個人要能夠收到別人打給他的電話,首先他要裝上一門電話。同樣,你必須先建立 socket 以偵聽線路。這個過程包含幾個步驟。首先,你要建立一個新的 socket,就象先裝上電話一樣。socket() 命令就完成這個工作。

因為 sockets 有幾種類型,你要註明你要建立什麼類型的。你要做一個選擇是 socket 的地址格式。如同電話有音頻和脈衝兩種形式一樣,socket 有兩個最重要的選項是 AF_UNIXIAF_INETAF_UNIX 就象 UNIX 路徑名一樣識別 sockets。這種形式對於在同一台機器上的 IPC 很有用。而 AF_INET 使用象 192.9.200.10 這樣被點號隔開的四個十進位數位地址格式。除了機器地址以外,還可以利用連接埠號碼來允許每台機器上的多個 AF_INET socket。我們這裡將著重於 AF_INET 方式,因為他很有用並廣泛使用。

另外一個你必須提供的參數是 socket 的類型。兩個重要的類型是 SOCK_STREAMSOCK_DGRAMSOCK_STREAM 表明資料象字元流一樣通過 socket 。而 SOCK_DGRAM 則表明資料將是資料報(datagrams)的形式。我們將講解 SOCK_STREAM sockets,他很常見並便於使用。

在建立 socket 後,我們就要提供 socket 偵聽的地址了。就象你還要個電話號碼來接電話一樣。bind() 函數來處理這件事情。

SOCK_STREAM sockets 讓串連請求形成一個隊列。如果你忙於處理一個串連,別的串連請求將一直等待到該串連處理完畢。listen() 函數用來設定最大不被拒絕的請求數(一般為5個)。一般最好不要使用 listen() 函數。

下面的代碼說明如何利用 socket()bind()listen() 函數建立串連並可以接受資料。

 

/* code to establish a socket; originally from bzs@bu-cs.bu.edu */ 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)); /* clear our address */ gethostname(myname, MAXHOSTNAME); /* who are we? */ hp= gethostbyname(myname); /* get our address info */ if (hp == NULL) /* we don't exist !? */ return(-1); sa.sin_family= hp->h_addrtype; /* this is our host address */ sa.sin_port= htons(portnum); /* this is our port number */ if ((s= socket(AF_INET, SOCK_STREAM, 0)) < 0) /* create socket */ return(-1); if (bind(s,&sa,sizeof(struct sockaddr_in)) < 0) { close(s); return(-1); /* bind address to socket */ } listen(s, 3); /* max # of queued connects */ return(s); }

在建立完 socket 後,你要等待對該 socket 的調用了。accept() 函數為此目的而來。調用 accept() 如同在電話鈴響後提起電話一樣。Accept() 返回一個新的串連到調用方的 socket 。

下面的代碼示範使用是個示範。

 

/* wait for a connection to occur on a socket created with establish() */ int get_connection(int s) { int t; /* socket of connection */ if ((t = accept(s,NULL,NULL)) < 0) /* accept connection if there is one */ return(-1); return(t); }

和電話不同的是,在你處理先前的串連的時候,你還可以接受調用。為此,一般用 fork 來處理每個串連。下面的代碼示範如何使用 establish()get_connection() 來處理多個串連。

 

#include <errno.h> /* obligatory includes */ #include <signal.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netinet/in.h> #include <netdb.h> #define PORTNUM 50000 /* random port number, we need something */ void fireman(void); void do_something(int); main() { int s, t; if ((s= establish(PORTNUM)) < 0) { /* plug in the phone */ perror("establish"); exit(1); } signal(SIGCHLD, fireman); /* this eliminates zombies */ for (;;) { /* loop for phone calls */ if ((t= get_connection(s)) < 0) { /* get a connection */ if (errno == EINTR) /* EINTR might happen on accept(), */ continue; /* try again */ perror("accept"); /* bad */ exit(1); } switch(fork()) { /* try to handle connection */ case -1 : /* bad news. scream and die */ perror("fork"); close(s); close(t); exit(1); case 0 : /* we're the child, do something */ close(s); do_something(t); exit(0); default : /* we're the parent so look for */ close(t); /* another connection */ continue; } } } /* as children die we should get catch their returns or else we get * zombies, A Bad Thing. fireman() catches falling children. */ void fireman(void) { while (waitpid(-1, NULL, WNOHANG) > 0) ; } /* this is the function that plays with the socket. it will be called * after getting a connection. */ void do_something(int s) { /* do your thing with the socket here : : */ }

撥號 (如何調用 socket)

現在你應該知道如何建立 socket 來接受調用了。那麼如何調用呢?和電話一樣,你要先有個電話。用 socket() 函數來完成這件事情,就象建立偵聽的 socket 一樣。

在給 socket 地址後,你可以用 connect() 函數來串連偵聽的 socket 了。下面是一段代碼。

 

int call_socket(char *hostname, unsigned short portnum) { struct sockaddr_in sa; struct hostent *hp; int a, s; if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host's */ errno= ECONNREFUSED; /* address? */ return(-1); /* no */ } memset(&sa,0,sizeof(sa)); memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */ sa.sin_family= hp->h_addrtype; sa.sin_port= htons((u_short)portnum); if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0) /* get socket */ return(-1); if (connect(s,&sa,sizeof sa) < 0) { /* connect */ close(s); return(-1); } return(s); }

這個函數返回一個可以流過資料的 socket 。

談話(如何通過 sockets 交談)

好了,你在要傳輸資料的雙方建立串連了,現在該傳輸資料了。read()write() 函數來處理吧。除了在 socket 讀寫和檔案讀寫中的一個區別外,和處理一般的檔案一樣。區別是你一般不能得到你所要的數目的資料。所以你要一直迴圈到你需要的資料的到來。一個簡單的例子:將一定的資料讀到緩衝。

 

int read_data(int s, /* connected socket */ char *buf, /* pointer to the buffer */ int n /* number of characters (bytes) we want */ ) { int bcount; /* counts bytes read */ int br; /* bytes read this pass */ bcount= 0; br= 0; while (bcount < n) { /* loop until full buffer */ if ((br= read(s,buf,n-bcount)) > 0) { bcount += br; /* increment byte counter */ buf += br; /* move buffer ptr for next read */ } else if (br < 0) /* signal an error to the caller */ return(-1); } return(bcount); }

相同的函數也可以寫資料,留給我們的讀者吧。

掛起(結束)

和你通過電話和某人交談後一樣,你要在 socket 間關閉串連。一般 close() 函數用來關閉每邊的 socket 串連。如果一邊的已經關閉,而另外一邊卻在向他寫資料,則返回一個錯誤碼。

世界語(交流的語言很重要)

現在你可以在機器間聯絡了,可是要小心你所說的話。許多機器有自己的方言,如 ASCII 和 EBCDIC。更常見的問題是位元組順序問題。除非你一直傳輸的都是文本,否則你一定要注意這個問題。幸運的是,人們找出瞭解決的辦法。

在很久以前,人們爭論哪種順序更“正確”。現在必要時有相應的函數來轉換。其中有 htons()ntohs()htonl()ntohl()。在傳輸一個整型資料前,先轉換一下。

 

i= htonl(i); write_data(s, &i, sizeof(i));

在讀資料後,再變回來。

 

read_data(s, &i, sizeof(i)); i= ntohl(i);

如果你一直堅持這個習慣,你將比別人少出錯的機會。

未來在你的掌握了(下一步?)

就用我們剛才討論的東西,你就可以寫自己的通訊程式了。和對待所有的新生事物一樣, 最好還是看看別人已經做了些什麼。這裡有許多關於 BSD socket 的東西可以參考。

請注意,例子中沒有錯誤檢查,這在“真實”的程式中是很重要的。你應該對此充分重視。

上一篇:《linux裝置驅動的安全連接埠分配》相關文檔:《linux socket 編程》
下一篇:《【linux 編程】linux/unix 進程的建立》


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.