linux 的套接字

套接字是一種通信機制,憑借這種機制,客戶/服務器系統的開發工作既可以在本地單機上進行,也可以跨網絡進行。

套接字的特性有三個屬性確定,它們是:域(domain),類型(type),和協議(protocol)。套接字還用地址作為它的名字。地址的格式隨域(又被稱為協議族,protocol family)的不同而不同。每個協議族又可以使用一個或多個地址族定義地址格式。

1.套接字的域

域指定套接字通信中使用的網絡介質。最常見的套接字域是AF_INET,它是指Internet網絡,許多Linux局域網使用的都是該網絡,當然,因特網自身用的也是它。其底層的協議——網際協議(IP)只有一個地址族,它使用一種特定的方式來指定網絡中的計算機,即IP地址。

在計算機系統內部,端口通過分配一個唯一的16位的整數來表示,在系統外部,則需要通過IP地址和端口號的組合來確定。

2.套接字類型

流套接字(在某些方面類似域標準的輸入/輸出流)提供的是一個有序,可靠,雙向字節流的連接。

流套接字由類型SOCK_STREAM指定,它們是在AF_INET域中通過TCP/IP連接實現的。他們也是AF_UNIX域中常見的套接字類型。

數據包套接字

與流套接字相反,由類型SOCK_DGRAM指定的數據包套接字不建立和維持一個連接。它對可以發送的數據包的長度有限制。數據報作為一個單獨的網絡消息被傳輸,它可能會丟失,復制或亂序到達。

數據報套接字實在AF_INET域中通過UDP/IP連接實現,它提供的是一種無需的不可靠服務。

3.套接字協議

只要底層的傳輸機制允許不止一個協議來提供要求的套接字類型,我們就可以為套接字選擇一個特定的協議。

創建套接字

socket系統調用創建一個套接字并返回一個描述符,該描述符可以用來訪問該套接字。

#include<sys/types.h>

#include<sys/socket.h>

int socket(int domain , int type , int protocol);

創建的套接字是一條通信線路的一個端點。domain參數指定協議族,type參數指定這個套接字的通信類型,protocol參數指定使用的協議。

domain參數可以指定的協議族如下

說明

AF_UNIX  UNIX域協議(文件系統套接字)

AF_INET  ARPA因特網協議(UNIX網絡套接字)

AF_ISO  ISO標準協議

AF_NS  施樂(XEROX)網絡系統協議

AF_IPX  NOVELL IPX協議

AF_APPLETALKAppletalk DDS

最常見的套接字域是AF_UNIX和AF_INET,前者用于通過Unix和Linux文件系統實現的本地套接字,后者用于Unix網絡套接字。AF_INET套接字可以用于通過包括因特網在內的TCP/IP網絡進行通信的程序。微軟Windows系統的winsock接口也提供了對這個套接字域的訪問功能。

socket函數的參數type指定用于新套接字的通信特性。它的取值包括SOCK_STREAM和SOCK_DGRAM。

SOCK_STREAM是一個有序、可靠、面向連接的雙字節流。通過TCP連接來實現。

SOCK_DGRAM是數據包服務,我們可以用它來發送最大長度固定的消息。但消息是否會被正確傳遞或消息是否不會亂序到達沒有保證。

套接字地址結構

結構struct sockaddr_un 定義了一種通用的套接字地址,它的類型是:

struct sockaddr_un

{

    sa_family_t sun_family;       /*AF_UNIX*/

    char              sun_path;         /*pathname*/

};

這是一種通用的定義,一般都不用。TCP/IP使用的是自己的結構體struct sockaddr_in,格式如下:

struct sockaddr_in

{

    short int sin_family;      //地址類型,一般為AF_INET

    unsigned short int sin_port;        //端口號

    struct in_addr sin_addr;        //IP地址

};

這里的struct in_addr的定義如下:

struct in_addr

{

    unsigned long int  s_addr;

};

結構體sockaddr和sockaddr_in的長度都是16字節。一般在編TCP/IP程序時,一般使用結構體sockaddr_in來設置地址,然后在需要的時候,通過強制類型轉換成sockaddr類型。

