1、1 / 141. 基本概念1.1. 什么是协议计算机通信网络中两台计算机之间进行通信所必须共同遵守的规定或规则。1.2. TCP/IP 协议的四层体系结构应用层(Telnet、FTP、HTTP、DNS、SNMP、SMTP )传输层(TCP、UDP)网络层(IP 、ICMP、IGMP)链路层(以太网、令牌环网、FDDI、IEEE802.3)1.3. IP 地址的结构、分类、多播( 或称组播)目前使用的 IP 协议为 ipv4(即 IP 协议的第四版) ,使用 32 位二进制表示结构:网络号 主机号分类:A,B,C,D,E 等五类地址,见下表类别 类标识 地址范围 第一字节 网络地址长度 最大网络
2、数 最大主机数 选用范围A 类 0 1126 1 字节 126 16777214 大型网络B 类 10 128191 2 字节 16382 65534 中型网络C 类 110 192223 3 字节 2097150 254 小型网络D 类 1110 224.0.0.0 239.255.255.255 224239 多点播送E 类 11110 240247 保留地址直接广播地址:主机号为全 1 的 IP 地址;多播:1) 一对多的通信,一个源点发送到多个终点;2) 标识一个多播的标识符即为一个 D 类地址;3) 使用 IGMP(网际组管理协议)协议,首部的协议字段值为 24) 多播地址只能用于目
3、的地址,不能用于源地址;5) 对多播数据报不产生 ICMP 差错报文;多播分两种:局域网范围的硬件多播;因特网范围的多播,因为大部分主机是通过局域网接入到因特网的,因此在多播的最后阶段,还是要在局域网范围内进行硬件多播。1.4. 多播(或称组播)IP 地址,如何映射到 MAC 地址组播 IP 地址,即 D 类地址,范围是 224.0.0.0239.255.255.255;MAC 地址的前 25 位是固定的,后 23 位与 IP 地址的后 23 位相同IP 地址 1110 yyyy yxxx xxxx xxxx xxxx xxxx xxxxMAC 地址 0000 0001 0000 0000 0
4、101 1110 0xxx xxxx xxxx xxxx xxxx xxxx(16 进制) 0 1 0 0 5 e1.5. 标识网络中的两个通信的进程或一条连接2 / 14五元组(协议,本地 IP 地址,本地端口号,远程 IP 地址,远程端口号)1.6. socket(套接字)的三种协议类型及其应用场合socket(int af, int type, int protocol)函数中,当第二个参数为AF_INET 时,第三个参数 type 的值可以为:SOCK_STREAM(流式套接字) 、SOCK_DGRAM(数据报套接字) 、SOCK_RAW(原始套接字) ,详情见下表:协议类型 应用场合
5、 socket 函数中的 protocol 值SOCK_STREAM TCP IPPROTO_TCPSOCK_DGRAM UDP IPPROTO_UDPSOCK_RAW raw sockets IPPROTO_ICMP1.7. 写代码:创建 socket1) TCPSOCKET tcpSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);2) UDPSOCKET udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);3) 原始套接字SOCKET icmpSocket = socket(AF_IN
6、ET, SOCK_RAM, IPPROTO_ICMP);1.8. TCP 的 C/S 通信模型WSAStartup()socket()bind()listen()accept()send()/recv()closesocket()WSACleanup()WSAStartup()socket()connect()closesocket()WSACleanup()Server Client初始化 winsock创建 socketsocket 与本地ip、端口绑定监听客户端的连接接受客户端的连接关闭 socket释放 winsock 资源建立与服务器的连接send()/recv()1.9. UDP
7、的 C/S 通信模型3 / 14WSAStartup()socket()bind()sendto()/recvfrom()closesocket()WSACleanup()WSAStartup()socket()closesocket()WSACleanup()Server Client初始化 winsock创建 socketsocket 与本地ip、端口绑定关闭 socket释放 winsock 资源sendto()/recvfrom()bind()1.10. 服务器端的两种绑定方式的区别IP 地址 端口参数方式 1 INADDR_ANY 非 0 值 所有网卡都可接收连接,所有人都 可连接方
8、式 2 IP 地址 非 0 值 某网卡接收,指定人群连接函数 bind 原型为: int bind(SOCKET s, const struct sockaddr *name, int namelen)代码如下:SOCKET listenSocket;struct sockaddr_in service;listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);/绑定方式 1:service.sin_port = htons(9999);service.sin_addr.s_addr = inet_addr(INADDR_ANY);/绑定
9、方式 2:service.sin_port = htons(9999);service.sin_addr.s_addr = inet_addr(“127.0.0.1“);bind(listenSocket, (SOCKADDR *) 1.11. 函数 listen 中第二个参数的含义函数原型为:int listen(SOCKET s, int backlog);第二个参数 backlog:设置等待连接队列的最大长度,若设置为 SOMAXCONN,则表示可用的最大长度。1.12. 在网络通信中,怎样传输一个字符串(可能含有汉字)4 / 14统一字符编码为 UTF-8,传输时,先传输字符串的长度,
10、再传输字符串内容。2. 程序设计2.1. 写代码:两个线程,交替打印数字步骤:WSAStartup(),初始化winsocksocket(),创建 TCP型的socketbind(),绑定服务器的IP、 PORTlisten(),监听 socketwhile (continue) accept(),接收客户端的连接创建新线程,传入通信用的socket,与客户端进行通信closesocket(),关闭socketWSACleanup(),释放winsock资源代码:/* main.cpp* 多线程程序* 两个线程,分别打印线程号+ 数字*/#include #include #include u
11、nsigned int CALLBACK myThread(void *p);int main() HANDLE handles2; handles0=(HANDLE)_beginthreadex(NULL, 0, myThread, NULL, 0, NULL);handles1=(HANDLE)_beginthreadex(NULL, 0, myThread, NULL, 0, NULL);WaitForMultipleObjects(2, handles, true, INFINITE);CloseHandle(handles0);CloseHandle(handles1);return
12、 0;unsigned int CALLBACK myThread(void *p)5 / 14for(int i = 1;i #include #include #pragma comment(lib, “ws2_32“)/线程:处理一个客户机的网络通信。unsigned int CALLBACK tcpThread(void *p);int main() int iResult;WORD wVersionRequested;WSADATA wsaData;SOCKET listenSocket;SOCKET acceptSocket;struct sockaddr_in service;H
13、ANDLE handles10;wVersionRequested = MAKEWORD(2, 2);/* Initializing Winsock */iResult = WSAStartup(wVersionRequested, if (iResult != 0) printf(“WSAStartup failed with error: %dn“, iResult);return -1;/* create a tcp socket */listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (listenSocket = I
14、NVALID_SOCKET) printf(“create socket falied with error: %dn“, WSAGetLastError();WSACleanup();return -1;/* bind the socket */service.sin_family = AF_INET;service.sin_port = htons(9999);service.sin_addr.s_addr = inet_addr(“127.0.0.1“);iResult = bind(listenSocket, (SOCKADDR *) if (iResult = SOCKET_ERRO
15、R) printf(“bind failed with error:%dn“, WSAGetLastError();7 / 14closesocket(listenSocket);WSACleanup();return -1;/* listen for incoming connection requests */iResult = listen(listenSocket, SOMAXCONN);if (iResult = SOCKET_ERROR) printf(“listen failed with error:%dn“, WSAGetLastError();closesocket(lis
16、tenSocket);WSACleanup();return -1;printf(“listening on socket.n“);/* accept new incoming connections */int again = 0;while (again #include #pragma comment(lib, “ws2_32“)int main() WSAData wsaData;WORD wVersionRequested;int iResult;struct sockaddr_in service;SOCKET listenSocket;wVersionRequested = MA
17、KEWORD(2, 2);iResult = WSAStartup(wVersionRequested, if (iResult != 0) printf(“WSAStartup failed with error: %dn“, iResult);return -1;listenSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);9 / 14if (listenSocket = INVALID_SOCKET) printf(“create socket failed with error: %dn“, WSAGetLastError();WSAC
18、leanup();service.sin_family = AF_INET;service.sin_addr.s_addr = inet_addr(“127.0.0.1“);service.sin_port = htons(9999);iResult = bind(listenSocket, (SOCKADDR *) if (iResult = SOCKET_ERROR) printf(“bind failed with error:%dn“, WSAGetLastError();closesocket(listenSocket);WSACleanup();return -1;struct s
19、ockaddr_in client_addr;int len = sizeof(client_addr);while(1) /* do something:* recvfrom()* sendto()*/closesocket(listenSocket);WSACleanup();return 0;2.4. 连接型 UDP 一般用于哪一方?什么场合?一般用于客户端;要求内核进行 UDP 包的过滤;2.5. 广播程序,每隔 3 秒广播本地时间步骤:WSAStartup(),初始化winsocksocket(),创建 socketsetsockopt(),允许广播while (continue)
20、getCurrentTime(),获取本地时间10 / 14sendto(),发送数据Sleep(),暂停3 秒closesocket(),关闭socketWSACleanup(),释放winsock资源代码:/* 广播服务器端程序* 每3秒广播服务器端的时间*/#include #include #include #pragma comment(lib, “ws2_32“)void getcurtime(char* curtime) time_t tm;/time.htime(/time.hsprintf(curtime, “%sn“, ctime(int main() int iResul
21、t;WORD wVersionRequested;WSADATA wsaData;SOCKET serverSocket;struct sockaddr_in service;char on = 1;char msg256;serverSocket = socket(AF_INET, SOCK_DGRAM, 0);setsockopt(serverSocket, SOL_SOCKET, SO_BROADCAST, /允许发广播包service.sin_family = AF_INET;service.sin_port = htons(9999);service.sin_addr.s_addr
22、= htonl(INADDR_BROADCAST);while (1) getcurtime(msg);sendto(serverSocket, msg, strlen(msg), 0, (sockaddr *)11 / 14printf(“%sn“,msg);Sleep(3000);/* sleep 3 seconds between send */ closesocket(serverSocket);WSACleanup();return 0;2.6. 多播(或称组播)程序,每隔 3 秒多播本地时间步骤:WSAStartup(),初始化winsocksocket(),创建 socketse
23、tsockopt(),允许广播while (continue) getCurrentTime(),获取本地时间sendto(),发送数据到某 D类地址Sleep(),暂停3 秒closesocket(),关闭socketWSACleanup(),释放winsock资源代码:/* 多播服务器端程序* 每3秒广播服务器端的时间*/#include #include #include #pragma comment(lib, “ws2_32“)void getcurtime(char* curtime) time_t tm;/time.htime(/time.hsprintf(curtime, “%
24、sn“, ctime(int main() int iResult;WORD wVersionRequested;WSADATA wsaData;SOCKET serverSocket;12 / 14struct sockaddr_in service;char on = 1;char msg256;serverSocket = socket(AF_INET, SOCK_DGRAM, 0);setsockopt(serverSocket, SOL_SOCKET, SO_BROADCAST, /允许发广播包service.sin_family = AF_INET;service.sin_port
25、 = htons(9999);service.sin_addr.s_addr = inet_addr(“226.6.6.6“);printf(“每3秒向组226.6.6.6发包,告之本机的时间n“ );while (1) getcurtime(msg);sendto(serverSocket, msg, strlen(msg), 0, (sockaddr *)printf(“%sn“,msg);Sleep(3000);/* sleep 3 seconds between send */ closesocket(serverSocket);WSACleanup();return 0;3. 协议分
26、析3.1. IP 数据报头部最大/ 小长度、头部选项最大/小长度,最多记录路由个数头部最小长度:20 Bytes头部最大长度:60 Bytes选项最小长度: 0 Bytes选项最小长度:40 Bytes最多记录路由: 9 个3.2. 程序设计步骤:MyPing (源主机发送类型为 8 代码为 0 的“回送请求报文” ,当目标主机收到该报文时,将标识符、序号、数据区复制以组成类型为 0 代码为 0 的“ 回送应答报文” ,发送给源主机。)1) 创建原始套接字;2) 设置套接字的超时选项(如 6 秒) ;3) 构造 ICMP 数据报:类型为 8 代码为 0 回送请求;13 / 144) 构造 IP
27、 数据报,将上述的 ICMP 数据报作为 IP 数据报的数据部分;设置超时选项;5) 发送 IP 数据报到目标 IP 地址、端口;6) 若收到目标主机的类型为 0 代码为 0 的回头应答报文,则表示目标地址、端口号可达;3.3. 程序设计步骤:路由 MTU 发现1) 创建原始套接字(ICMP 协议) ;2) 打开选项;3) 构造 IP 报文:4) 填写头部:报文头部长度设置为最大,不允许分片;5) 填写数据:TCP 数据报;当路由器发现报文长度太长,需要分片,但报文头部设置为不允许分片,则路由器丢弃该报文,并发送 ICMP 报文给源主机;本地收到 ICMP 报文后,将报文长度设置为收到的 IC
28、MP 报文长度,再次重新发送 IP 数据报。直到源主机收到来自目的主机的 TCP 确认报文,最后一次发送的报文长度就是该路径的 MTU。3.4. 程序设计步骤:记录两主机之间经过的路由1) 创建原始套接字;2) 打开记录路由选项:选项类为 0;选项号为 7;选项长度为 39;指针初值为 4;初始时,指针指向存放第一个 IP 地址的位置,经过各路由器时,存入每个路由器的IP 地址,指针的值每次加 4;最多可存放 9 个 IP 地址;3.5. 程序设计步骤:FTP 断点续传1) USER 用户名2) PASS 口令3) TYPE I(文件类型:二进制)4) FILE F(文件的数据结构:文件结构)
29、5) MODE S(文件的传输方式:流)6) PORT IP 端口号(客户端 IP、端口号)7) REST N(N:已下载的字节数)8) RETR 文件名9) QUIT(退出)3.6. 程序设计步骤:FTP 多线程下载1) 创建 N 个线程,通过 “SIZE 文件名”获取文件大小 M;2) 每个线程历经 3.5 中的 1)-6)步;3) 第 i 个线程: REST I*M/N;4) RETR 文件名;5) QUIT3.7. FTP 主动、被动方式的区别及适用的场合FTP 数据连接有两种方式:主动方式和被动方式;主动和被动是就服务器而言的;主动方式:服务器主动向客户端发起连接,客户端使用 POR
30、T 命令将本地的 IP 地址和端口号告诉服务器,服务器使用 20 端口连接客户端的端口;被动方式:服务器等待客户端的连接,客户端使用 PASV 命令使服务器处于被动传输模式,服务器使用 PORT 命令将服务器的端口(大于 1024 的非特权端口)告诉客户端,客户端向服务器发起连接;14 / 14主动模式适用场合:服务器在子网内;被动模式适用场合:客户端在子网内;客户端的防火墙可能会阻塞某些非特权端口,导致服务器无法主动连接客户端;3.8. 程序设计步骤:HTTP 断点续传1) 确定已下载数量 n;2) get 方法头部:range: bytes=n-(从 n 处开始下载)3.9. 程序设计步骤
31、:HTTP 文件多线程下载通过 HTTP 协议的 head 方法,提取 content-length 内容,获取文件的大小 M;将文件分成 n 份,每个线程都向服务器提出文件下载请求,第 i 个线程的 get 方法头部 Range: bytes=(i-1)*M/N i * M/N;3.10. HTTP 状态管理方法HTTP 是无状态的。若要对购物车进行状态管理,方法有:1) 通过 get 方法,传递参数2) 利用表单中的隐藏域3) session4) cookie4. 扩展4.1. TCP 连接池的设计方法定义连接池的大小 N;创建一个大小为 N 的 TCP 套接字数组,每个套接字绑定到本地 IP 和端口号;创建一个标记数组,大小为 N,分别标记对应的套接字是否空闲;当有新的连接请求时,在套接字数组中取出一个空闲的套接字,进行连接,并将该套接字的状态改为“正在使用” ;当有连接结束时,释放该连接,并将对应的状态改为“空闲” ;4.2. 服务器端程序如何判断客户端程序是否在线客户端按照一定的时间间隔向服务器发送心跳包,以通知服务器自己是在线的。若服务器在间隔一定的时间(如 5 分钟)后,没有收到心跳包,则认为该客户端不在线,服务器进行相应的数据保存及资源释放处理。5. 其他