0-Linux 网络编程修炼指南——内功心法

学习交流群:

Linux 学习交流群 610441700

  • 说明:本系列文章并不能取代 《UNP》这本旷世之作,文章中难免有错误与不足之处,希望读者们遇到有疑问的地方可以加群互相交流,共同进步。写这一系列文章的目的有三个:一是为了提升自己对 linux 的理解,二是锻炼自己能够把知识点讲清楚,三是希望能更好的帮助基础薄弱的同学能够学习 UNP。

  • 在学习 unix 网络编程前,请你务必对 《unix 环境编程》(缩写 apue)有所了解,因为网络编程中大量使用了 apue 中的基础知识。在本专栏中,不会对这些基础知识做重复讲解,如果你对 apue 不了解,可以先阅读这本书,或者移步至《Linux 环境编程》,掌握这些基础知识。

  • 此博客的主要参考书:《unix 网络编程》、《tcp/ip 详解》卷一、《计算机网络》第五版、《CCNA 学习指南》。我不希望读者只看博客不看书,如果你只看博客,你学到的知识也只是我掌握的一部分;博客的初衷只是给你一个引导,一份入门教程。最佳的学习方式是根据博客的引导,认真把 unp 和 tcp/ip 详解读完,我相信你的水平一定会超过我。另外,如果你在读书的过程中发现对部分知识点的理解和我不一致,可以直接在文章下面留言,我希望这个系列的文章是大家共同完成的结果,而不是靠我一个人。

  • 关于博客中的程序。博客中使用大量的代码作为示例讲解,有时候为了能突出重点,删除了很多无关的条件判断,错误处理等过程。如果你想采用这些代码,务必自行扩充代码,让你的程序更加健壮。典型的比如你可能会见到申请内存不释放,有些函数可能会返回失败等等,你都需要自行去扩充。

  • 相关工具和源码地址:

一、网络协议基础

001 网络协议基础
002 网络编程学习环境搭建
003 第一次抓包

二、TCP 协议(基础)

004 TCP 协议(抓包)
005 TCP 协议(基础)
006 TCP 协议(序号和确认号)
007 TCP 协议(建立连接)
008 TCP 协议(断开连接)
009 TCP 协议(连接异常)
010 TCP 协议(MSS)
011 TCP 协议(状态机)
012 TCP 协议(TIME_WAIT 状态)
013 TCP 协议(FIN_WAIT2)
014 TCP 协议(RST)
015 TCP 协议(半打开)
016 TCP 协议(同时关闭)

三、TCP 协议(进阶)

017 TCP 协议(迟到的 ACK —— Windows )
018 TCP 协议(迟到的 ACK—— Linux)
019 TCP 协议(Nagle)
020 TCP 协议(滑动窗口——基础)
021 TCP 协议(滑动窗口——抓包分析)
022 TCP 协议(PSH 标志)
023 TCP 协议(紧急指针)
024 TCP 协议(拥塞控制)
025 TCP 协议(慢启动——观察)
026 TCP 协议(慢启动与拥塞避免)
027 TCP 协议(快重传与快恢复)
028 TCP 协议(超时与重传)
029 TCP 协议(持续定时器)
030 TCP 协议(糊涂窗口综合症)
031 TCP 协议(保活定时器)

四、简单的回射服务器

032 网络编程概述
033 基于 TCP 的回射服务器
034 异常处理(accept 返回前连接中止)
035 并发服务器(多进程)
036 多进程并发服务器(僵尸进程与信号处理)
037 多进程并发服务器(并发测试)
038 连接断开异常(服务器进程终止)
039 连接断开异常(引发 SIGPIPE)
040 连接异常(服务器崩溃)
041 连接异常(服务器崩溃后重启)

五、IO 多路复用

042 IO 多路复用
043 使用 select 改进客户端
044 批量输入异常
045 批量输入异常处理(shutdown 函数)
046 不要将 IO 复用与 stdio 混合
047 将多进程并发服务器改成 IO 复用
048 拒绝服务型攻击(Denial-of-Service Attacks)
049 使用 poll 改写服务器
050 使用 epoll 改写服务器

六、套接字选项

051 套接字选项(概述)
052 打印套接字选项
053 套接字选项(SO_REUSEADDR)
054 套接字选项(SO_LINGER)
055 套接字选项(TCP_NODELAY)
056 套接字选项(TCP_CORK)
057 其它套接字选项

七、基本的 UDP 套接字编程