建立連接

函數connect用來在一個指定的套接字上創建一個連接,函數原型:

[cpp] view plain copy print?

  1. int connect(int socket, const struct sockaddr *address, size_t address_len);  

參數sockfd是一個由函數socket創建的套接字;

參數address是一個地址結構,需要連接的地址;

參數address_len為參數addr_addr的長度。

函數執行成功返回0,有錯誤發生則返回-1。

如果套接字類型是TCP,則該函數用于向服務器發出連接請求,服務器的IP地址和端口號由參數serv_addr指定;如果套接字類型是UDP,則該函數并不建立真正的連接,它只是告訴內核與該套接字進行通信的目的地址(由第二個參數指定),只有該目的地址發來的數據才會被該socket接收。

通常一個面向連接的套接字只能調用一次connect函數;而對于無連接的套接字則可以多次調用connect函數以改變與目的地址的綁定。

在套接字上監聽

函數listen把套接字轉化為被動監聽,函數原型:

int listen(int s, int backlog);

參數s指定了一個套接字;

參數backlog指定了該連接隊列的最大長度,如果已達到最大,則之后的連接請求將被服務器拒絕。

函數執行成功趕回0,有錯誤發生則返回-1。

由函數socket創建的套接字是主動套接字,這種套接字可以用來主動請求連接到某個服務器上。(通過connect()函數)。

作為服務器端的程序,通常在某個端口上監聽等待來自客戶端的連接請求。在服務器端,一般是先調用函數socket創建一個主動套接字,然后調用函數bind將該套接字綁定到某個端口上,接著再調用函數listen將該套接字轉化為監聽套接字,等待來自于客戶端的連接請求。

函數listen只是將套接字設置為傾聽模式以等待連接請求,它并不能接收連接請求,真正的接收客戶端連接請求的是accept()函數。

接收連接

函數accept用來接收一個連接請求,函數原型:

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);

參數s是由socket創建,經函數bind綁定到本地某一端口上,然后通過函數listen轉化而來的監聽套接字;

參數addr用來保存發起連接請求的主機的地址和端口;

參數addrlen是addr所指向的結構體的大小。

函數執行成功返回一個新的代表客戶端的套接字,出錯則返回-1。

只能對面向連接的套接字使用accept函數。accept執行成功時,將創建一個新的套接字,并且這個新的套接字分配一個套接字描述符,并返回這個新的套接字描述符。這個新的套接字描述符與打開文件返回的文件描述符類似,進程可以利用這個新的套接字描述符與客戶端交換數據,參數s所指定的套接字繼續等待客戶端的連接請求。

[cpp] view plain copy print?

  1. /*  Make the necessary includes and set up the variables.  */  

  2.   

  3. #include <sys/types.h>  

  4. #include <sys/socket.h>  

  5. #include <stdio.h>  

  6. #include <sys/un.h>  

  7. #include <unistd.h>  

  8. #include <stdlib.h>  

  9.   

  10. int main()  

  11. {  

  12.     int sockfd;  

  13.     int len;  

  14.     struct sockaddr_un address;  

  15.     int result;  

  16.     char ch = 'A';  

  17.   

  18. /*  Create a socket for the client.  */  

  19.   

  20.     sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  

  21.   

  22. /*  Name the socket, as agreed with the server.  */  

  23.   

  24.     address.sun_family = AF_UNIX;  

  25.     strcpy(address.sun_path, "server_socket");  

  26.     len = sizeof(address);  

  27.   

  28. /*  Now connect our socket to the server's socket.  */  

  29.   

  30.     result = connect(sockfd, (struct sockaddr *)&address, len);  

  31.   

  32.     if(result == -1) {  

  33.         perror("oops: client1");  

  34.         exit(1);  

  35.     }  

  36.   

  37. /*  We can now read/write via sockfd.  */  

  38.   

  39.     write(sockfd, &ch, 1);  

  40.     read(sockfd, &ch, 1);  

  41.     printf("char from server = %c\n", ch);  

  42.     close(sockfd);  

  43.     exit(0);  

  44. }  

