丶阻塞模型簡介
不知道大家有沒有注意到.我們客戶端 或者服務端.的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;
}