《Unix网络编程》卷1:套接字联网API(第3版):简介、传输层、套接字编程
2017-08-05 23:47
525 查看
全书共31章+附录。
计划安排:吃透这本书,一天三章+源码,并实测代码做当天笔记,CSDN见。
时间安排:计划时间1.5个月 == 6个周末 == 12天。
2017.08.05 第01-03章:TCP/IP简介、传输层、套接字编程简介
2017.08.06 第04-06章:基本TCP编程、TCP客户端/服务器程序、I/O复用
2017.08.12 第07-09章:套接字选项、基本UDP编程、基本SCTP编程
2017.08.13 第10-12章:SCTP客户端/服务器程序例子、名字与地址互换、IPv4和IPv6互操作性
2017.08.19 第13-15章:守护进程和inetd超级服务器、高级I/O、Unix域协议
2017.08.20 第16-18章:非阻塞I/O、ioctl操作、路由套接字
2017.08.26 第19-21章:密钥管理套接字、广播、多播
2017.08.27 第22-24章:高级UDP编程、高级SCTP编程、带外数据
2017.09.02 第25-27章:信号驱动I/O、线程、IP选项
2017.09.03 第28-30章:原始套接字、数据链路访问、客户端/服务器程序设计范式
2017.09.09 第31章-附录:流。附录:IPv4/6协议、调试技术
2017.09.10 整理、总结:思维导图。
举例来说:web服务器程序时一个长时间运行的守护程序,web客户与服务器之间使用TCP通信,TCP转而使用IP通信,IP通过以太网驱动程序的数据链路层通信。
客户端和服务器通常是用户进程,而TCP和IP协议通常是内核中"协议栈"的一部分。
LAN:局域网(内网)
WAN:广域网(外网)
路由器是广域网的架构设备。当下最大的广域网是因特网internet。
【Tips】调用sprintf无法检查目的缓冲区是否溢出,相反,snprintf要求其第二个参数指定目的缓冲区的大小,因此可以确保该缓冲区不溢出。get√
许多网络入侵是由黑客通过发送数据,导致服务器对sprintf的调用使其缓冲区溢出而发生的,必须小心使用的函数还有gets/strcat/strcpy,通常应分别改为调用fgets/strncat/strncpy,更好的替代函数还有strlcat/strlcpy可以确保结果是正确终止的字符串。
OSI模型 open systems interconnection
全称:计算机通信开放系统互连模型。
物理层/数据链路层:主要是设备驱动和网络硬件,通常我们不必关心。
网络层:由IPv4和IPv6这两个协议处理。详细在附录A中。
传输层:即本书所讲的套接字编程接口,从应用层(上3层)进入传输层的接口。
应用层/会话层/表示层:统称为应用层,如web客户端(浏览器)、telnet客户端、web服务器、FTP服务器等。
网络细节的两个基本命令:netstat /
ifconfig
netstat
(1)netstat -ni // 提供网络接口信息,-n输出数值地址而不是反向解析为名字
$ netstat -ni
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 0 15459 0 0 0 10444 0 0 0 BMRU
lo 16436 0 138 0 0 0 138 0 0 0 LRU
lo 环回接口
eth0 以太网接口
(2)netstat -nr // 展示路由表信息,另一种确定接口的方法
$ netstat -nr
内核 IP 路由表
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.31.1 0.0.0.0 UG 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
192.168.31.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
(3)ifconfig eth0 // 获得eth0以太网接口的详细信息
$ ifconfig eth0
eth0 Link encap:以太网 硬件地址 00:0c:29:55:a0:99
inet 地址:192.168.31.205 广播:192.168.31.255 掩码:255.255.255.0
inet6 地址: fe80::20c:29ff:fe55:a099/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:15624 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:10571 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:1468669 (1.4 MB) 发送字节:1070042 (1.0 MB)
中断:19 基本地址:0x2000
// MULTICAST 标志通常指明该接口所在主机支持多播。
(4)ping // 测试ip地址是否联通当前以太网络
$ ping -b 192.168.31.255
PING 192.168.31.255 (192.168.31.255) 56(84) bytes of data.
64 bytes from 192.168.31.255: icmp_req=1 ttl=64 time=0.253 ms
64 bytes from 192.168.31.255: icmp_req=2 ttl=64 time=0.022 ms
64 bytes from 192.168.31.255: icmp_req=3 ttl=64 time=0.029 ms
...
64位体系结构的趋势
原因之一是:在每个进程内部可以由此使用更长的编址长度(即64位指针),从而可以寻址更大的内存空间(超过2^32字节)。
UDP:用户数据报协议,无连接协议。数据报套接字。不保证最终达到目的地。
ICMP:网际控制消息协议,处理路由器和主机之间流通的错误和控制消息。
ARP:地址解析协议,把IPv4地址映射成一个硬件地址。
RARP:反地址解析协议,把一个硬件地址映射成一个IPv4地址。
SCTP(内容略)
TIME_WAIT状态有两个存在的理由:
(1)可靠地实现TCP全双工连接的终止;
(2)允许老的重复分节在网络中消逝。
端口号port:
传输层协议都使用16位整数的端口号来区分不同的进程。
0~1023(自控保留) | 1024~49151(已登记) |49152~65535(动态私有)
通讯对socket pair:
每个TCP分节中都有16bit的端口号和32bit的IPv4地址。
IPv4套接字地址结构:sockaddr_in
#include <netinet/in.h>
struct in_addr {
in_addr_t s_addr; /* 32bit IPv4 address. */
};
struct sockaddr_in {
uint8_t sin_len; /* length of structure (16) */
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* 16bit TCP/UDP port number */
struct in_addr sin_addr; /* 32bit IPv4 address */
char sin_zero[8]; /* unused */
};
通用套接字地址结构:sockaddr
#include <sys/socket.h>
struct sockaddr {
uint8_t sa_len; /* length of structure */
sa_family_t sa_family; /* address family: AF_XXXX value */
char sa_data[14]; /* protocol-specific address */
};
IPv6套接字地址结构:sockaddr_in6
#include <netinet/in.h>
struct in6_addr {
uint8_t s6_addr[16]; /* 128bit IPv6 address */
};
#define SIN6_LEN
struct sockaddr_in6 {
uint8_t sin6_len; /* length of structure (28) */
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* transport layer port */
uint32_t sin6_flowinfo; /* flow information, undefined */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* set of interfaces for a scope */
};
新的通用套接字地址结构:sockaddr_storage
#include <netinet/in.h>
struct sockaddr_strorage {
uint8_t ss_len; /* length of structure */
sa_family_t ss_family; /* address family: AF_XXXX value */
};
字节序转换接口函数:大小端字节序转换
#include <netinet/in.h>
uint16_t htons (uint16_t host16bitvalue);
uint32_t htonl (uint32_t host32bitvalue); // 此2个函数均返回:网络字节序的值
uint16_t ntohs (uint16_t net16bitvalue);
uint32_t ntohl (uint32_t net32bitvalue); // 此2个函数均返回:主机字节序的值
接口特点:h代表host,n代表network,s代表short,l代表long。
字节操纵接口函数:
#include <string.h>
void bzero (void *dest, size_t nbytes); // 将目标字节串中指定数目的字节置0
void bcopy (const void *src, void *dest, size_t nbytes);
int bcmp (const void *ptr1, const void *ptr2, size_t nbutes);
#include <string.h> // ANSI C函数
void *memset (void *dest, int c, size_t len); // 将目标自揭穿指定数目字节置c
void *memcpy (void *dest, const void *src, size_t nbytes);
int memcmp (const void *ptr1, const void *ptr2, size_t nbytes);
接口特点:b代表字节,mem代表内存。
地址转换函数:
#include <arpa/inet.h>
int inet_aton (const char *strptr, struct in_addr *addrptr); // 返回值:字符串有效为1,否则为0
in_addr_t inet_addr (const char *strptr); // 字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE
char *inet_ntoa (struct in_addr inaddr); // 指向一个点分十进制数字符串的指针
/* 随IPv6出现的新函数×2 */
int inet_pton (int family, const char *strptr, void *addrptr);
const char *inet_ntop (int family, const void *addrptr, char *strptr, size_t len);
【Tips】函数以结构为参数是罕见的,更常见的是以指向结构变量的指针为参数。
2017.08.05
01-03章节完成...
计划安排:吃透这本书,一天三章+源码,并实测代码做当天笔记,CSDN见。
时间安排:计划时间1.5个月 == 6个周末 == 12天。
2017.08.05 第01-03章:TCP/IP简介、传输层、套接字编程简介
2017.08.06 第04-06章:基本TCP编程、TCP客户端/服务器程序、I/O复用
2017.08.12 第07-09章:套接字选项、基本UDP编程、基本SCTP编程
2017.08.13 第10-12章:SCTP客户端/服务器程序例子、名字与地址互换、IPv4和IPv6互操作性
2017.08.19 第13-15章:守护进程和inetd超级服务器、高级I/O、Unix域协议
2017.08.20 第16-18章:非阻塞I/O、ioctl操作、路由套接字
2017.08.26 第19-21章:密钥管理套接字、广播、多播
2017.08.27 第22-24章:高级UDP编程、高级SCTP编程、带外数据
2017.09.02 第25-27章:信号驱动I/O、线程、IP选项
2017.09.03 第28-30章:原始套接字、数据链路访问、客户端/服务器程序设计范式
2017.09.09 第31章-附录:流。附录:IPv4/6协议、调试技术
2017.09.10 整理、总结:思维导图。
>>第1章丶简介
网络应用系统主要构成有两部分:客户端(client)和服务器(server)。举例来说:web服务器程序时一个长时间运行的守护程序,web客户与服务器之间使用TCP通信,TCP转而使用IP通信,IP通过以太网驱动程序的数据链路层通信。
客户端和服务器通常是用户进程,而TCP和IP协议通常是内核中"协议栈"的一部分。
LAN:局域网(内网)
WAN:广域网(外网)
路由器是广域网的架构设备。当下最大的广域网是因特网internet。
/* 一个简单的时间获取客户程序server */ #include <time.h> #include "unp.h" #define MAXLINE 4096 #define LISTENQ 1024 //#define SA struct sockaddr typedef struct sockaddr SA; int main(int argc, char **argv) { int listenfd, connfd; struct sockaddr_in servaddr; char buff[MAXLINE]; time_t ticks; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(1300); /* daytime server */ bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); listen(listenfd, LISTENQ); for ( ; ; ) { connfd = accept(listenfd, (SA *) NULL, NULL); ticks = time(NULL); snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); write(connfd, buff, strlen(buff)); close(connfd); } } /* 一个简单的时间获取客户程序client */ #include "unp.h" #define MAXLINE 1024 //#define SA struct sockaddr typedef struct sockaddr SA; int main(int argc, char **argv) { int sockfd, n; char recvline[MAXLINE + 1]; struct sockaddr_in servaddr; if (argc != 2) printf ("usage: a.out <IPaddress>\n"); if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) printf ("socket error\n"); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(1300); /* daytime server */ if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) printf ("inet_pton error for %s\n", argv[1]); if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0) printf ("connect error\n"); while ( (n = read(sockfd, recvline, MAXLINE)) > 0) { recvline = 0; /* null terminate */ if (fputs(recvline, stdout) == EOF) printf ("fputs error\n"); } if (n < 0) printf ("read error\n"); exit(0); } /* unp.h */ #ifndef __UNP_H__ #define __UNP_H__ #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <sys/time.h> /* timeval{} for select() */ #include <time.h> /* timespec{} for pselect() */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <errno.h> #include <fcntl.h> /* for nonblocking */ #include <netdb.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> /* for S_xxx file mode constants */ #include <sys/uio.h> /* for iovec{} and readv/writev */ #include <unistd.h> #include <sys/wait.h> #include <sys/un.h> /* for Unix domain sockets */ #endif //__UNP_H__
/* 上述例子中原书做了比较安全的接口实现,如 Socket() 伪代码演示:*/ /* include Socket */ int Socket(int family, int type, int protocol) { int n; if ( (n = socket(family, type, protocol)) < 0) err_sys("socket error"); return(n); } /* err_sys() */ void err_sys(const char *fmt, ...) { va_list ap; va_start(ap, fmt); err_doit(1, fmt, ap); va_end(ap); exit(1); } /* err_doit() */ static void err_doit(int errnoflag, const char *fmt, va_list ap) { int errno_save; char buf[MAXLINE]; errno_save = errno; /* value caller might want printed */ vsprintf(buf, fmt, ap); if (errnoflag) sprintf(buf+strlen(buf), ": %s", strerror(errno_save)); strcat(buf, "\n"); fflush(stdout); /* in case stdout and stderr are the same */ fputs(buf, stderr); fflush(stderr); /* SunOS 4.1.* doesn't grok NULL argument */ return; }
【Tips】调用sprintf无法检查目的缓冲区是否溢出,相反,snprintf要求其第二个参数指定目的缓冲区的大小,因此可以确保该缓冲区不溢出。get√
许多网络入侵是由黑客通过发送数据,导致服务器对sprintf的调用使其缓冲区溢出而发生的,必须小心使用的函数还有gets/strcat/strcpy,通常应分别改为调用fgets/strncat/strncpy,更好的替代函数还有strlcat/strlcpy可以确保结果是正确终止的字符串。
OSI模型 open systems interconnection
全称:计算机通信开放系统互连模型。
物理层/数据链路层:主要是设备驱动和网络硬件,通常我们不必关心。
网络层:由IPv4和IPv6这两个协议处理。详细在附录A中。
传输层:即本书所讲的套接字编程接口,从应用层(上3层)进入传输层的接口。
应用层/会话层/表示层:统称为应用层,如web客户端(浏览器)、telnet客户端、web服务器、FTP服务器等。
网络细节的两个基本命令:netstat /
ifconfig
netstat
(1)netstat -ni // 提供网络接口信息,-n输出数值地址而不是反向解析为名字
$ netstat -ni
Kernel Interface table
Iface MTU Met RX-OK RX-ERR RX-DRP RX-OVR TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0 1500 0 15459 0 0 0 10444 0 0 0 BMRU
lo 16436 0 138 0 0 0 138 0 0 0 LRU
lo 环回接口
eth0 以太网接口
(2)netstat -nr // 展示路由表信息,另一种确定接口的方法
$ netstat -nr
内核 IP 路由表
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.31.1 0.0.0.0 UG 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
192.168.31.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
(3)ifconfig eth0 // 获得eth0以太网接口的详细信息
$ ifconfig eth0
eth0 Link encap:以太网 硬件地址 00:0c:29:55:a0:99
inet 地址:192.168.31.205 广播:192.168.31.255 掩码:255.255.255.0
inet6 地址: fe80::20c:29ff:fe55:a099/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:15624 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:10571 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:1468669 (1.4 MB) 发送字节:1070042 (1.0 MB)
中断:19 基本地址:0x2000
// MULTICAST 标志通常指明该接口所在主机支持多播。
(4)ping // 测试ip地址是否联通当前以太网络
$ ping -b 192.168.31.255
PING 192.168.31.255 (192.168.31.255) 56(84) bytes of data.
64 bytes from 192.168.31.255: icmp_req=1 ttl=64 time=0.253 ms
64 bytes from 192.168.31.255: icmp_req=2 ttl=64 time=0.022 ms
64 bytes from 192.168.31.255: icmp_req=3 ttl=64 time=0.029 ms
...
64位体系结构的趋势
原因之一是:在每个进程内部可以由此使用更长的编址长度(即64位指针),从而可以寻址更大的内存空间(超过2^32字节)。
>>第2章丶传输层TCP-UDP-SCTP
TCP:传输控制协议,面相连接,全双工字节流。流套接字。关心:确认、超时、重传等细节。UDP:用户数据报协议,无连接协议。数据报套接字。不保证最终达到目的地。
ICMP:网际控制消息协议,处理路由器和主机之间流通的错误和控制消息。
ARP:地址解析协议,把IPv4地址映射成一个硬件地址。
RARP:反地址解析协议,把一个硬件地址映射成一个IPv4地址。
SCTP(内容略)
TIME_WAIT状态有两个存在的理由:
(1)可靠地实现TCP全双工连接的终止;
(2)允许老的重复分节在网络中消逝。
端口号port:
传输层协议都使用16位整数的端口号来区分不同的进程。
0~1023(自控保留) | 1024~49151(已登记) |49152~65535(动态私有)
通讯对socket pair:
每个TCP分节中都有16bit的端口号和32bit的IPv4地址。
>>第3章丶套接字编程简介
【套接字地址结构】IPv4套接字地址结构:sockaddr_in
#include <netinet/in.h>
struct in_addr {
in_addr_t s_addr; /* 32bit IPv4 address. */
};
struct sockaddr_in {
uint8_t sin_len; /* length of structure (16) */
sa_family_t sin_family; /* AF_INET */
in_port_t sin_port; /* 16bit TCP/UDP port number */
struct in_addr sin_addr; /* 32bit IPv4 address */
char sin_zero[8]; /* unused */
};
通用套接字地址结构:sockaddr
#include <sys/socket.h>
struct sockaddr {
uint8_t sa_len; /* length of structure */
sa_family_t sa_family; /* address family: AF_XXXX value */
char sa_data[14]; /* protocol-specific address */
};
IPv6套接字地址结构:sockaddr_in6
#include <netinet/in.h>
struct in6_addr {
uint8_t s6_addr[16]; /* 128bit IPv6 address */
};
#define SIN6_LEN
struct sockaddr_in6 {
uint8_t sin6_len; /* length of structure (28) */
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* transport layer port */
uint32_t sin6_flowinfo; /* flow information, undefined */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* set of interfaces for a scope */
};
新的通用套接字地址结构:sockaddr_storage
#include <netinet/in.h>
struct sockaddr_strorage {
uint8_t ss_len; /* length of structure */
sa_family_t ss_family; /* address family: AF_XXXX value */
};
/* 输出主机字节序程序 */ #include "unp.h" int main(int argc, char **argv) { union { short s; char c[sizeof(short)]; } un; un.s = 0x0102; printf("This host byteorder is: "); if (sizeof(short) == 2) { if (un.c[0] == 1 && un.c[1] == 2) printf("big-endian\n"); else if (un.c[0] == 2 && un.c[1] == 1) printf("little-endian\n"); else printf("unknown\n"); } else printf("sizeof(short) = %d\n", sizeof(short)); exit(0); }
字节序转换接口函数:大小端字节序转换
#include <netinet/in.h>
uint16_t htons (uint16_t host16bitvalue);
uint32_t htonl (uint32_t host32bitvalue); // 此2个函数均返回:网络字节序的值
uint16_t ntohs (uint16_t net16bitvalue);
uint32_t ntohl (uint32_t net32bitvalue); // 此2个函数均返回:主机字节序的值
接口特点:h代表host,n代表network,s代表short,l代表long。
字节操纵接口函数:
#include <string.h>
void bzero (void *dest, size_t nbytes); // 将目标字节串中指定数目的字节置0
void bcopy (const void *src, void *dest, size_t nbytes);
int bcmp (const void *ptr1, const void *ptr2, size_t nbutes);
#include <string.h> // ANSI C函数
void *memset (void *dest, int c, size_t len); // 将目标自揭穿指定数目字节置c
void *memcpy (void *dest, const void *src, size_t nbytes);
int memcmp (const void *ptr1, const void *ptr2, size_t nbytes);
接口特点:b代表字节,mem代表内存。
地址转换函数:
#include <arpa/inet.h>
int inet_aton (const char *strptr, struct in_addr *addrptr); // 返回值:字符串有效为1,否则为0
in_addr_t inet_addr (const char *strptr); // 字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE
char *inet_ntoa (struct in_addr inaddr); // 指向一个点分十进制数字符串的指针
/* 随IPv6出现的新函数×2 */
int inet_pton (int family, const char *strptr, void *addrptr);
const char *inet_ntop (int family, const void *addrptr, char *strptr, size_t len);
【Tips】函数以结构为参数是罕见的,更常见的是以指向结构变量的指针为参数。
/* 预防万一,不让实现返回一个不足的字节计数值,封装read和write代码: */ ssize_t readn(int fd, void *vptr, size_t n) { size_t nleft; ssize_t nread; char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; /* and call read() again */ else return(-1); } else if (nread == 0) break; /* EOF */ nleft -= nread; ptr += nread; } return(n - nleft); /* return >= 0 */ } ssize_t writen(int fd, const void *vptr, size_t n) { size_t nleft; ssize_t nwritten; const char *ptr; ptr = vptr; nleft = n; while (nleft > 0) { if ( (nwritten = write(fd, ptr, nleft)) <= 0) { if (nwritten < 0 && errno == EINTR) nwritten = 0; /* and call write() again */ else return(-1); /* error */ } nleft -= nwritten; ptr += nwritten; } return(n); }
2017.08.05
01-03章节完成...
相关文章推荐
- 《Unix网络编程》卷1:套接字联网API(第3版):基本TCP编程、TCP客户端/服务器程序、I/O复用
- 《Unix网络编程》卷1:套接字联网API(第3版):非阻塞I/O、ioctl操作、路由套接字
- 《Unix网络编程》卷1:套接字联网API(第3版):套接字选项、基本UDP编程
- 《Unix网络编程》卷1:套接字联网API(第3版):广播、多播、信号驱动I/O、线程
- 《Unix网络编程》卷1:套接字联网API(第3版):名字与地址互换、IPv4和IPv6互操作性
- 《Unix网络编程》卷1:套接字联网API(第3版):守护进程和inetd超级服务器、高级I/O、Unix域协议
- UNIX网络编程第3版.第1卷,套接字联网API:英文影印版
- 图灵社区 : 图书 : UNIX网络编程 卷1:套接字联网API(英文版•第3版)
- UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订)
- Unix网络编程--卷一:套接字联网API 读书笔记
- UNIX网络编程卷一:套接字联网API(学习笔记一)
- 套接字联网API之一 TCP套接字
- UNIX网络编程卷一 笔记 第三章 套接字编程简介
- UNIX网络编程之套接字API
- Unix网络编程(三)--套接字编程简介
- UNP-UNIX网络编程 第三章:套接字编程简介
- Unix网络编程卷一套接字联网API环境搭建
- Socket套接字API简介
- Unix网络编程 卷1 第3章:套接字编程简介(套接字地址结构)
- UNIX网络编程---套接字编程简介(三)