![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5e94af0202c611501c542823e113f365.png)
起码要有个东西会动之——进度条
void CdemoDlg::OnBnClickedBtnOnSure()
{
// TODO: 在此添加控件通知处理程序代码
//“确认上传”按钮进度条
CProgressCtrl* pProgressCtrl = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS1);
pProgressCtrl->SetRange(0, 100);
pProgressCtrl->SetPos(0);
for (int n = 0; n <= 100; n++) {
pProgressCtrl->SetPos(n);
}
}
void CdemoDlg::OnBnClickedBtnOnCancel()
{
// TODO: 在此添加控件通知处理程序代码
//“取消上传”按钮与“确认上传”对应同一个进度条
CProgressCtrl* pProgressCtrl = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS1);
pProgressCtrl->SetRange(0, 100);
pProgressCtrl->SetPos(0);//进度条
}
void CdemoDlg::OnBnClickedBtnDownStart()
{
// TODO: 在此添加控件通知处理程序代码
//“开始下载”按钮进度条
CProgressCtrl* pProgressCtrl = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS3);
pProgressCtrl->SetRange(0, 100);
pProgressCtrl->SetPos(0);
for (int n = 0; n <= 100; n++) {
pProgressCtrl->SetPos(n);
}
}
void CdemoDlg::OnBnClickedBtnDownCancel()
{
// TODO: 在此添加控件通知处理程序代码
//“取消下载”按钮与“开始下载”对应同一个进度条
CProgressCtrl* pProgressCtrl = (CProgressCtrl*)GetDlgItem(IDC_PROGRESS3);
pProgressCtrl->SetRange(0, 100);
pProgressCtrl->SetPos(0);
}
我要开始将玄学了之——SOCKET通信
TCP/IP协议
TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
TCP/IP传输协议是严格来说是一个四层的体系结构,应用层、传输层、网络层和数据链路层都包含其中。
OSI七层网络模型和TCP/IP四层模型对比:
UDP协议
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议,是支持一个无连接的传输协议。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。
Internet 的传输层有两个主要协议,互为补充。无连接的是 UDP,它除了给应用程序发送数据包功能并允许它们在所需的层次上架构自己的协议之外,几乎没有做什么特别的事情。面向连接的是 TCP,该协议几乎做了所有的事情。
TCP/IP协议通过三次握手来建立可靠的传输,应用于传输完整性要求将较高的连接中。
UDP协议则是提供了高速传输和实时性,主要用于实时通信等。
SPCKET
配图来自有道词典,从本意“插座”,大致可以猜测Socket的意思和用途。
应用程序在使用 TCP 或 UDP协议时,会用到操作系统提供的类库。这种类库一般被称为 API(Application Programming Interface,应用编程接口),API既是一组定义、程序及协议的集合。其中被广泛使用的就是一种名为Socket的API。
套接字Socket=(IP地址:端口号),即套接字的表示方法是点分十进制的lP地址后面写上端口号,中间用冒号或逗号隔开。每一个传输层连接唯一地被通信两端的两个端点(即两个套接字)所确定。例如:如果IP地址是210.37.145.1,而端口号是23,那么得到套接字就是(210.37.145.1:23)。基于套接字的这种设定方式,应用程序就能利用套接字,可设置对端的 IP 地址,端口号,并实现数据的发送与接收。
SOCKET通信过程
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个 Socket。建立网络通信连接至少要一对Socket,既是通信双方的IP地址和端口号都要是已知的。使用Socket进行通信时,同样要使用到 服务端和客户端,但是基于TCP协议和UDP协议的连接方式存在不同之处。
TCP和UDP最大的区别在于是否需要客户端与服务端建立连接后才能进行数据传输 TCP会在建立连接之后才发送信息,而UDP发送独立的数据报,其中包含了完整的目的地地址和端口。
但是笼统地来说,抛开使用的协议不同之外,大体过程参看下图:
基于TCP协议的Socket通信
传输前先开服务端,accept,等客户端接入,然后获得 客户端socket然后进行IO操作。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤。
1. 服务器监听。
服务器端socket并不定位具体的客户端socket,而是处于等待连接的状态,实时监控网络状态。
2. 客户端请求
由客户端的socket提出连接请求,要连接的目标是服务器端的socket。为此,客户端的socket必须首先描述它要连接的服务器的socket,指出服务器端socket的地址和端口号,然后就向服务器端socket提出连接请求。
3. 连接确认 。
所谓连接确认,是指当服务器端socket监听到或者说接收到客户端socket的连接请求,就会响应客户端socket的请求,建立一个新的线程,并把服务器端socket的描述发送给客户端。一旦客户端确认了此描述,连接就建立好了。而服务器端socket继续处于监听状态,接收其他客户端socket的连接请求。
具体到服务器端和客户端,步骤如下:
Socket服务端的工作
- 创建服务端Socket,绑定指定的客户端socket;
- 监听客户端的请求;
- 连接建立后,通过输入流读取客户端发送的请求信息;
- 通过输出流向客户端发送响应信息;
- 关闭连接。
Socket客户端的工作
- 创建客户端Socket,指明需要连接的服务端的socket(IP地址及端口号);
- 连接建立后,通过输出流向服务端发送请求信息;
- 通过输入流获取服务端的响应信息;
- 关闭连接。
基于UDP协议的Socket通信
我们小组的代码和之前老师讲的,都是基于UDP协议的Socket通信,它有个优点就是:简单。
UDP协议以数据报作为数据的传输载体,在进行传输时,首先要把传输的数据定义成数据报(Datagram),数据报包含一个报头(header)和数据本身,报头中指明数据要到达的Socket(IP地址及端口号),再将数据以数据报的形式发送出去,除非服务端收到后又回复一段表示“确认”的数据报,负责客户端无法知道服务端是否收到。
具体到服务器端和客户端,步骤如下:
Socket服务端的工作
- 创建服务端,指定端口号;
- 创建数据报,用于接收客户端发来的数据;
- 接收客户端发来的数据;
- 读取数据
Socket客户端的工作
- 定义服务端的socket;
- 创建数据报,包含发送的数据信息和服务器socket;
- 创建客户端Socket;
- 发送数据报。
SOCKET连接函数
在 Windows 当中,socket 被当作一个网络连接来对待,因此需要调用专门针对 socket 而设计的数据传输函数。
socket()函数
用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议未指定(等于0),则使用缺省的连接方式。
对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。
#include <sys/socket.h>
int socket( int af, int type, int protocol);
af:一个地址描述。仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
bind()函数
#include <winsock.h>
int PASCAL FAR bind( SOCKET sockaddr, const struct sockaddr FAR* my_addr,int addrlen);
sockfd:已经建立的socket;
my_addr:一个指向sockaddr结构体类型的指针;
addrlen:my_addr结构的长度,可以用sizeof操作符获得。
listen()函数
用于监听客户端请求
#include <sys/socket.h>
int listen( int sockfd, int backlog);
sockfd:用于标识一个已捆绑未连接套接口的描述字。
backlog:等待连接队列的最大长度
connect() 函数
connect()用于建立与指定socket的连接。
#include <sys/socket.h>
int connect(SOCKET s, const struct sockaddr * name, int namelen);
s:标识一个未连接socket
name:指向要连接套接字的sockaddr结构体的指针
namelen:sockaddr结构体的字节长度
accept()函数
在一个socket接受的一个连接
#include <sys/socket.h>
accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:套接字描述符,该套接口在listen()后监听连接。
addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。
read()、write()函数
int read(int fd, void *buf, size_t count);
int write(int handle, void *buf, int nbyte);
closesocket()函数
关闭socket
closesocket( SOCKET s);
s:一个套接口的描述字。
小组作业之——基于UDP协议的Socket通信的核心代码
//“作为服务器”代码
void CdemoDlg::OnBnClickedBtnServ()
{
// TODO: 在此添加控件通知处理程序代码
// 服务器绑定地址
bind(socket_serv, (struct sockaddr*)&si, sizeof(si));
// 创建客户端地址
SOCKADDR addrCli;
int nCliAddrLen = sizeof(addrCli);
// 接收客户端消息缓存
char buff[100] = { 0 };
// 接收客户端信息
while (true)
{
recvfrom(socket_serv, buff, sizeof(buff), 0, (sockaddr*)&addrCli, &nCliAddrLen);
SetDlgItemText(IDC_STATIC_RECV, CString(buff)); // 显示
}
}