[cpp] view plain copy print?

  1. /*  Make the necessary includes and set up the variables.  */  

  2.   

  3. #include <sys/types.h>  

  4. #include <sys/socket.h>  

  5. #include <stdio.h>  

  6. #include <sys/un.h>  

  7. #include <unistd.h>  

  8. #include <stdlib.h>  

  9.   

  10. int main()  

  11. {  

  12.     int server_sockfd, client_sockfd;  

  13.     int server_len, client_len;  

  14.     struct sockaddr_un server_address;  

  15.     struct sockaddr_un client_address;  

  16.   

  17. /*  Remove any old socket and create an unnamed socket for the server.  */  

  18.   

  19.     unlink("server_socket");  

  20.     server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  

  21.   

  22. /*  Name the socket.  */  

  23.   

  24.     server_address.sun_family = AF_UNIX;  

  25.     strcpy(server_address.sun_path, "server_socket");  

  26.     server_len = sizeof(server_address);  

  27.     bind(server_sockfd, (struct sockaddr *)&server_address, server_len);  

  28.   

  29. /*  Create a connection queue and wait for clients.  */  

  30.   

  31.     listen(server_sockfd, 5);  

  32.     while(1) {  

  33.         char ch;  

  34.   

  35.         printf("server waiting\n");  

  36.   

  37. /*  Accept a connection.  */  

  38.   

  39.         client_len = sizeof(client_address);  

  40.         client_sockfd = accept(server_sockfd,   

  41.             (struct sockaddr *)&client_address, &client_len);  

  42.   

  43. /*  We can now read/write to client on client_sockfd.  */  

  44.   

  45.         read(client_sockfd, &ch, 1);  

  46.         ch++;  

  47.         write(client_sockfd, &ch, 1);  

  48.         close(client_sockfd);  

  49.     }  

  50. }  

原創文章,作者:NddTx99521,如若轉載,請注明出處:http://www.www58058.com/37187

(1)
NddTx99521NddTx99521
上一篇 2016-08-18
下一篇 2016-08-18

相關推薦

  • linux函數和數組定義

    函數介紹:    函數function 是由若干條shell 命令組成的語句塊,實現代碼重用和模塊化編程。    它與shell 程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell 程序的一部分。    函數和shell 程序比較相似,區別在于:   …

    Linux干貨 2017-04-02
  • magedu_20160804

    文本處理工具和grep過濾工具以及正則表達式    一.文本處理工具相關命令    前面課程中曾經使用過cat,head,tail命令查看相關文件,head,tail分別查看文件開頭、結尾的行信息。對于特定想要顯示的信息則需要特定的文本查看指令,比如cut,sort,paste,uniq等。如下內容分別對每個…

    Linux干貨 2016-08-08
  • haproxy實戰之haproxy實現mysql負載均衡

    haproxy實戰之haproxy實現mysql負載均衡 實驗目的haproxy + mysql實現負載均衡 1.準備機器,做好時間同步,域名主機名解析 192.168.42.151 [node2 haproxy] 192.168.42.152 [node3 mariadb] 192.168.42.153 [node4 mariadb] 2.node3,no…

    Linux干貨 2017-06-29
  • 馬哥教育網絡22班第二周課程練習

    1,Linux上的文件管理類命令有哪些?其常用的使用方法機器相關實例演示 文件管理類命令:   cp 文件復制         常用選項:             -i:交互式           &…

    Linux干貨 2016-08-30
  • Nginx配置與應用詳解

    前言 Nginx是由俄羅斯軟件工程師Igor Sysoev開發的一個高性能的HTTP和反向代理服務器,具備IMAP/POP3和SMTP服務器功能。Nginx最大的特點是對高并發的支持和高效的負載均衡,在高并發的需求場景下,是Apache服務器不錯的替代品。目前,包括新浪、騰訊等知名網站都已使用Nginx作為Web應用服務器。本文帶來的是Nginx配置與應用詳…

    Linux干貨 2015-06-15
  • 軟鏈接與硬鏈接

    軟鏈接 硬鏈接

    2017-11-18
欧美性久久久久