日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unix网络编程第一卷学习总结

發布時間:2024/8/1 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unix网络编程第一卷学习总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡述和TCP/IP

如果要寫一個簡單的ipv6客戶端程序,需要sockaddr_in6地址,其余用法和ipv4的用法一致,
先指定地址族,端口,ip地址,connect成功后,就可以發包收包

int inet_pton(int af, const char *restrict src, void *restrict dst);
其中af可以是ipv4,也可以是ipv6,此函數的目的是把字符串轉換為dst二進制流

如果加了鎖 pthread_cond_wait 會保證 比 pthread_cond_signal 先解鎖

32位系統中size_t是一個32位值, 64系統中size_t是一個64位值
UDP不保證各個數據報順序跨網絡后保持不變,也不保證每個數據報只到達一次

傳輸層

UDP是無連接的,以下的代碼是客戶端給服務端進行發送時的處理, 其中dest_addr可以動態的切換
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);

SCTP 在客戶端與服務器之間提供關聯(一個關聯是指兩個系統間的一次通信),可能因為SCTP支持多宿(單個SCTP結點能夠支持多個ip地址)而涉及不止兩個地址
SCTP是面向消息的
TIME_WAIT的時間一般是最長分結生命期(MSL)的兩倍,通常為1分鐘~4分鐘


