发布时间:2010-9-5 16:32
分类名称:windows
背景知识
TCP/IP模型
应用层 |
传输层(TCP/UDP) |
网络层(IP) |
数据链路层 |
通常的套接字是应用到传输层的接口.(有一种原始的, 可以直接使用IP).
传输层一般的俩个协议: UDP (User Datagram Protocol) and TCP (Transmission Control Protocol ).
UDP
无连接 (发送数据无需建立连接)
不可靠 (不能确保UDP数据报最终达到目的地, 对接收的数据不发送确认, 无法指定是否到达目的地, 数据不会重发).
速度快 (由于无需建立连接和确认信息, 速度也就快了).
TCP
面向连接 (发送数据前先要建立连接)
可靠 (当发送数据后, 要求对方返回一个确认信息, 如果没有接收到对方确认, 重发.)
基于字节流 (对发送的数据排序, 每个发送字节关联一个序列号. 对方根据此序列号进行数据排序, 确保数据的顺序).
三握手:
客户 ----SYN---> 服务器
客户 <--SYN and ACK--- 服务器
客户 ----SYN---> 服务器
端口(0 ~ 65535, (unsigned long) 216)
0 ~ 1023 一般和某些服务绑定, 所以一般我们不用这个范围的端口.
1024 ~ 49151 提供一般应用程序使用.(Win Socket开发选中的范围)
动态或私有端口: 49151 ~ 65535
IP地址
struct sockaddr_in {
short sin_family; // must be AF_INET
unsigned short sin_port; //
端口号
struct in_addr sin_addr; // IP
地址
char sin_zero[8]; //
扩充
};
字节顺序
Intel的电脑一般都是将低地址对应实际数子的高位.如
计算机用一个Short 表示1的方式:
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,0x0F
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
1本来应该是:
00 01, 计算机将其以字节为单位反转高低位, 为: 01 00.
再如, 一个u_long类型: 4432
内存表示为: 50 11 00 00, 实际应该是: 00 00 11 51
而网络流就是按照这种顺序传输的, 所以要利用 htonl, htons, ntohl, ntohs四个函数来转换字节流顺序.
关于关闭
关闭连接有两个选择: 从容关闭, 硬关闭.
两个混淆的关闭: 关闭连接, 关闭套接字.
从容关闭: 一方关闭连接, 另一方可以继续读取网络堆栈的数据, 直到数据传送完毕, 双方都关闭了连接,可以保证在关闭之前,数据都被接收.
硬关闭: 只要一方关闭了连接, 另一方就无法接收数据, 数据无法传送完毕.
关系:
关闭连接的方式包括: 从容关闭连接方式 和 硬关闭连接方式
关闭套接字其实就是相对于操作系统而已的, 释放资源, 关闭句柄,相当于CloseHandle的意思.
关闭连接函数: shutdown and WSASendDisconnect函数
关闭套接字函数: closesocket(同时隐含执行shutdown函数功能
从容关闭的思想, 甲方我要关闭 -> 通知给乙方, 乙方得到消息后, 哦知道了(此时甲方还能接收数据), 乙方可以选择继续发送数据,也可以通知甲方,我也要关闭了, 然后调用closesocket关闭套接字 -> 甲方得到关闭消息,closesocket关闭套接字.
甲:客户端, 乙:服务器.
(甲: 调用shutdown(SD_SEND), 接着等待FD_READ和FD_CLOSE, 等到FD_CLOSE关闭套接字)
(乙: 调用 shutdown(SD_SEND), 紧着调用closesocket)
基本的函数:
WSAStartup socket bind listen accept recv send closesocket
shutdown connect recvfrom sendto WSCleanup
getsockopt setsockopt
俩大模式: 阻塞模式, 非阻塞模式.
五大模型: Select模型, WSAAsyncSelect模型, WSAEventSelect模型, 重叠I/O, 完成端口.
阻塞模式, 在哪儿阻塞?
有两个地方:
1. 等待数据的时候, 当数据没有到达, 就会被阻塞, 等待数据的到达.
2. 数据到达, 从系统缓冲区复制到用户区, 此过程也被阻塞.
五个模型的阻塞方式各不相同.
通常说的非阻塞, 其实是在上面提到的阻塞的第一个地方不阻塞. 真正实现两个地方都不阻塞的, 只有 重叠I/O, 完成端口 俩个模型.
阻塞模型
如果是单线程, 一旦阻塞, 程序就被操作系统换入等待队列, 程序就处于等待状态,(如果是GUI界面, 可以想象假死的状态多么难堪). 所以可以用多线程来操作, 例如主线程来运行GUI的消息循环, 创建一个线程用来等待(recv之类的函数), 创建一个线程来接处理数据.这样在其中一个线程等待的时间里, 可以来响应用户消息和处理数据等. 可以想象. 如果大量用户接入, 就要创建大量的线程, 对系统造成的开销很大.
非阻塞模式
可以看到, 程序循环检测数据. 浪费CPU, 如果写个程序能看到, 程序的占有率基本上总是100%, 对系统的资源占用很大, 而且比起阻塞模式而已, 没有什么优点. 非阻塞的作用主要体现在和5个模型共同协作, 才能体现其优点.
Select模型
可以同时等待多个套接字.
WSAAsyncSelect模型
WSAAsyncSelect 是 select模型的异步版本. 那么异步指的是谁和谁异步?
这里的异步应该是指: 目前执行WSAAsyncSelect的线程和等待数据的到达俩个流程是异步的. 也就是说, 谁也不等谁, 线程调用完此函数继续下下执行, 数据到达才发送消息给应用程序窗口. 接着线程就要处理这批数据了. 通过图看的出, 从缓冲区复制到用户空间的数据仍然是阻塞的. 这个过程类似于ReadFile, 所以还没算真正意义的(执行流程和接收数据的)异步.
WSAAsyncSelect 与 Select
WSAAsyncSelect模型与Select模型相同, 都可以有效的对多个套接字进行管理.
WSAAsyncSelect是基于消息的, 所以必须创建窗口(可以创建一个隐藏的窗口), 向Unix很多基于命令符界面的环境, 都一般用Select模型.
WSAAsyncSelect自动将套接字设置为非阻塞模式, 而select并没有这个特性
WSAEventSelect模型
通过对比图, 可以看出, WSAEventSelect 和 WSAAsyncSelect的唯一区别就是通知方式不同, WSAAsyncSelect 是通过发送消息来通知应用程序的, 而WSAEventSelect则是事件形式通知. 当然实现的形式也不相同. 一个是通过操作系统发送消息给窗口过程来处理. 一个是操作系统触发事件, 在应用程序端则是等待事件发生(由于等待事件是阻塞的, 所以要多开一个线程).
重叠I/O
对比:
完成端口模型
完成端口解决了 “one-thread-per-client” 问题.
网络程序开发流程:
1. 需求分析
2. 根据需求, 进行数据包设计(一般分为包头和包数据两部分, 包头用来存储包的必要信息, 如信息类型, 数据长度等)
3. 定义传输协议(如何传输).
4. 理解需求, 设计总体架构, 利用设计模式等方法, 进行问题分析和设计类图.
5. 实现, 通常要配合多线程来实现通信问题. (一般有等待客户请求线程, 接收数据线程, 发送数据线程, 资源清理线程).
6. 实现服务器端.
7. 实现客户端.