丶阻塞模型簡(jiǎn)介
不知道大家有沒(méi)有注意到.我們客戶(hù)端 或者服務(wù)端.的TCP 收發(fā)數(shù)據(jù)的時(shí)候(send/recv)如果接受不到數(shù)據(jù)就一直不返回.從而造成我們網(wǎng)絡(luò)的阻塞.程序無(wú)法正常執(zhí)行.
不過(guò)針對(duì)這一方法.我們可以開(kāi)一個(gè)線(xiàn)程去專(zhuān)門(mén)接受數(shù)據(jù).或者發(fā)送數(shù)據(jù).
這個(gè)就是我們常說(shuō)的阻塞.
只要我們創(chuàng)建的套接字都是阻塞模型. 就是說(shuō)數(shù)據(jù)接受不到不返回.
我們可以設(shè)置為非阻塞.就是不管數(shù)據(jù)有沒(méi)有來(lái)到都會(huì)返回.如果來(lái)到.會(huì)有通知.我們可以編程接受數(shù)據(jù).
設(shè)置非阻塞模式方法
ioctlsocket(SOCKET s, long cmd, u_long *arpg);
改變套接字模式.為飛租she.
二丶阻塞模式迭代模式 與 并發(fā)連接模式
1.阻塞模式的迭代模式 就是指每次只服務(wù)一個(gè)連接.只有服務(wù)完當(dāng)前的客戶(hù)端連接之后.才會(huì)繼續(xù)服務(wù)下一個(gè)客戶(hù)連接
2.并發(fā)連接模式 通過(guò)多線(xiàn)程.可以同時(shí)服務(wù)多個(gè)鏈接.沒(méi)一個(gè)線(xiàn)程處理一個(gè)客戶(hù)端的連接.
阻塞迭代模式步驟
1.生成一個(gè)函數(shù).綁定本地地址跟監(jiān)聽(tīng).
2.生成一個(gè)函數(shù).專(zhuān)門(mén)接受一個(gè)客戶(hù)端連接.并且返回對(duì)應(yīng)連接的套接字.
3.處理沒(méi)一個(gè)客戶(hù)端的連接.實(shí)現(xiàn)接受跟發(fā)送數(shù)據(jù).
4.關(guān)閉一個(gè)連接.
其實(shí)就是講創(chuàng)建服務(wù)端網(wǎng)絡(luò)做了一個(gè)封裝.
如下代碼. 一個(gè).h文件.存放函數(shù)聲明.一個(gè).cpp封裝了網(wǎng)絡(luò)連接的代碼.
.h文件:
復(fù)制代碼
#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); //初始化數(shù)據(jù) int initSocket(); //1.創(chuàng)建套接字.綁定地址,開(kāi)始監(jiān)聽(tīng) SOCKET BindAnListen(int nBacklog); //接受連接分裝 SOCKET AccepeConnect(SOCKET hSocket); //接受跟發(fā)送數(shù)據(jù) BOOL ClientReadAnWriteData(SOCKET hSocket); //關(guān)閉數(shù)據(jù)連接 BOOL ColseConnect(SOCKET hSocket);
復(fù)制代碼
.cpp實(shí)現(xiàn).
復(fù)制代碼
#include "initSocket.h" void DebugLog(TCHAR *str)
{
cout << str << WSAGetLastError() << endl;
} //初始化數(shù)據(jù) int initSocket()
{
WSADATA data; if (WSAStartup(MAKEWORD(2, 2), &data))
{
DebugLog(TEXT("initsocket faile")); return 0;
}
} //1.創(chuàng)建套接字.綁定地址,開(kāi)始監(jiān)聽(tīng) SOCKET BindAnListen(int nBacklog)
{ //創(chuàng)建套接字 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;
} //監(jiān)聽(tīng)套接字 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;
} //接受跟發(fā)送數(shù)據(jù) BOOL ClientReadAnWriteData(SOCKET hSocket)
{ char szBuffer[1024] = { NULL }; int nBufferSzie = sizeof(szBuffer); //循環(huán)處理數(shù)據(jù) 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 << "接受到的數(shù)據(jù)為: " << szBuffer << endl; //接著循環(huán)發(fā)送回去. int nSendDataBytes = 0; while (nSendDataBytes < nRecvBytes)
{ int nRetValue = send(hSocket, szBuffer, nBufferSzie, 0); if (nRetValue > 0)
{
nSendDataBytes = nSendDataBytes + nRetValue; //每次發(fā)送的數(shù)據(jù)都增加.這樣就會(huì)發(fā)送過(guò)去了 } else if (nRetValue == SOCKET_ERROR)
{
DebugLog(TEXT("發(fā)送數(shù)據(jù)失敗")); return FALSE;
} else { //send 返回0 也就是send失敗了.客戶(hù)端關(guān)閉了 DebugLog(TEXT("發(fā)送數(shù)據(jù)失敗,客戶(hù)端已經(jīng)關(guān)閉了")); return FALSE;
}
}
}
} while (0 != nRecvBytes); return FALSE;
}
BOOL ColseConnect(SOCKET hSocket)
{ //shutdown 跟 closesocket一樣.不過(guò) TCP 會(huì)發(fā)送一個(gè)FIN分段.給對(duì)方表名已經(jīng)完成數(shù)據(jù)發(fā)送 if (shutdown(hSocket,SD_SEND) == SOCKET_ERROR)
{
DebugLog(TEXT("關(guān)閉連接失敗")); return FALSE;
} //注意.客戶(hù)端會(huì)發(fā)送一個(gè)數(shù)據(jù).不寫(xiě)也可以. return TRUE;
}
復(fù)制代碼
上面的代碼只是把我們網(wǎng)絡(luò)創(chuàng)建的一些步驟給封裝了.并沒(méi)有實(shí)際編寫(xiě)我們用的代碼.
在main函數(shù)中使用.只服務(wù)一個(gè)socket操作
復(fù)制代碼
// Server.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。 // #include "initSocket.h" int main()
{ //初始化 initSocket(); //1.綁定并且監(jiān)聽(tīng) SOCKET hSocket = BindAnListen(1); if (INVALID_SOCKET == hSocket)
{
DebugLog(TEXT("main Bind Fail")); goto Opt;
} // 2.循環(huán)接受套接字連接 while (true) //主要代碼是這里.
{ //接受客戶(hù)端連接 SOCKET hRetSocket = AccepeConnect(hSocket); if (INVALID_SOCKET == hRetSocket)
{
DebugLog(TEXT("main accept Fail")); break;
} //讀取數(shù)據(jù). if (FALSE == ClientReadAnWriteData(hRetSocket))
{ //只服務(wù)一個(gè)socket.對(duì)其進(jìn)行讀取寫(xiě)入操作.然后下方進(jìn)行關(guān)閉. break;
} if (ColseConnect(hRetSocket))
{ break;
}
}
Opt:
getchar(); //等待一下.觀(guān)看錯(cuò)誤內(nèi)容 ColseConnect(hSocket); return 0;
}