058 基于 UDP 协议的回射服务器
059 UDP 数据报丢失
060 客户端也能做服务器?
061 面向连接的 UDP
062 UDP 与 connect
063 不可靠的 UDP 协议
064 TCP/UDP 混合服务器

八、高级 I/O 函数

065 高级 I/O 函数与技术
066 套接字超时(alarm)
067 套接字超时(select)
068 套接字超时(SO_RCVTIMEO 与 SO_SNDTIMEO)
069 recv 和 send 函数
070 散布读、聚集写
071 recvmsg 和 sendmsg 函数
072 套接字与标准I/O

九、Unix 域协议

073 进程间传递描述符(概述)
074 Unix 域套接字地址结构
075 Unix 域字节流回射服务器
076 抽象 unix 域套接字地址
077 Unix 域数据报回射服务器
078 socketpair 函数
079 辅助数据
080 进程间传递描述符(策略)
081 凭证的发送与接收

十、非阻塞 I/O

082 再议 select 版回射客户端
083 非阻塞 I/O
084 使用非阻塞 I/O 改写回射客户端
085 使用多线程改写回射客户端
086 时间获取客户端
087 非阻塞 connect
088 非阻塞 connect 版本的 web 客户程序
089 非阻塞 accept

十一、ICMP 协议

090 IP 协议(基础)
091 接收 IP 数据报
092 ICMP 协议(基础)
093 接收 ICMP 报文
094 ICMP 协议(回显请求与应答)
095 PING 命令实现
096 ICMP 协议(时间戳请求与应答)
097 ICMP 协议(端口不可达)
098 traceroute 程序

十二、广播

099 IPv4 地址
100 广播
101 指向子网的广播
102 受限广播地址
103 使用广播的 UDP 回射客户端
104 信号引起的竞争错误

十二、多播

105 多播(基础)
106 多播地址
107 使用多播的 UDP C/S 程序

十三、接口操作

108 网络接口
109 获取接口信息(一)
110 获取接口信息(二)

十四、高级 UDP 编程

111 UDP 数据报被截断
112 封装 recvFromFlags
113 给 udp 增加可靠性(一)
114 给 udp 增加可靠性(二)
115 并发的 UDP 服务器

待更新……

