随着网络的普及,软件对网络的依赖性也日益增强。打开电脑,浏览器,聊天工具,网络游戏等等和网络相关的内容。然而开发网络程序所面对的问题是网络应用程序的性能、安全性和效率等等。这里先介绍一下网络通信的基础知识,然后再演示一个实例:通过网络编程实现网络聊天和传输文件。
一、网络模型
1.1 OSI参考模型
1.2 TCP/IP参考模型
二、基础协议
2.1 IP协议
2.2 TCP协议
图中给出了TCP协议数据报头的格式。
源端口、目的端口:16位长。标识出远端和本地的端口号。
顺序号:32位长。表明了发送的数据报的顺序。
确认号:32位长。希望收到的下一个数据报的序列号。
TCP协议数据报头DE 头长:4位长。表明TCP头中包含多少个32位字。
接下来的6位未用。
ACK:ACK位置1表明确认号是合法的。如果ACK为0,那么数据报不包含确认信息,确认字段被省略。
PSH:表示是带有PUSH标志的数据。接收方因此请求数据报一到便可送往应用程序而不必等到缓冲区装满时才传送。
RST:用于复位由于主机崩溃或其它原因而出现的错误的连接。还可以用于拒绝非法的数据报或拒绝连接请求。
SYN:用于建立连接。
FIN:用于释放连接。
窗口大小:16位长。窗口大小字段表示在确认了字节之后还可以发送多少个字节。
校验和:16位长。是为了确保高可靠性而设置的。它校验头部、数据和伪TCP头部之和。
可选项:0个或多个32位字。包括最大TCP载荷,窗口比例、选择重发数据报等选项。
最大TCP载荷:允许每台主机设定其能够接受的最大的TCP载荷能力。在建立连接期间,双方均声明其最大载荷能力,并选取其中较小的作为标准。如果一台主机未使用该选项,那么其载荷能力缺省设置为536字节。
窗口比例:允许发送方和接收方商定一个合适的窗口比例因子。这一因子使滑动窗口最大能够达到232字节。
TCP协议数据报头选择重发数据报:这个选项允许接收方请求发送指定的一个或多个数据报。
2.3 UDP协议
总结起来:
1.无连接,不可靠;
2.出错(通过校验和检查)就丢掉此包,丢失不重传,只是给个警告;
3.包的格式,有源端口和目的端口,校验和等;
4.端口号,根据应用层服务的不同,可以是默认的端口,也可以自己设定。
UDP协议的主要特点
UDP是一种无连接的、不可靠的传输层协议;
在完成进程到进程的通信中提供了有限的差错检验功能;
设计比较简单的UDP协议的目的是希望以最小的开销来达到网络环境中的进程通信目的;
进程发送的报文较短,同时对报文的可靠性要求不高,那么可以使用UDP协议。
UDP的基本工作过程
UDP用户数据报传输过程中的封装与拆封
UDP报文传输队列
UDP的复用和分用
TCP/IP协议族中用端口号来标识进程;
端口号是在0到65535之间的整数;
客户程序随机选取的临时端口号;
每一种服务器程序被分配了确定的全局一致的熟知端口号;
每一个客户进程都知道相应的服务器进程的熟知端口号。
UDP使用的熟知端口号
UDP数据报格式
UDP检验和的检验范围:
伪头部
UDP头
应用层数据
三、套接字编程及相关函数介绍
套接字是网络编程的接口,提供了一种网络数据发送和接受的机制。套接字是网络通信的基础,一个套接字表示通信的一端,使用套接字可以实现数据包在网络上的传输。
socket 函数,用于根据指定的地址协议簇,套接字类型和协议类型创建一个套接字。
SOCKET socket(
int af,//指定协议簇,对于TCP/IP协议的套接字,它只能是AF_INET(即IPV4协议)
int type, //Socket套接字的类型
int protocol //协议类型
);
Socket类型:
SOCK_STREAM:流式套接字,提供有序的,可靠的,双向的且基于连接的字节流;
SOCK_DGRAM:数据包套接字,提供无连接,不可靠的数据传输服务;
SOCK_RAW:原始套接字。
若成功,则返回一个socket数据类型(套接字的句柄)。失败则返回一个INVALID_SOCKET,错误信息可通过WSAGetLastError函数返回。
bind函数,用来绑定套接字到指定的地址。
int bind(
SOCKET S, //套接字句柄
const struct sockaddr FAR* name, //该套接字的本地信息(指向本地地址结构)
int namelen//该地址结构的长度
);
创建了套接字后,应该将该套接字绑定到本地的某个地址和端口上。bind函数能够把处于未连接状态的套接字绑定到指定的地址,一般在connect或listen之前调用,可以对基于连接的套接字进行绑定操作,也可以对无连接的套接字进行绑定操作。
connect函数,用于连接远程服务器
int connect(
SOCKET s, //即将在其上建立连接的那个套接字
const struct FAR* name, //设定连接的服务器端地址信息,/*不必转换为网络字节顺序*/
int namelen //指定服务器端地址的长度
)
返回值:成功返回零,否则通过函数WSAGetLastError获取。
connect函数用于与指定的远程地址创建连接。如果第一个参数的所指定的套接字未曾绑定,则系统为其分配一个端口进行关联,并设置该套接字为已绑定状态。
listen函数,监听远程连接。
int listen(
SOCKET S, //指定套接字
int backlog //等待连接队列的最大长度
)
作用是将指定的套接字设置为监听模式。
在调用listen函数监听远程连接之前,首先需要确保套接字s已经绑定到了某个端口。参数backlog指定等待连接的队列大小,如果在某一连接请求到达时队列已满,服务器就直接拒绝连接,客户端就会收到错误值WSAECONNREFUSED
accept函数,用来接受客户端的连接。
SOCKET accept(
SOCKET s,//被listen函数设置为监听状态的套接字
struct sockaddr FAR* addr,//指向一个缓冲区的指针,接受连接实体的地址和端口信息,返回值
int FAR* addrlen //地址信息的长度,也是一个返回值
)
此函数返回一个新的套接字,此套接字对应已经接受的那个客户机连接,对于该客户机后续的所有操作,都应使用这个套接字,至于原来的套接字,仍然用于接受其他客户机连接,而且仍处于监听状态,如果无连接请求,服务进程被阻塞。
通过accept函数可以为连接请求队列中的第一个请求提供服务,接受客户端连接后返回返回一个新的套接字,同时把客户端的地址信息及其长度保存在addr和addrlen所指向的地址,以后对于该客户端的所有操作都使用这个新的套接字。
send函数,用来在已经建立连接的套接字上发送数据。
int send(
SOCKET s, //已建立连接的套接字
const char FAR* buf, //包含将要传递的数据
int len, //缓冲区的长度
int flags //设置为0
)
返回发送数据的字节数
sendTo函数,用来向指定地址发送数据。
int sendto(
SOCKET s, //套接字句柄
const char* buf, //指向待发送数据的缓冲区
int len, //数据长度
int flags //指定调用方式,一般设置为0
const struct sockaddr* to //指向目标地址结构
int tolen//地址结构长度
)
recv函数,用来在已经建立连接的套接字上接收数据。
int recv(
SOCKET s,//已建立连接的套接字
char FAR* buf,//保存接受的数据
int len,//缓冲区的长度
int flags//设置为0
)
返回接受数据的字节数
recvfrom函数,用来向指定地址接收数据。
int recvfrom(
SOCKET s, //套接字句柄
char* buf, //指向待发送数据的缓冲区
int len, //缓冲区长度
int flags, //指定调用方式,一般设置为0
const struct sockaddr* from, //指向目标地址结构
int* fromlen//地址结构长度
)
setsockopt函数,用来设置套接字属性。
int setsockopt(
SOCKET s, //套接字句柄
int level, //选项定义的层次,目前仅支持SOL_SOCKET和IPPROTO_TCP
int optname, //选项
const char* optval, //指向选项值缓冲区
int optlen//选项值缓冲区长度
)
参数optname指定要设置的选项标识,参数optval指向选项数据,参数optlen则表示选项数据的长度。
ioctlsocket函数,用来设置套接字I/O模型。
int ioctlsocket(
SOCKET s, //套接字句柄
long cmd, //指定操作类型
u_long* agrp//指向操作数
)
用于获取或设置与套接字相关的系统参数,与套接字具体使用的协议无关。