TCP/IP协议
- OSI参考模型采用分层划分原则,将网络中的数据传输划分为7层,其中,物理层居于最下层,是最基础、核心的网络硬件层;应用层居于最上层,负责应用资源的管理。每一层使用下层的服务,并向上层提供服务。
-
TCP/IP通信协议
采用如下4层的层级结构,每一层都呼叫它的下一层提供的网络来完成自己的需求。
(1)应用层:应用程序间沟通的层,如简单电子邮件传输协议(SMTP)、文件传输协议(FTP)、网络远程访问(Telnet)协议等。
(2)传输层:在此层中提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据包协议(UDP)等,TCP和UDP给数据包加入传输数据,并把它传输到下一层中。这一层负责传送数据,并且确定数据已被送达并接收。
(3)互联网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),
如网际协议(IP)。
(4)网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。 -
IP网际协议
32位的IP地址主要分为两部分:前缀和后缀。前缀表示计算机所属的物理网络,后缀确定该网络上的唯一一台计算机。在互联网上,
每一个物理网络都有一个唯一的网络号,根据网络号的不同,可以将IP地址分为5类,即A类、B类、C类、D类和E类。
其中,A类、B类和C类属于基本类,D类用于多播发送,E类属于保留类
-
TCP/IP协议的数据包
(1)IP数据包。IP数据包在IP协议间发送,主要在以太网与网际协议模块之间传输,提供无链接数据包传输。
IP协议不能保证数据包的准确发送,但能保证最大限度地发送数据。
(2)TCP数据包。传输控制协议(TCP)是一种提供可靠数据传输的通信协议,它在网际协议模块和TCP模块之间传输,
TCP数据包分为TCP包头和数据两部分。TCP包头包含源端口、目的端口、序列号、确认序列号、头部长度、码元比特、
窗口、校验和、紧急指针、可选项、填充位和数据区。
在发送数据时,应用层的数据传输到传输层,加上TCP的TCP包头,数据就构成了包文。
报包是网际层IP的数据,如果再加上IP首部,就构成了IP数据包。
TCP提供了一个安全可靠的、面向连接的、全双工的(包含两个独立且方向相反的连接)流传输服务,
允许两个应用程序建立一个连接,并在全双工方向上发送数据和终止连接
可靠传输服务软件都是面向数据流的。
(3)UDP数据包。用户数据包协议(UDP)是一个面向无连接的协议,采用该协议后,两个应用程序间不需要先建立连接,
它为应用程序提供了一次性的数据传输服务。UDP工作在网际协议模块与UDP模块之间,不提供差错恢复,不能提供数据重传,
所以使用UDP的应用程序都比较复杂,如DNS(域名解析服务)应用程序
UDP数据包分为伪首部和首部两个部分,伪首部包含原IP地址、目标IP地址、协议字、UDP长度、源端口、目的端口、
包文长度、校验和、数据区,是为了计算和检验而设置的。
伪首部包含IP首部一些字段,其目的是让UDP两次检查数据是否正确到达目的地。
(4)ICMP数据包。ICMP被称为网际控制包文协议。作为IP协议的附属协议,
ICMP用来与其他主机或路由器交换错误包文和其他重要信息,可以将某个设备的故障信息发送到其他设备上**套接字**
-
套接字,实际上是一个指向传输提供者的句柄。在Winsock中,就是通过操作该句柄来实现网络通信和管理的
-
套接字可以分为3种,即原始套接字、流式套接字和数据包套接字。
原始套接字:在Winsock2规范中,它能够使程序开发人员对底层的网络传输机制进行控制,在原始套接字下接收的数据中含有IP头。
流式套接字:提供了双向、有序、可靠的数据传输服务,该类型套接字在通信前需要双方建立连接,TCP协议采用的就是流式套接字
数据包套接字:提供双向的数据流,但是它不能保证数据传输的可靠性、有序性和无重复性,UDP协议采用的就是数据包套接字。
Winsock的使用
- 在使用套接字函数前,用户需要引用winsock2.h头文件,并链接ws2_32.lib库文件
- 常用的套接字函数
(1)WSAStartup函数:该函数用于初始化ws2_32.dll动态链接库。在使用套接字函数之前,一定要初始化ws2_32.dll动态链接库
wVersionRequested:表示调用者使用Windows socket的版本,高字节记录修订版本,低字节记录主版本,
例如,如果Windows socket的版本为2.1,则高字节记录1,低字节记录2。
lpWSAData:是一个WSADATA结构指针,该结构详细记录了Windows套接字的相关信息
(2)socket函数。该函数用于创建一个套接字。语法如下:
SOCKET socket ( int af,int type, int protocol );
af:地址家族。通常为AF_INET。
AF_INET:表示IPv4地址格式,通常用于Internet上的通信。
AF_INET6:表示IPv6地址格式,支持更多的地址空间。
AF_UNSPEC:未指定地址族,通常用于允许系统默认选择地址族
在大多数TCP/IP网络编程中,AF_INET 是最常用的值
type:通信类型。
SOCK_STREAM:提供可靠的、面向连接的字节流服务。通常用于TCP协议。
SOCK_DGRAM:提供无连接的、不可靠的数据报服务。通常用于UDP协议。
SOCK_RAW:提供原始套接字,允许应用程序直接访问网络层数据。
protocol:套接口所用的协议。如果用户不指定,可以设置为0。
IPPROTO_TCP(TCP传输协议)
IPPROTO_UDP(UDP传输协议)。
如果设置为0,则系统会根据地址族和套接字类型自动选择一个合适的协议。
注意:在大多数情况下,将 protocol 设置为0是安全的,除非你有特定的需求需要选择特定的协议。
返回值:是创建的套接字句柄。
(3)bind函数。该函数用于将套接字绑定到指定的端口和地址上。语法如下:
int bind (SOCKET s,const struct sockaddr FAR* name,int namelen );
s:套接字标识。
name:sockaddr结构指针。该结构包含要结合的地址和端口号。
namelen:确定name缓冲区的长度。
返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
(4)listen函数。该函数用于将套接字设置为监听模式。对于流式套接字,必须处于监听模式才能接收客户端套接字的连接。
int listen ( SOCKET s, int backlog);
s:套接字标识。
backlog:等待连接的最大队列长度。
例如,如果backlog被设置为2,此时有3个客户端同时发出连接请求,那么前两个客户端连接放置在等待队列中,
第3个客户端得到错误信息。
(5)accept函数。该函数用于接受客户端的连接。在流式套接字中,只有套接字处于监听状态,才能接受客户端的连接。
SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );
s:套接字,处于监听状态。
addr:sockaddr_in结构指针,包含一组客户端的端口号、IP地址等信息。
addrlen:用于接收参数addr的长度。
返回值:新的套接字,它对应于已经接受的客户端连接,对于该客户端的所有后续操作,都应使用这个新的套接字。
(6)closesocket函数。该函数用于关闭套接字。
int closesocket (SOCKET s);
s:标识一个套接字。
如果参数s设置有SO_DONTLINGER选项,则调用该函数后会立即返回,
但此时如果有数据尚未传送完毕,会继续传递数据,然后才关闭套接字。
(7)connect函数。该函数用于发送一个连接请求。
int connect (SOCKET s,const struct sockaddr FAR* name,int namelen );
s:套接字。
name:套接字s想要连接的主机地址和端口号。
namelen:name缓冲区的长度。
返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。
用户可以通过WSAGE-TLASTERROR得到其错误描述。
(8)htons函数。该函数将一个16位的无符号短整型数据由主机排列方式转换为网络排列方式
u_short htons (u_short hostshort );
hostshort:主机排列方式的无符号短整型数据。
返回值:16位的网络排列方式数据。
(9)htonl函数。该函数将一个无符号长整型数据由主机排列方式转换为网络排列方式。
u_long htonl ( u_long hostlong);
hostlong:主机排列方式的无符号长整型数据。
返回值:32位的网络排列方式数据。
(10)inet_addr函数。该函数将一个由字符串表示的地址转换为32位的无符号长整型数据。
unsigned long inet_addr (const char FAR * cp);
cp:一个IP地址字符串。
返回值:32位无符号长整数
(11)recv函数。该函数用于从面向连接的套接字中接收数据。
int recv (SOCKET s,char FAR* buf,int len,int flags);
s:套接字。
buf:接收数据的缓冲区。
len:buf的长度。
flags:函数的调用方式。
MSG_PEEK,表示查看传来的数据,在序列前端的数据会被复制一份到返回缓冲区中,但是这个数据不会从序列中移走。
MSG_OOB,表示用来处理Out-Of-Band数据,也就是外带数据。
(12)send函数。该函数用于在面向连接方式的套接字间发送数据。
int send (SOCKET s,const char FAR * buf, int len,int flags);
s:套接字。
buf:存放要发送数据的缓冲区。
len:缓冲区长度。
flags:函数的调用方式。
(13)select函数。该函数用来检查一个或多个套接字是否处于可读、可写或错误状态。
int select (int nfds,fd_set FAR * readfds,fd_set FAR * writefds,fd_set FAR * exceptfds, const struct timeval FAR *
timeout);
nfds:无实际意义,只是为了和UNIX下的套接字兼容。
readfds:一组被检查可读的套接字。
writefds:一组被检查可写的套接字。
exceptfds:被检查有错误的套接字。
timeout:函数的等待时间。
(14)WSACleanup函数。该函数用于释放为ws2_32.dll动态链接库初始化时分配的资源。
int WSACleanup (void);
(15)WSAAsyncSelect函数。该函数用于将网络中发生的事件关联到窗口的某个消息中。
int WSAAsyncSelect (SOCKET s, HWND hWnd,unsigned int wMsg,long lEvent);
s:套接字。
hWnd:接收消息的窗口句柄。
wMsg:窗口接收来自套接字中的消息。
lEvent:网络中发生的事件
(16)ioctlsocket函数。该函数用于设置套接字的I/O模式。
int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);
s:待更改I/O模式的套接字。
cmd:对套接字的操作命令。
FIONBIO,当argp为0时,表示禁止非阻塞模式;当argp为非0时,表示设置非阻塞模式。
FIONREAD,表示从套接字中可以读取的数据量。
SIOCATMARK,表示所有的外带数据都已被读入。这个命令仅适用于流式套接字,
并且该套接字已被设置为可以在线接收外带数 据(SO_OOBINLINE)。
argp:命令参数。 - Winsock2新增的函数。
WSAAccept:accept函数扩展版本,支持条件接收和套接口分组。
WSACloseEvent:释放一个事件对象。
WSAConnect:connect函数的扩展版本,支持连接数据交换和QOS规范。
WSACreateEvent:创建一个事件对象。
WSADuplicateSocket:为一个共享套接口创建一个新的套接口描述字。
WSAEnumNetworkEvents:检查是否有网络事件发生。
WSAEnumProtocols:得到每个可以使用的协议信息。
WSAEventSelect:把网络事件和一个事件对象连接。
WSAGetOverlappedResu:得到重叠操作的完成状态。
WSAGetQOSByName:为一个传输协议服务名提供相应的QOS参数。
WSAHtonl:htonl函数的扩展版本。
WSAHtons:htons函数的扩展版本。
WSAIoctl:ioctlsocket函数的允许重叠操作的版本。
WSAJoinLeaf:在多点对话中计入一个叶节点。
WSANtohl:ntohl函数的扩展版本。
WSANtohs:ntohs函数的扩展版本。
WSARecv:recv函数的扩展版本,支持分散/聚焦I/O和冲抵套接口操作。
WSARecvDisconnect:终止套接口的接收操作。如果套接口是基于连接的,得到拆除数据。
WSARecvFrom:recvfrom函数的扩展版本,它支持分散/聚焦I/O和冲抵套接口操作。
WSAResetEvnet:重新初始化一个数据对象。
WSASend:send函数的扩展版本,支持分散/聚焦I/O和冲抵套接口操作。
WSASendDisconnect:启动一系列拆除套接口连接的操作,并且可以选择发送拆除数据。
WSASendTo:sendto函数的扩展版本,支持分散/聚焦I/O和冲抵套接口操作。
WSASetEvent:设置一个数据对象。
WSASocket:socket函数的扩展版本,以一个PROTOCOL_INFO结构作为输入参数,并且允许创建重叠套接口,它还允许创建套接口组。
WSAWaitFoMultipleEvent:阻塞多个事件对象。
连接流
- 面向连接流主要指通信双方在通信前先建立连接。建立连接的步骤如下:
创建套接字socket。
将创建的套接字绑定(bind)到本地的地址和端口上。
服务器端设置套接字的状态为监听状态(listen),准备接受客户端的连接请求。
服务器端接受请求(accept),同时返回得到一个用于连接的新套接字。
使用这个新套接字进行通信(通信函数使用send/recv)。
释放套接字资源(closesocket)。
面向连接流的整个过程分为客户端和服务器端
- 面向无连接的