Linux网络编程笔记(修订版).pdf
《Linux网络编程笔记(修订版).pdf》由会员分享,可在线阅读,更多相关《Linux网络编程笔记(修订版).pdf(22页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 Linux 网络编程笔记网络编程笔记(修订版修订版)收藏收藏 新一篇新一篇:C+中中 的内存错误与泄漏的内存错误与泄漏|旧一篇旧一篇:Linux 下下 C+程序调试及除错方法程序调试及除错方法 我的网络编程笔记,因为最近又要做Linux下的网络编程,故重新修订,其中一些内容参考了文末的链接及文章 1.基本概念 1 2.基本接口 2 2.1.打开一个 socket 4 2.2.将 socket 绑定定指定的端口bind 4 2.3.侦听 socketlisten(服务器端)4 2.4.等待接收请求accept(服务器端)4 2.5.连接到 socketconnect 5 2.6.利用 sock
2、et 传输数据 5 2.6.1.read 和 write 5 2.6.2.recv 和 send 5 2.6.3.recvfrom 和 sendto 6 2.6.4.recvmsg 和 sendmsg 6 2.7.套接字的关闭 close/shutdown 6 3.最常用的服务器模型.7 3.1.循环服务器:7 3.1.1.循环服务器之 UDP 服务器 7 3.1.2.循环服务器之 TCP 服务器 7 3.2.并发服务器 7 3.2.1.并发服务器之 TCP 服务器 7 3.2.2.并发服务器之多路复用 I/O 8 3.2.3.并发服务器之 UDP 服务器 9 4.实例分析 10 5.参考链接
3、及文章 12 1.基本概念基本概念 说到网络编程,不得不先提到 OSI 参考模型,其七层模型从下到上分别为 1.物理层(Physical Layer,PH)2.数据链路层(Data Link Layer,DL)3.网络层(Network Layer,N)4.运输层(Transport Layer,T)5.会话层(Session Layer,S)6.表示层(Presentation Layer,P)7.应用层(Application Layer,A)现在最流行的网络协议无疑就是 TCP/IP(Transmission Control Protocol/Internet Protocol)协议.注
4、:IP(Internet Protocol),网际协议;IP 是 TCP/IP 的最底层,高层协议都要转化为 IP 包,IP 包含了源地址和目的地址,路由决策也发生在 IP 层;ICMP(Internet Control Message Protocol),网际报文协议;它包括了数据包的错误、控制等相关信息。比如 ping 命令就是利用 ICMP 来测试一个网络的连接情况的工具;TCP(Transmission Control Protocol),传输控制协议。TCP 运行在 IP 之上,是基于数据流连接和面向的协议,应用程序把数据要经过 TCP/IP 的分割成若干包,这样数据就以字节流发送和
5、接收,到达目的地后,TCP/IP 再按顺序进行组装。TCP/IP 要保证机器与机器之间的连接的可靠性,还要有纠错。TCP 是否被选择,取决于应用程序或服务;UDP(User Datagram Protocol),用户数据报协议,象 TCP 一样运行在 IP 之上,是基于数据报或分组的协议,UDP/IP 可以直接发送和接收数据报文,而不必做验证,这一点与 TCP/IP 不同。TCP 是否被选择,取决于应用程序或服务;2.基本接口基本接口 以 Unix/Linux 平台为例,系统会建立许多网络服务程序$netstat-a Proto Recv-Q Send-Q Local Address Fore
6、ign Address State tcp 0 0 *:1975 :LISTEN udp 0 0 *:1978 :tcp 0 0 MYServer:34320 192.168.1.2:1521 ESTABLISHED 以上可以看到有三个网络连接,一个是 TCP 接连在 1975 端口侦听,一个 UDP 连接在 1978 端口,另外一个 TCP 连接是连接到 DB 的 我们可以看出,客户端程序需要通过”主机名:端口号”与服务器建立连接.主机名其实就是 IP 地址.上面的 MYServer 其实是 192.168.1.3,这样的主机名与 IP 的对应关系由本机的 host文件或 DNS 服务器解析
7、提供.$more/etc/hosts#that require network functionality will fail.127.0.0.1 localhost.localdomain localhost 192.168.1.3 MYServer$more/etc/resolv.conf nameserver 192.168.1.1 当然,我们在编程时无需查询这些文件或服务器,系统提供了 API:gethostbyname/gethostbyaddr#include extern int h_errno;struct hostent*gethostbyname(const char*na
8、me);#include /*for AF_INET*/struct hostent*gethostbyaddr(const char*addr,int len,int type);它们会返回一个指针,指向如下结构的对象 struct hostent char h_name;/official name*/char *h_aliases;/*alias list*/int h_addrtype;/*address type*/int h_length;/*address length*/char *h_addr_list;/*address list*/;#define h_addr h_ad
9、dr_list0/*backward compatibility*/h_addr_list 是一个与域名对应的 IP 地址的列表,勤快的程序员会依次尝试连接列表中返回的 IP 地址 懒惰的程序员只会用 h_addr,h_addr_list 列表中的第一个 IP 地址 不同的应用程序使用不同的端口和协议,比如常用的 ftp 就用 21 端口和 tcp 协议$more/etc/services#service-name port/protocol aliases.#comment ftp 21/tcp ftp 21/udp fsp fspd ssh 22/tcp#SSH Remote Login
10、Protocol ssh 22/udp#SSH Remote Login Protocol telnet 23/tcp telnet 23/udp#24-private mail system smtp 25/tcp mail smtp 25/udp mail.同样,程序中是无需查询这个文件的,Unix 提供了 getservbyname#include struct servent*getservbyname(const char*name,const char*proto);返回 struct servent char s_name;/official service name*/char
11、 *s_aliases;/*alias list*/int s_port;/*port number*/char s_proto;/protocol to use*/知道主机名(IP)和端口号,我们就可以编写在这台主机的运行的或是连接到它的网络应用程序了 Unix/Linux 系统中是通过提供套接字(socket)来进行网络编程的.网络程序通过socket 和其它几个函数的调用,会返回一个通讯的文件描述符,我们 可以将这个描述符看成普通的文件的描述符来操作,可以通过向描述符读写操作实现网络之间的数据交流.2.1.打开一个打开一个 socket int socket(int domain,int
12、 type,int protocol)domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX 和 AF_INET 等).AF_UNIX 只能够用于单一的 Unix 系统进程间通信,而 AF_INET 是针对 Internet 的,因而可以允许在远程主机之间通信(当我们 mansocket 时发现 domain 可选项是 PF_*而不是 AF_*,因为 glibc 是 posix 的实现所以用 PF 代替了 AF,不过我们都可以使用的).type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等)SOCK_STREAM表明我们用的是TCP协议,这样会
13、提供按顺序的,可靠,双向,面向连接的比特流.SOCK_DGRAM 表明我们用的是 UDP 协议,这样只会提供定长的,不可靠,无连接的通信.protocol:由于我们指定了 type,所以这个地方我们一般只要用 0 来代替就可以了socket 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看 errno 可知道出错的详细情况.2.2.将将 socket 绑定定指定的端口绑定定指定的端口bind int bind(int sockfd,struct sockaddr*my_addr,int addrlen)sockfd:是由 socket 调用返回的文件描述符.addrlen:是
14、sockaddr 结构的长度.my_addr:是一个指向 sockaddr 的指针.在中有 sockaddr 的定义 structsockaddr unisgnedshortas_family;charsa_data14;不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(structsockaddr_in)来代替.在中有 sockaddr_in 的定义 structsockaddr_in unsignedshortsin_family;unsignedshortintsin_port;structin_addrsin_addr;unsignedcharsin_zero8;我们主
15、要使用 Internet 所以 sin_family 一般为 AF_INET,sin_addr 设置为 INADDR_ANY 表示可以和任何的主 机通信,sin_port 是我们要监听的端口号.sin_zero8是用来填充的.bind 将本地的端口同 socket 返回的文件描述符捆绑在一 起.成功是返回 0,失败的情况和 socket 一样 2.3.侦听侦听 socketlisten(服务器端服务器端)int listen(int sockfd,int backlog)sockfd:是 bind 后的文件描述符.backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时,使用这
16、个表示可以介绍的排队长度.listen 函数将 bind 的文件描述符变为监听套接字.返回的情况和 bind 一样.2.4.等待接收请求等待接收请求accept(服务器端服务器端)int accept(int sockfd,struct sockaddr*addr,int*addrlen)sockfd:是 listen 后的文件描述符.addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了.bind,listen 和 accept 是服务器端用的函 数,accept 调用时,服务器端的程序会一直阻塞到有一个客户程序发出了连接.accept 成功时返回最后的服务器端的
17、文件描述符,这个时候服务器 端可以向该描述符写信息了.失败时返回-1 2.5.连接到连接到 socketconnect int connect(int sockfd,struct sockaddr*serv_addr,int addrlen)sockfd:socket 返回的文件描述符.serv_addr:储存了服务器端的连接信息.其中 sin_add 是服务端的地址 addrlen:serv_addr 的长度 connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同服务端通讯的文件描述符失败时返回-1.2.6.利用利用 socket 传输数据传输数据 2.6.1.read
18、 和和 write ssize_t read(int fd,void*buf,size_t nbyte)read 函数是负责从 fd 中读取内容.当读成功时,read 返回实际所读的字节数,如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误.如果错误为 EINTR 说明读是由中断引起的,如果是 ECONNREST 表示网络连接出了问题.和上面一样,我们也写一个自己的读函数.ssize_t write(int fd,const void*buf,size_t nbytes)write 函数将 buf 中的 nbytes 字节内容写入文件描述符 fd.成功时返回写的字节数.失败
19、时返回-1.并设置 errno 变量.在网络程序中,当我们向套接字文件描述符写时有俩种可能.1)write 的返回值大于 0,表示写了部分或者是全部的数据.2)返回的值小于 0,此时出现了错误.我们要根据错误类型来处理.如果错误为 EINTR 表示在写的时候出现了中断错误.如果为 EPIPE 表示网络连接出现了问题(对方已经关闭了连接).为了处理以上的情况,我们自己编写一个写函数来处理这几种情况.2.6.2.recv 和和 send 和 read 和 write 差不多.不过它们提供 了第四个参数来控制读写操作.int recv(int sockfd,void*buf,int len,int
20、flags)int send(int sockfd,void*buf,int len,int flags)前面的三个参数和 read,write 一样,第四个参数可以是 0 或者是以下的组合 _|MSG_DONTROUTE|不查找路由表|MSG_OOB|接受或者发送带外数据|MSG_PEEK|查看数据,并不从系统缓冲区移走数据|MSG_WAITALL|等待所有数据|-|MSG_DONTROUTE:是 send 函数使用的标志.这个标志告诉 IP 协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程序里面.MSG_OOB:表示可以接收和发送带外的数据.关于带外数据我们
21、以后会解释的.MSG_PEEK:是 recv 函数的使用标志,表示只是从系统缓冲区中读取内容,而不清楚系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志.MSG_WAITALL 是 recv 函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候 recv 回一直阻塞,直到指定的条件满足,或者是发生了错误.1)当读到了指定的字节时,函数正常返回.返回值等于 len 2)当读到了文件的结尾时,函数正常返回.返回值小于 len 3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)如果 flags 为 0,则和 read,w
22、rite 一样的操作.还有其它的几个选项,不过我们实际上用的很少,可以查看 Linux Programmers Manual 得到详细解释.2.6.3.recvfrom 和和 sendto int recvfrom(int sockfd,void*buf,int len,unsigned int flags,struct sockaddr*from int*fromlen)int sendto(int sockfd,const void*msg,int len,unsigned int flags,struct sockaddr*to int tolen)sockfd,buf,len的意义和r
23、ead,write一样,分别表示套接字描述符,发送或接收的缓冲区及大小.recvfrom 负责从 sockfd 接收数据,如果 from 不是 NULL,那么在 from 里面存储了信息来源的情况,如果对信息的来源不感兴趣,可以将 from 和 fromlen 设置为 NULL.sendto 负责向 to 发送信息.此时在 to 里面存储了收信息方的详细资料.2.6.4.recvmsg 和和 sendmsg recvmsg 和 sendmsg 可以实现前面所有的读写函数的功能.int recvmsg(int sockfd,struct msghdr*msg,int flags)int send
24、msg(int sockfd,struct msghdr*msg,int flags)struct msghdr void*msg_name;int msg_namelen;struct iovec*msg_iov;int msg_iovlen;void*msg_control;int msg_controllen;int msg_flags;struct iovec void iov_base;/缓冲区开始的地址*/size_t iov_len;/*缓冲区的长度*/msg_name 和 msg_namelen 当套接字是非面向连接时(UDP),它们存储接收和发送方的地址信息.msg_name
25、 实际上是一个指向struct sockaddr 的指针,msg_name是结构的长度.当套接字是面向连接时,这两个值应设为NULL.msg_iov和msg_iovlen指出接受和发送的缓冲区内容.msg_iov 是一个结构指针,msg_iovlen 指出这个结构数组的大小.msg_control和msg_controllen 这两个变量是用来接收和发送控制数据时的msg_flags 指定接受和发送的操作选项.和 recv,send 的选项一样 2.7.套接字的关闭套接字的关闭 close/shutdown 关闭套接字有两个函数 close 和 shutdown.用 close 时和我们关闭文
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 网络 编程 笔记 修订版
限制150内