netstat 命令 netstat是用来显示网络的连接,路由表和接口统计等网络的信息.netstat有许多的 选项 我们常用的选项是 -an 用来显示详细的网络状态.至于其它的选项我们可以使用帮 助手册获得详细的情况. telnet telnet是一个用来远程控制的程序,但是我们完全可以用这个程序来调试我们的服务端程 序的. 比如我们的服务器程序在监听 8888 端口,我们可以用 telnet localhost 8888来查 看服务端的状况. TCP(Transfer Control Protocol)传输控制协议是一种面向连接的协议,当我们的网络程 序使用 这个协议的时候,网络可以保证我们的客户端和服务端的连接是可靠的,安全的. UDP(User Datagram Protocol)用户数据报协议是一种非面向连接的协议,这种协议并不能保证 我们的网络程序的连接是可靠的,所以我们现在编写的程序一般是采用 TCP协议的 socket int socket(int domain, int type,int protocol) domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和AF_INET 等). AF_UN IX 只能够用于单一的 Unix 系统进程间通信,而 AF_INET 是针对Internet的,因而可以允许在 远程 主机之间通信(当我们 man socket 时发现 domain 可选项是 PF_*而不是AF_*,因为 glibc 是 posix 的实现 所以用 PF代替了 AF,不过我们都可以使用的). type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等) SOCK_STREAM 表明 我们用的是 TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是 UDP协议,这样只会提供定长的,不可靠,无连接的通信. protocol:由于我们指定了 type,所以这个地方我们一般只要用 0 来代替就可以了 sock et 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看 errno 可知道出错 的详细情况. bind int bind(int sockfd, struct sockaddr *my_addr, int addrlen) sockfd:是由socket调用返回的文件描述符. addrlen:是 sockaddr结构的长度. my_addr:是一个指向 sockaddr的指针. 在<linux/socket.h>;中有 sockaddr的定义 struct sockaddr{ unisgned short as_family; char sa_data[14]; }; 不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sock addr_in) 来代替.在<linux/in.h>;中有 sockaddr_in 的定义 struct sockaddr_in{ unsigned short sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; 我们主要使用 Internet所以 sin_family 一般为 AF_INET,sin_addr设置为 INADDR_ANY 表 示可以 和任何的主机通信,sin_port 是我们要监听的端口号.sin_zero[8]是用来填充的 .. bind 将本地的端口同 socket返回的文件描述符捆绑在一起.成功是返回 0,失败的情况和 socket一样 listen int listen(int sockfd,int backlog) sockfd:是 bind 后的文件描述符. backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示 可以介绍的排队长度. listen函数将 bind 的文件描述符变为监听套接字.返回的情况和 b ind 一样. accept int accept(int sockfd, struct sockaddr *addr,int *addrlen) sockfd:是 listen后的文件描述符. addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,li sten和 accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个 客户程序发出了连接. accept 成功时返回最后的服务器端的文件描述符,这个时候服务 器端可以向该描述符写信息了. 失败时返回-1 connect int connect(int sockfd, struct sockaddr * serv_addr,int addrlen) sockfd:socket返回的文件描述符. serv_addr:储存了服务器端的连接信息.其中 sin_add 是服务端的地址 addrlen:serv_addr的长度 connect函数是客户端用来同服务端连接的.成功时返回 0,sockfd 是同服务端通讯的文件 描述符 失败时返回-1. 总的来说网络程序是由两个部分组成的--客户端和服务器端.它们的建立步骤一般是: 服务器端 socket-->;bind-->;listen-->;accept 客户端 socket-->;connect 字节转换函数 在网络上面有着许多类型的机器,这些机器在表示数据的字节顺序是不同的, 比如 i386 芯 片是低字节在内存地址的低端,高字节在高端,而 alpha 芯片却相反. 为了统一起来,在 Li nux 下面,有专门的字节转换函数. unsigned long int htonl(unsigned long int hostlong) unsigned short int htons(unisgned short int hostshort) unsigned long int ntohl(unsigned long int netlong) unsigned short int ntohs(unsigned short int netshort) 在这四个转换函数中,h 代表 host, n 代表 network.s 代表 short l 代表 long 第一个函 数的意义是将本机器上的 long 数据转化为网络上的 long. 其他几个函数的意义也差不多 .. IP 和域名的转换 在网络上标志一台机器可以用 IP或者是用域名.那么我们怎么去进行转换呢? struct hostent *gethostbyname(const char *hostname) struct hostent *gethostbyaddr(const char *addr,int len,int type) 在<netdb.h>;中有 struct hostent的定义 struct hostent{ char *h_name; /* 主机的正式名称 */ char *h_aliases; /* 主机的别名 */ int h_addrtype; /* 主机的地址类型 AF_INET*/ int h_length; /* 主机的地址长度 对于IP4 是 4 字节 32 位*/ char **h_addr_list; /* 主机的 IP地址列表 */ } #define h_addr h_addr_list[0] /* 主机的第一个 IP地址*/ gethostbyname 可以将机器名(如 linux.yessun.com)转换为一个结构指针.在这个结构里 面储存了域名的信息 gethostbyaddr可以将一个 32 位的 IP地址(C0A80001)转换为结构指针. 这两个函数失败时返回 NULL 且设置 h_errno 错误变量,调用 h_strerror()可以得到详细的 出错信息 字符串的 IP 和 32位的 IP 转换. 在网络上面我们用的 IP都是数字加点(192.168.0.1)构成的, 而在 struct in_addr结构中 用的是 32 位的 IP, 我们上面那个 32 位IP(C0A80001)是的 192.168.0.1 为了转换我们可以 使用下面两个函数 int inet_aton(const char *cp,struct in_addr *inp) char *inet_ntoa(struct in_addr in) 函数里面 a 代表 ascii n 代表 network.第一个函数表示将 a.b.c.d 的 IP转换为 32 位的 I P,存储在 inp 指针里面.第二个是将 32 位 IP转换为 a.b.c.d 的格式 服务信息函数 在网络程序里面我们有时候需要知道端口.IP和服务信息.这个时候我们可以使用以下几 个函数 int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen) int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen) struct servent *getservbyname(const char *servname,const char *protoname) struct servent *getservbyport(int port,const char *protoname) struct servent { char *s_name; /* 正式服务名 */ char **s_aliases; /* 别名列表 */ int s_port; /* 端口号 */ char *s_proto; /* 使用的协议 */ } 一般我们很少用这几个函数.对应客户端,当我们要得到连接的端口号时在 connect调用成 功后使用可得到 系统分配的端口号.对于服务端,我们用 INADDR_ANY 填充后,为了得到连 接的 IP我们可以在 accept 调用成功后 使用而得到IP地址. 在网络上有许多的默认端口和服务,比如端口 21 对 ftp80 对应 WWW.为了得到指定的端口号 的服务 我们可以调用第四个函数,相反为了得到端口号可以调用第三个函数.
评论 51
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值