此圖比較仔細的說明了,客戶端與服務端三次握手,數據通信,四次分手的過程以及調用的函數,其中我們比較關注TIME_WAIT狀態,可以看出,四次分手時,當服務端準備好斷開時,會主動發送一個FIN,這時客戶端處于TIME_WAIT狀態
TIME_WAIT解決方式可以使用socket opt選項進行復用,并且TIME_WAIT的狀態有兩個存在的理由

  • 可靠的實現TCP全雙工連接的終止
    當服務端ACK M + 1丟失,那么客戶端必須要維護狀態信息,當服務端發送FIN N時,要提醒服務端重新發送ACK
  • 允許老的重復分節在網絡中消逝
    當老的連接和端口的鏈路斷開后,過一段時間相同的連接和端口建立另外一個鏈接,TCP必須保證老連接的數據重復在新鏈路發送,既然TIME_WAIT的時間是MSL(MSL,即Maximum Segment Lifetime,一個數據分片(報文)在網絡中能夠生存的最長時間)的兩倍,那么足以讓老的分組數組只存貨MSL時間后就被丟棄
  • 眾所周知的端口號范圍是0 ~ 1023, 由LANA(the internet assigned numbers authority)
    1024 ~ 49154 不受LANA控制,有LANA控制并維護使用狀況清單

    49152 ~ 65535是動態的或私用的端口,這些端口我們稱之臨時端口

    如果一個服務器的地址是多宿的,我們想讓任何一個地址都可以建立連接,那么我們可以指定服務器監聽的地址為INADDR_ANY,

    ipv4最大的數據報為65535個字節,包括IPV4首部20和tcp首部20,ipv6數據報的最大大小為65575個字節,包括60字節的ipv6首部,
    ipv4要求的最小鏈路MTU是68字節,允許最大的IPV4頭部(40字節)拼接最小的片段,ipv6要求的最小MTU為1280字節
    ipv4和ipv6都定義了最小重組緩沖區的大小,ipv4的默認緩沖區是576字節,對于ipfv6是1500字節

    ipv4首部DF位如果被設置,則代表不分片

    quinta@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_rmem 4096 87380 6291456 quinta@ubuntu:~$ cat /proc/sys/net/ipv4/tcp_wmem 4096 16384 4194304 quinta@ubuntu:~$ cat /proc/sys/net/core/rmem_max 212992 quinta@ubuntu:~$ cat /proc/sys/net/core/wmem_max 212992

    從上到下,分別是tcp 默認讀緩沖區,tcp默認寫緩沖區,tcp可設置讀最大緩沖區,tcp可設置寫最大緩沖區

    如果UDP發送的大小大于buffer的長度,那么可能會報錯EMSGSIZE,也可能不會報錯,但可能有的UDP不返回這種錯誤。

    socketadd_in和 socketaddr_un可以無縫和socketaddr進行轉換,都是16字節,那么我們為什么要使用socketaddr_in進行賦值呢,一i那位socketaddr中端口和ip是在一個數組中,對開發者不友好

    套接字編程簡介

    IPV6的地址族是AF_INET6,
    IPV6 新定義了通用字段 sockaddr_storage, sockaddr_storage足夠大,可以容納系統支持的最苛刻的對齊要求。

    include <netinet/in.h> struct sockaddr {unsigned short sa_family; // 2 bytes address family, AF_xxxchar sa_data[14]; // 14 bytes of protocol address };sockaddr_in 占用20字節 // IPv4 AF_INET sockets: struct sockaddr_in {short sin_family; // 2 bytes e.g. AF_INET, AF_INET6unsigned short sin_port; // 2 bytes e.g. htons(3490)struct in_addr sin_addr; // 4 bytes see struct in_addr, belowchar sin_zero[8]; // 8 bytes zero this if you want to };struct in_addr {unsigned long s_addr; // 4 bytes load with inet_pton() };

    sockaddr_in6 占用28字節

    struct sockaddr_in6 {sa_family_t sin6_family; /* AF_INET6 */in_port_t sin6_port; /* Transport layer port # */uint32_t sin6_flowinfo; /* IPv6 flow information */struct in6_addr sin6_addr; /* IPv6 address */uint32_t sin6_scope_id; /* IPv6 scope-id */};struct in6_addr {union {uint8_t u6_addr8[16];uint16_t u6_addr16[8];uint32_t u6_addr32[4];} in6_u;#define s6_addr in6_u.u6_addr8#define s6_addr16 in6_u.u6_addr16#define s6_addr32 in6_u.u6_addr32};

    sockaddr_storage占用128字節

    #define _SS_MAXSIZE 128 /* Implementation specific max size */ #define _SS_ALIGNSIZE (sizeof (int64_t))/* Implementation specific desired alignment */ /** DeFinitions used for sockaddr_storage structure paddings design.*/ #define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof (sa_family_t)) #define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (sa_family_t)+_SS_PAD1SIZE + _SS_ALIGNSIZE)) struct sockaddr_storage {sa_family_t __ss_family; /* address family *//* Following fields are implementation specific */char __ss_pad1[_SS_PAD1SIZE];/* 6 byte pad,this is to make implementation/* specific pad up to alignment field that *//* follows explicit in the data structure */int64_t __ss_align; /* field to force desired structure *//* storage alignment */char __ss_pad2[_SS_PAD2SIZE];/* 112 byte pad to achieve desired size,*//* _SS_MAXSIZE value minus size of ss_family *//* __ss_pad1,__ss_align fields is 112 */ };

    sockaddr_un是UNIX域,

    從進程到內核傳遞套接字地址結構的函數有三個,bind connect sendto

    從內核到進程的函數有四個,accept recvfrom getsockname getpeername,

    大端地址 是 高位字節 在 低地址,小端地址則相反

    以下是一段驗證大小端的代碼和htons 和 ntohs的博客,通過結果可以看出htons和ntohs的轉換結果一致,說明了兩個函數的作用一致,如果是大端,則轉化為小端,如果是小端,則轉換為大端

    #include<stdio.h> #include <arpa/inet.h>int main() {uint16_t num = 4096; // 0x1000if(num == htons(num)){printf("big endian\n");printf("address = %X\n", num);}else{printf("little endian");printf("address = %X\n", num);}uint16_t big_num = htons(num);printf("num = %d\n", big_num);printf("address = %X\n", big_num);big_num = ntohs(num);printf("num = %d\n", big_num);printf("address = %X\n", big_num);return 0; }


    inet_aton 用來將ip地址轉換為32位的網絡序
    inet_addr有類似的作用,這個函數現在已經廢棄不用
    inet_ntoa用來將in_addr結構轉換為字符串

    隨著ipv6的發展,新增了有類似功能的函數

    其中family可以顯示的指定網絡協議名稱,AF_INET或AF_INET6, inet_pton用來將ip地址轉換為網絡序,inet_ntop轉換,可以理解為inet_pton和inet_aton是套了層殼子

    基本TCP套接字編程


    客戶端 connect函數前不需要調用bind,這時連接時內核會分配一個臨時端口
    listen函數把一個未連接的套接字轉換為一個被動套接字,指示內核應該接受該套接字的連接請求

    int listen(int sockfd, int backlog);其中backlog解釋如下,簡單可理解為服務器可支持的最大連接數
    The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow

    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);可以看到addr是指對端的ip地址
    The argument addr is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket
    fork如果在accept之后,那么文件描述符,父進程與子進程共享,需要將父進程的文件描述符關閉,如果想要徹底關閉文件描述符,那么需要在父進程,子進程都調用close來關閉

    在一個沒有調用bind的TCP客戶端,connect成功返回后,getsocketname返回的是內核賦予該連接的本地ip地址和本地端口號

    在以0去bind的時候,getsockname返回的是內核賦予的本地端口號,同樣也可以獲得某個套接字的地址族,適合于以下情況,未調用bind就connect了

    TCP客戶/服務端程序示例

    SIGCHLD 是內核在任何一個進程停止時,發給它的父進程的一個信號,如果父進程比子進程先結束,而且沒有回收子進程,那么子進程將會變成一個僵尸進程

    void (*signal(int sig, void (*func)(int)))(int)
    圖片部分來自于

    https://www.runoob.com/cprogramming/c-function-signal.html


    EINTER錯誤由在非阻塞套接字上不能立即完成的操作返回,例如,當套接字上沒有排隊數據可讀時調用了recv()函數。此錯誤不是嚴重錯誤,相應操作應該稍后重試

    wait函數會阻塞直到第一個子進程返回
    wait_pid可以按子進程的進程號進行等待,正確的方法是在信號處理函數中循環調用waitpid進行處理

    accept需要判斷正確錯誤,如果返回一個非致命的錯誤,要再次進行accept,出現這種問題的原因是三次握手后,客戶端又發送了一次復位的請求,內核隊列中將會把此套接字刪除,但是業務層調用accept時,不知道曾經有一個已完成的連接被從隊列中刪除了

    當服務器進程停止時,會向客戶端發送一個FIN,客戶端回送一個ACK,此時服務器的狀態時FIN_WAIT2, 客戶端的狀態是CLOSE_WAIT,但是此時客戶端還是可以發送數據的,發送數據不會報錯,服務器收到數據時會發送回一個響應,但是業務層不會收到響應,再次發送時會引用SIGPIPE信號,默認導致進程退出

    假如客戶端和服務器之間通過路由連接,但是服務器異常崩潰,那么tcp會重傳數據約12次,直到放棄重傳,客戶機阻塞在readline中,最后會收到一個ETIMEOUT錯誤

    客戶端與服務器通信如果是文本串的格式,相對安全,如果是二進制結構,那么可能存在大小端轉換的問題

    I/O復用: select 和 poll函數

    select和poll和組合的區別是多路復用可以預先告訴進程,使得內核一旦發現進程指定的一個或多個I/O條件就緒,就主動通知進程
    異步接收數據時,如果數據沒有準備好,那么會返回一個EWOULDBLOCK錯誤,如果數據復制了一些,那么此時會返回成功

    接收數據也可以使用信號驅動模式,通訊過程如下

    異步IO aio_read 和信號驅動模式的主要區別在于,信號驅動式I/O是由內核通知我們何時能啟動一個I/O操作,而異步IO模型是由內核通知我們I/O操作何時完成


    select 可以設置低水位,比如我們默認知道小于X字節時,不是有效的數據,則可以設置內核收到X字節時,才通知應用層

    close函數把套接字的引用計數減1,當套接字等于0時,才會主動執行關閉操作,但是shutdown函數可以不管引用計數就激發TCP的正常連接終止序列
    shutdown 依賴于 howto的值,SHUT_RD 關閉連接的讀這一半,SHUT_WR關閉連接寫這一半,SHUT_RDWR連接的讀半部和寫半部都關閉

    for ( ; ; ) {36 rset = allset; /* structure assignment */37 nready = Select(maxfd+1, &rset, NULL, NULL, NULL);3839 if (FD_ISSET(listenfd, &rset)) { /* new client connection */40 clilen = sizeof(cliaddr);41 connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);42 #ifdef NOTDEF43 printf("new client: %s, port %d\n",44 Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),45 ntohs(cliaddr.sin_port));46 #endif4748 for (i = 0; i < FD_SETSIZE; i++)49 if (client[i] < 0) {50 client[i] = connfd; /* save descriptor */51 break;52 }53 if (i == FD_SETSIZE)54 err_quit("too many clients");5556 FD_SET(connfd, &allset); /* add new descriptor to set */57 if (connfd > maxfd)58 maxfd = connfd; /* for select */59 if (i > maxi)60 maxi = i; /* max index in client[] array */6162 if (--nready <= 0)63 continue; /* no more readable descriptors */64 }

    可以看下以上代碼塊,為什么每次用allset給rset復制,因為每次select時候,如果fd沒有變更,那么select 會自動把fd從set中刪除

    當服務器使用阻塞IO時,可能會有以下問題,客戶端只發送了一個字節后就不再發送,這時服務器下次就會在io處阻塞,影響其他用戶的發送

    如果不在關心某個特定描述符,那么可以把對應pollfd結構中的fd設置為一個負數

    套接字選項

    主要又四個函數需要著重了解
    gesocketopt
    setsocketopt
    fcntl
    ioctl


    以上是所有的套接字選項

    int getsockopt(int sockfd, int level, int optname,
    void *optval, socklen_t *optlen);
    此時返回的optval是一個整數,如果為0,則代表相應選項被禁止,非0代表被啟用
    類似的,setsockopt也是如此,

    tcp存在流量控制,不允許發送超過窗口大小的數據
    TCP套接字緩沖區的長度至少是MSS的四倍

    基本UDP套接字編程

    UDP發送一個0字節長度是可行的,接受為0也不代表斷開連接

    大部分tcp服務器都是并發的,大部分udp服務器都是迭代的

    #include "unp.h"void dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen) {int n;char sendline[MAXLINE], recvline[MAXLINE + 1];socklen_t len;struct sockaddr *preply_addr;preply_addr = Malloc(servlen);while (Fgets(sendline, MAXLINE, fp) != NULL) {Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);len = servlen;n = Recvfrom(sockfd, recvline, MAXLINE, 0, preply_addr, &len);if (len != servlen || memcmp(pservaddr, preply_addr, len) != 0) {printf("reply from %s (ignored)\n",Sock_ntop(preply_addr, len));continue;}recvline[n] = 0; /* null terminate */Fputs(recvline, stdout);} }

    可以看下這段代碼有什么問題, 當對端機器只有一個網卡是沒問題的,當服務器有多個網卡就會判斷錯誤,遇到這種情況怎么辦呢,對端機器要針對每塊網卡進行bind操作,用多路轉接進行監聽

    如果服務器沒有啟動,客戶端udp連接發送sendto,可能成功,但是實際上發送失敗,因為異步錯誤不會返回給程序

    udp客戶發送的臨時端口不能改變,但是ip可以改變

    UDP默認緩沖區大小為42080字節
    tcp和udp可以綁定同意端口

    名字與地址轉換

    gethostbyname把主機名字映射為IPV4地址,gethostbyaddr執行相反的映射
    getservbyname是根據服務名稱,協議名稱,獲取ip地址信息
    getservbyport類似

    下圖是getaddrinfo中hints中ai_family 和 ai_socktype的對應關系

    gai_strerror可以打印出 getaddrinfo錯誤信息

    同樣getaddrinfo既可以處理ipv4,也可以處理ipv6

    UDP套接字不需要設置SO_REUSEADDR選項

    getnameinfo是getaddrinfo的互補函數

    gethostbyname,gethostbyaddr,getservbyname,getservbyport都是不可重入的
    inet_pton,inet_atop都是可重入的

    高級套接字編程

    當服務器既有ipv4網卡和ipv6網卡時,如果客戶端是ipv4,那么與服務器通信時會映射為ipv6地址,但是實際上兩者的通信方式依然是ipv4通信
    當ipv6客戶端指向一個ipv4映射的ipv6地址時,那么實際上通信方式依然是ipv4

    守護進程和inetd超級服務器

    int daemon_init(const char *pname, int facility) {int i;pid_t pid;if ( (pid = Fork()) < 0)return (-1);else if (pid)_exit(0); /* parent terminates *//* child 1 continues... */if (setsid() < 0) /* become session leader */return (-1);Signal(SIGHUP, SIG_IGN);if ( (pid = Fork()) < 0)return (-1);else if (pid)_exit(0); /* child 1 terminates *//* child 2 continues... */daemon_proc = 1; /* for err_XXX() functions */chdir("/"); /* change working directory *//* close off file descriptors */for (i = 0; i < MAXFD; i++)close(i);/* redirect stdin, stdout, and stderr to /dev/null */open("/dev/null", O_RDONLY);open("/dev/null", O_RDWR);open("/dev/null", O_RDWR);openlog(pname, LOG_PID, facility);return (0); /* success */ }

    需要充分理解此函數

    高級IO函數

    connect超時時間一般為75秒
    可以使用alarm使的超時時間減短,但是最大超時時間就是75秒

    也可以使用SO_RVTTIMEO進行設置

    Unit域協議

    unit域和普通的區別是只能在本機通信
    bind時會bind一個本地地址
    socketpair僅僅適用于unix套接字,比較類似pipe函數
    在一個未綁定的Unix套接字上發送數據報不會自動給這個套接字捆綁一個路徑名
    socketpair也可以來傳遞文件描述符,通過帶外數據進行讀取

    ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd) {struct msghdr msg;struct iovec iov[1];ssize_t n;#ifdef HAVE_MSGHDR_MSG_CONTROLunion {struct cmsghdr cm;char control[CMSG_SPACE(sizeof(int))];} control_un;struct cmsghdr *cmptr;msg.msg_control = control_un.control;msg.msg_controllen = sizeof(control_un.control); #elseint newfd;msg.msg_accrights = (caddr_t) &newfd;msg.msg_accrightslen = sizeof(int); #endifmsg.msg_name = NULL;msg.msg_namelen = 0;iov[0].iov_base = ptr;iov[0].iov_len = nbytes;msg.msg_iov = iov;msg.msg_iovlen = 1;if ( (n = recvmsg(fd, &msg, 0)) <= 0)return(n);#ifdef HAVE_MSGHDR_MSG_CONTROLif ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {if (cmptr->cmsg_level != SOL_SOCKET)err_quit("control level != SOL_SOCKET");if (cmptr->cmsg_type != SCM_RIGHTS)err_quit("control type != SCM_RIGHTS");*recvfd = *((int *) CMSG_DATA(cmptr));} else*recvfd = -1; /* descriptor was not passed */ #else /* *INDENT-OFF* */if (msg.msg_accrightslen == sizeof(int))*recvfd = newfd;else*recvfd = -1; /* descriptor was not passed */ /* *INDENT-ON* */ #endifreturn(n); }

    貼一個關鍵代碼,這塊代碼主要實現了進程間的fd傳遞

    非阻塞IO

    對于一個非阻塞的io,當緩沖空間不夠時,會發送一個EWOULDBLOCK的錯誤,

    非阻塞模式下,connect很可能會返回EINPROGRESS錯誤碼

    #include "unp.h"int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) {int flags, n, error;socklen_t len;fd_set rset, wset;struct timeval tval;flags = Fcntl(sockfd, F_GETFL, 0);Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);error = 0;if ( (n = connect(sockfd, saptr, salen)) < 0)if (errno != EINPROGRESS)return(-1);/* Do whatever we want while the connect is taking place. */if (n == 0)goto done; /* connect completed immediately */FD_ZERO(&rset);FD_SET(sockfd, &rset);wset = rset;tval.tv_sec = nsec;tval.tv_usec = 0;if ( (n = Select(sockfd+1, &rset, &wset, NULL,nsec ? &tval : NULL)) == 0) {close(sockfd); /* timeout */errno = ETIMEDOUT;return(-1);}if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {len = sizeof(error);if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)return(-1); /* Solaris pending error */} elseerr_quit("select error: sockfd not set");done:Fcntl(sockfd, F_SETFL, flags); /* restore file status flags */if (error) {close(sockfd); /* just in case */errno = error;return(-1);}return(0); }

    這段代碼首先設置fd非阻塞,如果connect的結果是EINPROGRESS,則代碼三次握手還未完成,通過select設置一個超時時間,等到超時時間結束后

    服務器accept建議使用非阻塞模式,因為當監聽到fd可讀時,如果在accept之前客戶端就斷開,那么accept就會阻塞,所以讓accept為非阻塞是很有必要的

    ioctl操作

    ioctl通常會進行以下6類操作
    套接字操作,文件操作,接口操作,ARP高速緩存操作,路由表操作,流系統

    路由套接字

    廣播


    ipv4支持廣播,ipv6不支持
    通常廣播地址是ip最后是255的地址
    內核不允許對廣播數據報執行分片

    多播

    ipv4的D類地址,224.0.0.0 到 239.255.255.255,是ipv4多播地址,
    IP_ADD_MEMBERSHIP 和 MCAST_JOIN_GROUP 類似

    高級UDP套接字編程

    如果程序想要支持多播和廣播,那必須要使用UDP
    UDP沒有連接建立和刪除,只需要兩個分組就可以交換一個請求和一個應答,tcp卻需要20個分組
    tcp有一些udp的優勢,如果想要一些特性,必須由應用程序自行提供他們,包括以下

    如果想要請求重傳式程序使用UDP,那么必須在客戶程序中增加以下兩個特性,1) 超時和重傳 2)序列號
    如果創建一個并發UDP服務器,1 當與客戶只發送幾個消息,可以fork一個子進程,并讓子進程處理
    2 與客戶端交換多個消息,可以讓服務端為每個客戶創建一個套接字,在其上bind一個臨時端口,然后使用該端口發送對該客戶的所有應答

    帶外數據

    帶外數據也叫做加速數據,帶外數據被認為比普通數據有更高的優先級

    如果新的OOB在舊的OOB被讀取之前就到達,那么舊的OOB數據會被丟棄
    當緊急數據到達實際的緩沖區,該數據字節可能被拉出帶外,也有可能被留在帶內,SO_OOBILLINE默認是禁止的
    如果進程多次讀入同一個帶外字節,讀入操作將返回EINVAL
    內核檢測到oob數據發送過來時,需要注冊SIGURG, 并且設置文件描述符為F_SETOWN
    如果設置了在線數據,讀位置總是從帶外數據開始的
    帶外數據可以時任何8位值

    SO_KEEPALIVE 在兩個小時沒有數據交換時,會自動發送一個探針

    信號驅動式IO

    信號驅動IO時內核有數據時,通過應該注冊的回調函數進行通知,信號名稱是SIGIO

    IP選項

    ipv4允許在20個字節首部固定部分之后跟以最多共40個字節的選項
    這些選項可以通過IP_OPTIONS套接字選項
    可以通過getsockopt或setsockopt進行設置

    客戶/服務器程序設計樣例

    總結

    以上是生活随笔為你收集整理的Unix网络编程第一卷学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。