起碼要有個東西會動之——進度條
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)); // 顯示
}
}