Linux嵌入式之————網絡編程socket

1、socket簡介

Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,

Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,

一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
門面模式,用自己的話說,就是系統對外界提供單一的接口,外部不需要了解內部的實現
socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,

都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。

我的理解就是Socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對

其進行的操作(讀/寫IO、打開、關閉)

2、tcp\ip協議簡介

TCP/IP(Transmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,

是一個工業標準的協議集,它是爲廣域網(WANs)設計的。
UDP(User Data Protocol,用戶數據報協議)是與TCP相對應的協議。它是屬於TCP/IP協議族中的一種。

3、網絡中進程之間的通信

要實現網絡中進程之間的通信,那麼就要解決一個問題,如何唯一標識一個進程?

否則,通信就無從談起。在本地可以通過進程PID來唯一標識一個進程,但是在網絡中這是行不通的。

其實TCP/IP協議族已經幫我們解決了這個問題,網絡層的“ip地址”可以唯一標識網絡中的主機,而

傳輸層的“協議+端口”可以唯一標識主機中的應用程序(進程)。這樣利用三元組(ip地址,協議,端口)

就可以標識網絡的進程了,網絡中的進程通信就可以利用這個標誌與其它進程進行交互。
使用TCP/IP協議的應用程序通常採用應用編程接口:UNIX  BSD的套接字(socket)和

UNIX System V的TLI(已經被淘汰),來實現網絡進程之間的通信。就目前而言,幾乎所有的應用程序都是採用socket,

而現在又是網絡時代,網絡中進程通信是無處不在。

4、socket編程常用函數

4.1、socket()

int socket(int domain, int type, int protocol);
socket函數對應於普通文件的打開操作。普通文件的打開操作返回一個文件描述字,

而socket()用於創建一個socket描述符(socket descriptor),它唯一標識一個socket。

這個socket描述字跟文件描述字一樣,後續的操作都有用到它,把它作爲參數,通過它來進行一些讀寫操作。
正如可以給fopen的傳入不同參數值,以打開不同的文件。創建socket的時候,也可以指定不同的參數創建

不同的socket描述符,socket函數的三個參數分別爲:
domain:即協議域,又稱爲協議族(family)。常用的協議族有,AF_INET、AF_INET6、

AF_LOCAL(或稱AF_UNIX,Unix域socket)、AF_ROUTE等等。協議族 決定了socket的地址類型,

在通信中必須採用對應的地址,如AF_INET決定了要用ipv4地址(32位的)與端口號(16位的)的組合、

AF_UNIX決定了要用一 個絕對路徑名作爲地址。
type:  指定socket類型。常用的socket類型有:

SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等
protocol:故名思意,就是指定協議。常用的協議有:

IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它們分別對應

TCP傳輸協議、UDP 傳輸協議、STCP傳輸協議、TIPC傳輸協議
注意:並不是上面的type和protocol可以隨意組合的,如SOCK_STREAM不可以跟IPPROTO_UDP組合。

當protocol爲0時,會自動選擇type類型對應的默認協議。

4.2、bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind()函數把一個地址族中的特定地址賦給socket。例如對應AF_INETAF_INET6就是把一個ipv4或ipv6地址和端口號組合賦給socket。即將地址、端口和socket返回的描述符做一個綁定
sockfd:即socket描述字,它是通過socket()函數創建了,唯一標識一個socket。bind()函數就是將給這個描述字綁定一個名字。
addr:一個const struct sockaddr *指針,指向要綁定給sockfd的協議地址。這個地址結構根據地址創建socket時的地址協議族的不同而不同。
如如ipv4對應的是:

struct sockaddr_in {
sa_family_t    sin_family; /* address family: AF_INET */
in_port_t      sin_port;   /* port in network byte order */
struct in_addr sin_addr;   /* internet address */
};

/* Internet address. */
struct in_addr {
uint32_t       s_addr;     /* address in network byte order */
};</span>
ipv6對應的是:
struct sockaddr_in6 { 
sa_family_t     sin6_family;   /* AF_INET6 */ 
in_port_t       sin6_port;     /* port number */ 
uint32_t        sin6_flowinfo; /* IPv6 flow information */ 
struct in6_addr sin6_addr;     /* IPv6 address */ 
uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */ 
};

struct in6_addr { 
unsigned char   s6_addr[16];   /* IPv6 address */ 
};</span>

addrlen:對應的是地址的長度。

4.3、listen()、connect()函數

如果作爲一個服務器,在調用socket()、bind()之後就會調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請 求。
int listen(int sockfd, int backlog);
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen函數的第一個參數即爲要監聽的socket描述字,第二個參數爲相應socket可以排隊的最大連接個數。socket()函數創建的socket默認是一個主動類型的,
listen函數將socket變爲被動類型的,等待客戶的連接請求。
connect函數的第一個參數即爲客戶端的socket描述字,第二參數爲服務器的socket地址,第三個參數爲socket地址的長度。客戶端通過調用connect函數來建立與 TCP服務器的連接。

4.4、accept()函數

TCP服務器端依次調用socket()、bind()、listen()之後,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()之後就想TCP服務器發送了一個連 接請求。TCP服務器監聽到這個請求之後,就會調用accept()函數取接收請求,這樣連接就建立好了。之後就可以開始網絡I/O操作了,即類同於普通文件的讀寫I/O操 作。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函數的第一個參數爲服務器的socket描述字,第二個參數爲指向struct sockaddr *的指針,用於返回客戶端的協議地址,第三個參數爲協議地址的長度。
如果accpet成功,那麼其返回值是由內核自動生成的一個全新的描述字,代表與返回客戶的TCP連接。即返回的fd與客戶端socket返回的fd一一對應,這樣就能進行通信了

4.5、read()、write()函數

服務器與客戶已經建立好連接了之後。就可以調用網絡I/O進行讀寫操作了,即實現了網咯中不同進程之間的通信!
int read(int fd, void *buf, size_t count);
int write(int fd, const void *buf, size_t count);

4.6、close()函數

當然用完socket之後肯定是要關閉的
int close(int fd);

5、socket網絡編程流程圖

6、一個例子的下載地址(本人親測)

http://download.csdn.net/detail/huangan_xixi/9492617

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章