linux socket


linux 的 C 庫路徑為  /usr/include,可以直接查看源碼,也可以通過 "man 頭文件名" 來學習,需要查看某個函數如 bind() ,則只需要 man 2 bind 即可。

 

如:<stdint.h> 定義了 int8_t、int16_t、int32_t、int64_t、uint8_t、uint16_t、uint32_t、uint64_t

詳見:/usr/include/stdint.h   或 man stdint.h

 

IPv4套接口地址結構:

struct sockaddr_in {
  short int sin_family;                      /* Address family 2個字節 */
  unsigned short int sin_port;       /* Port number 2個字節 */
  struct in_addr sin_addr;              /* Internet address 4個字節 */
  unsigned char sin_zero[8];         /* Same size as struct sockaddr 8個字節,暫不使用,一般設為0*/
  };

sim_family 決定地址家族,IPv4 必須設置為 AF_INET,其它還有 AF_INET6 (IPv6協議)、AF_ROUTE(路由套接口)

in_addr 這個結構體里只含有一個成員:

struct in_addr {
    uint32_t s_addr;
    };

sin_addr.s_addr 使用網絡字節序,可以使用 inet_addr 方法將字符串格式 IP 轉換成網絡字節序。

通用套接口地址結構:

struct sockaddr {
  unsigned short sa_family;     /* address family, AF_xxx */
  char sa_data[14];                 /* 14 bytes of protocol address */
  }; 

connect、 bind、 accept 方法使用的都是通用套接口地址結構,需要把 sockaddr_in 結構強轉為 sockaddr 即可。

不同的主機可能有不同的字節序,其中x86 為小端字節序。網絡字節序規定為大端字節序。

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
    unsigned int x = 0x12345678;
    unsigned char *p = (unsigned char*)&x;
    printf("%0x %0x %0x %0x\n",p[0],p[1],p[2],p[3]);
    unsigned y = htonl(x);
    p = (unsigned char*)&y;
    printf("%0x %0x %0x %0x\n",p[0],p[1],p[2],p[3]);
}

輸出結果為:

 

字節序轉換函數:

#include <arpa/inet.h>

uint32_t htonl(unit32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint32_t ntohs(uint16_t netshort);

 

地址轉換函數:

#include <netinet/in.h>
#include <arpa/inet.h>

in_addr_t inet_addr(const char *cp);  //點分十進制轉換成32位整型地址
int inet_aton(const char *cp, struct in_addr *inp); //同上
char *inet_ntoa(struct in_addr in);  //32位整型地址轉換成點分十進制地址

 

套接字類型:

SOCK_STREAM    流式套接字
SOCK_DGRAM     數據報套接字
SOCK_RAW       原始套接字

 

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

如:socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

也可以寫成:socket(AF_INET, SOCK_STREAM, 0);    //第三個參數為0,表示由系統自動選擇協議,而 AF_INET 和 SOCK_STREAM 組合一定是 IPPROTO_TCP 協議。

 

指定本機地址:

server.sin_addr.s_addr = htonl(INADDR_ANY);    //推薦
server.sin_addr.s_addr = inet_addr("127.0.0.1");
inet_aton("127.0.0.1",&server.sin_addr);  

 

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 

在綁定之前,盡量使用 setsockopt() 來設置 SO_REUSEADDR 套接字選項,可以使得不必等待 TIME_WAIT 狀態消失就可以重啟服務器。如:

int on = 1;
setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

level:(級別): 指定選項代碼的類型。
  SOL_SOCKET: 基本套接口
  IPPROTO_IP: IPv4套接口
  IPPROTO_IPV6: IPv6套接口
  IPPROTO_TCP: TCP套接口
optname(選項名): 選項名稱
optval(選項值): 是一個指向變量的指針 類型:整形,套接口結構, 其他結構類型:linger{}, timeval{ }
optlen(選項長度) :optval 的大小

返回值:標志打開或關閉某個特征的二進制選項

 

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

 

int listen(int sockfd, int backlog);

第二個參數表示監聽隊列最大連接數(包括未完成連接隊列和已完成連接隊列的大小之和),可以使用 SOMAXCONN 這個宏。

 

int accept(int listenfd, struct sockaddr* client, socklen_t *addlen);

client 和 addlen 是用來返回客戶端的套接字地址結構和對應的結構長度的。如果不關心這兩個值,可以使用  accept(int sockfd, NULL, NULL);

否則為:

sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);     //這時必須要有初始值,否則 accept() 失敗
accept(sockfd, (sockaddr*)&client_addr, &client_addr_len));

使用 socket() 創建一個套接字時,默認情況下它是一個主動套接字,即將調用 connect() 發起連接的客戶端套接字,對於服務器,必須調用 listen() 函數,將這個未連接的套接字轉換成被動套接字,即監聽套接字,負責接受每個客戶的連接請求一個服務器常常只有一個監聽套接字,而且會一直存在,直到服務器關閉。accept() 函數可以返回已連接套接字描述符,已連接套接字是內核為每個被接受的客戶端分別創建的,負責與對應的客戶端進行數據傳輸。當服務器完成與該客戶端的數據傳輸時,需要關閉該已連接描述符。

 

數據傳輸函數:

因為套接字描述符也是一種文件描述符,所以可以使用文件讀寫函數 read() 和 write():

#include <unistd.h>

int write(int sockfd, char *buf, int len);     //出錯返回 -1,成功返回大於0的整數,為發送的字節數

int read(int sockfd, char *buf, int len);  //出錯返回 -1,成功返回大於0的整數,為接收的字節數

此外,還有 TCP套接字提供的 send() 和 recv() 函數:

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void* buf, size_t len, int flags);    //前三個參數同 write() ,第四個參數為傳輸控制標志,一般設為0,也可以為其它值如:MSG_OOB 等

ssize_t recv(int sockfd, void *buf, size_t len, int flags);   //前三個參數同 read() ,第四個參數一般設為0,也可以為其它值如:MSG_PEEK 等

 

關閉套接字:

#include <unistd.h>

close(int sockfd);

 

UDP的數據傳輸函數:

ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr *to, int addrlen); 

ssize_t recvfrom(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr *from, int addrlen);

前四個參數完全等同 TCP 的 send 和 recv 方法,第五和第六個參數類似於 accept() 的后兩個參數。這兩個函數,也可以用於TCP協議,但是一般不這么使用。

UDP的套接字不需要監聽,只要 bind() 需要監聽的端口就可以接收數據了。

 

exit() 退出當前進程。

父進程退出,程序結束,但子進程卻沒有退出,可以通過 "px aux | grep 二進制文件名" 來查看。所以需要通過信號通知的方式,來通知子進程退出。

 

sysconf(_SC_NPROCESSORS_CONF)   獲取服務器處理器數目(核心數)

 

 

關於下面的寫法,見於:http://www.spongeliu.com/415.html

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM