[心得]UNP知識整理

tangchen2016發表於2016-10-30

Unix網路程式設計是一本系統程式設計的經典書籍。這裡整理了相關知識。

#include    "unp.h"

int
main(int argc, char **argv)
{
    int                 sockfd, n, counter = 0;
    char                recvline[MAXLINE + 1];
    struct sockaddr_in  servaddr;

    if (argc != 2)
        err_quit("usage: a.out <IPaddress>");

    if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        err_sys("socket error");

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port   = htons(13);    /* daytime server */
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
        err_quit("inet_pton error for %s", argv[1]);

    if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
        err_sys("connect error");

    while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
        counter++;
        recvline[n] = 0;    /* null terminate */
        if (fputs(recvline, stdout) == EOF)
            err_sys("fputs error");
    }
    if (n < 0)
        err_sys("read error");

    printf("counter = %d\n", counter);
    exit(0);
}

這裡的做法有一個缺陷,就是它和協議耦合在一起。

約定包裹函式名的首字母大寫。
SCTP:流控制傳輸協議
ICMP:網際控制訊息協議

TCP三路握手:建立連線
1. 伺服器呼叫socketbindlisten被動開啟連線
2. 客戶呼叫connect主動開啟,傳送SYN同步
3. 伺服器返回ACK+SYN給客戶
4. 客戶確認SYN

TCP中執行主動關閉時會經歷TIME_WAIT狀態。這個狀態是為了實現TCP全雙工連線終止(處理最終那個ACK丟失的情況),並允許老的重複分節從網路中消逝。
最長分節生命期MSL是任何IP資料包在因特網上存活的最長時間。

TCP使用4分組交換序列終止連線。

inet_aton
inet_addr
inet_ntoa
在點分十進位制數串與它長度為32位的網路位元組序二進位制值間轉換ipv4地址。
inet_pton
inet_ntop
通用p:presentation和numeric

讀寫socket位元組流:
ssize_t readn(int filedes, void *buff, size_t nbytes);
ssize_t written(int filedes, const void *buff, size_t nbytes);
ssize_t readlineint filedes,void *buff,size_t maxlen);

socket head file:
#include <sys/socket.h>

建立套接字
int socket(int family, int type, int protocol);
連線
int connect(int sockfd, const struct sockaddr *servaddr, sockelen_t addrlen);

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

int listen(int sockfd, int backlog);

int accept(int sockfd, struct socketaddr *cliaddr, socklen_t *addrlen);

int close(int sockfd);

最流行的I/O模型是阻塞式的
select函式允許程式指示核心等待多個事件中的任何一個發生,並只在有一個或多個事件發生或經歷一段指定時間後喚醒它。
#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdpl,df_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);

終止網路連線的close函式有2個限制:
1.close把描述符-1,僅在該計數變回0時才關閉套接字。
2.close終止讀和寫兩個方向的資料傳送。
shutdown可以避免這兩點:
int shutdown(int sockfd, int howto);

poll函式類似select函式,但提供額外資訊
#include <poll.h>
int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);

域名系統DNS在主機名和ip地址之間做對映。
#include <netdb.h>
查詢主機名
按名
struct hostent *gethostbyname(const char *hostname);
按地址
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);
查詢服務
按名
struct servent *getservbyname(const char *servname, const char *protoname);
按埠
struct servent *getservbyport(int port, const char *protoname);

getaddrinfo函式能處理名字到地址以及服務到埠這兩種轉換。返回的是一個sockaddr。
int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);

void freeaddrinfo(struct addrinfo *ai);
釋放資源

getnameinfo是getaddrinfo的補充,它以套接字地址為引數,返回描述其中主機的一個字串和描述其中服務的另一個字串。
int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);

守護程式是在後臺執行且不與任何控制終端關聯的程式。
在守護程式中記錄訊息的常用技巧就是呼叫syslog函式
#include <syslog.h>

void syslog(int priority, const char *message,...);

可能阻塞的套接字呼叫包括以下4類:
1.輸入操作,read,readv, recv,recvfrom, recvmsg
2.輸出操作,write,writev, send, sendto,sendmsg
3.接受外來連線,accept函式
4.發起外出連線,connect函式

網路程式,特別是伺服器程式,經常在程式啟動執行後使用ioctrl獲取所在主機全部網路介面的資訊,包括介面地址,是否支援廣播,是否支援多播等。

#include <unistd.h>

int ioctl(int fd, int request,...);

網路相關的request劃分為6類:
套接字操作(是否位於帶外標記)
檔案操作(設定或清除非阻塞標誌)
介面操作(返回介面列表,獲取廣播地址)
ARP快取記憶體操作(建立,修改,獲取或刪除)
路由表操作(增加或刪除)
流系統

使用sysctl可以檢查路由表和介面列表的程式卻不限使用者許可權。

#include <sys/param.h>
#include <sys/sysctl.h>

int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);

廣播傳送的資料包由傳送主機某個所在子網上的所有主機接收。廣播的劣勢在於同一子網上的所有主機都必須處理資料包。

TCP沒有真正的帶外資料,不過提供緊急模式和緊急指標。帶外資料未廣泛使用,telnet和rlogin使用它。它們使用帶外資料是為了統治遠端有異常情況發生,而且伺服器丟棄帶外標記前接收所有輸入。

訊號驅動式I/O就是讓核心在套接字上發生“某事”時使用SIGIO訊號通知程式。

相關文章