網路程式設計基礎第四講阻塞模型
網路程式設計基礎第四講阻塞模型
一丶阻塞模型簡介
不知道大家有沒有注意到.我們客戶端 或者服務端.的TCP 收發資料的時候(send/recv)如果接受不到資料就一直不返回.從而造成我們網路的阻塞.程式無法正常執行.
不過針對這一方法.我們可以開一個執行緒去專門接受資料.或者傳送資料.
這個就是我們常說的阻塞.
只要我們建立的套接字都是阻塞模型. 就是說資料接受不到不返回.
我們可以設定為非阻塞.就是不管資料有沒有來到都會返回.如果來到.會有通知.我們可以程式設計接受資料.
設定非阻塞模式方法
ioctlsocket(SOCKET s, long cmd, u_long *arpg);
改變套接字模式.為飛租she.
二丶阻塞模式迭代模式 與 併發連線模式
1.阻塞模式的迭代模式 就是指每次只服務一個連線.只有服務完當前的客戶端連線之後.才會繼續服務下一個客戶連線
2.併發連線模式 通過多執行緒.可以同時服務多個連結.沒一個執行緒處理一個客戶端的連線.
阻塞迭代模式步驟
1.生成一個函式.繫結本地地址跟監聽.
2.生成一個函式.專門接受一個客戶端連線.並且返回對應連線的套接字.
3.處理沒一個客戶端的連線.實現接受跟傳送資料.
4.關閉一個連線.
其實就是講建立服務端網路做了一個封裝.
如下程式碼. 一個.h檔案.存放函式宣告.一個.cpp封裝了網路連線的程式碼.
.h檔案:
#pragma once #include "stdafx.h" #include <WinSock2.h> #include <iostream> #pragma comment(lib,"ws2_32.lib") using namespace std; #include "initSocket.h" void DebugLog(TCHAR *str); //初始化資料 int initSocket(); //1.建立套接字.繫結地址,開始監聽 SOCKET BindAnListen(int nBacklog); //接受連線分裝 SOCKET AccepeConnect(SOCKET hSocket); //接受跟傳送資料 BOOL ClientReadAnWriteData(SOCKET hSocket); //關閉資料連線 BOOL ColseConnect(SOCKET hSocket);
.cpp實現.
#include "initSocket.h" void DebugLog(TCHAR *str) { cout << str << WSAGetLastError() << endl; } //初始化資料 int initSocket() { WSADATA data; if (WSAStartup(MAKEWORD(2, 2), &data)) { DebugLog(TEXT("initsocket faile")); return 0; } } //1.建立套接字.繫結地址,開始監聽 SOCKET BindAnListen(int nBacklog) { //建立套接字 BOOL bRet = FALSE; SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == hSocket) { DebugLog(TEXT("BindAnListen Fail")); return INVALID_SOCKET; } //繫結套接字 sockaddr_in addr; addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //htonl addr_any addr.sin_family = AF_INET; addr.sin_port = htons(8524); bRet =bind(hSocket, (sockaddr *)&addr, sizeof(addr)); if (SOCKET_ERROR == bRet) { DebugLog(TEXT("bind fail")); closesocket(hSocket); WSACleanup(); return INVALID_SOCKET; } //監聽套接字 bRet = FALSE; bRet =listen(hSocket, nBacklog); if (SOCKET_ERROR ==bRet) { DebugLog(TEXT("Listen fail")); closesocket(hSocket); WSACleanup(); return INVALID_SOCKET; } return hSocket; } //接受連線分裝 SOCKET AccepeConnect(SOCKET hSocket) { sockaddr_in addr; int nSize = sizeof(addr); SOCKET hNewSocket = accept(hSocket, (LPSOCKADDR)&addr, &nSize); if (hNewSocket == INVALID_SOCKET) { DebugLog(TEXT("Accept An Connect Fail")); return INVALID_SOCKET; } return hNewSocket; } //接受跟傳送資料 BOOL ClientReadAnWriteData(SOCKET hSocket) { char szBuffer[1024] = { NULL }; int nBufferSzie = sizeof(szBuffer); //迴圈處理資料 int nRecvBytes = 0; do { nRecvBytes = recv(hSocket,szBuffer, nBufferSzie, 0); if (SOCKET_ERROR == nRecvBytes) { DebugLog(TEXT("Recv Data Fail")); return FALSE; } else if (0 != nRecvBytes) { szBuffer[nRecvBytes] = 0; cout << "接受到的資料為: " << szBuffer << endl; //接著迴圈傳送回去. int nSendDataBytes = 0; while (nSendDataBytes < nRecvBytes) { int nRetValue = send(hSocket, szBuffer, nBufferSzie, 0); if (nRetValue > 0) { nSendDataBytes = nSendDataBytes + nRetValue; //每次傳送的資料都增加.這樣就會發送過去了 } else if (nRetValue == SOCKET_ERROR) { DebugLog(TEXT("傳送資料失敗")); return FALSE; } else { //send 返回0 也就是send失敗了.客戶端關閉了 DebugLog(TEXT("傳送資料失敗,客戶端已經關閉了")); return FALSE; } } } } while (0 != nRecvBytes); return FALSE; } BOOL ColseConnect(SOCKET hSocket) { //shutdown 跟 closesocket一樣.不過 TCP 會發送一個FIN分段.給對方表名已經完成資料傳送 if (shutdown(hSocket,SD_SEND) == SOCKET_ERROR) { DebugLog(TEXT("關閉連線失敗")); return FALSE; } //注意.客戶端會發送一個數據.不寫也可以. return TRUE; }
上面的程式碼只是把我們網路建立的一些步驟給封裝了.並沒有實際編寫我們用的程式碼.
在main函式中使用.只服務一個socket操作
// Server.cpp : 定義控制檯應用程式的入口點。 // #include "initSocket.h" int main() { //初始化 initSocket(); //1.繫結並且監聽 SOCKET hSocket = BindAnListen(1); if (INVALID_SOCKET == hSocket) { DebugLog(TEXT("main Bind Fail")); goto Opt; } // 2.迴圈接受套接字連線 while (true)//主要程式碼是這裡. { //接受客戶端連線 SOCKET hRetSocket = AccepeConnect(hSocket); if (INVALID_SOCKET == hRetSocket) { DebugLog(TEXT("main accept Fail")); break; } //讀取資料. if (FALSE == ClientReadAnWriteData(hRetSocket)) { //只服務一個socket.對其進行讀取寫入操作.然後下方進行關閉. break; } if (ColseConnect(hRetSocket)) { break; } } Opt: getchar(); //等待一下.觀看錯誤內容 ColseConnect(hSocket); return 0; }
主要就是服務端的程式碼.客戶端進行傳送資料即可.
連結: LG5U9ug" target="_blank" rel="nofollow,noindex">https://pan.baidu.com/s/1_vdcmPE0cYaFPn9LG5U9ug 密碼:8ezo