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. 服務器調用socket,bind,listen被動打開連接
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 readline(int 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信號通知進程。