區別:
阻塞式迭代模式:
每次只服務一個連接,只有在服務完當前服務器連接之後,纔會繼續服務下一個客戶端連接
阻塞式併發連接模式:
通過多線程,可以同時服務多個連接,每一個線程處理一個客戶端連接
步驟:
阻塞式迭代模式步驟:
1,先連接處理,綁定本地地址和監聽
2,接受一個客戶端連接並返回對應的連接的套接字
3,處理一個客戶端的連接,實現接受和發送數據
4,關閉一個服務
5,服務器主體
阻塞式併發模式:
和迭代模式基本相同,只是在於處理客戶端連接上,我們需要用到多線程來處理客戶端連接,以給予服務端同時處理業務的能力
多線程函數:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // pointer to security attributes
DWORD dwStackSize, // initial thread stack size
LPTHREAD_START_ROUTINE lpStartAddress, // pointer to thread function
LPVOID lpParameter, // argument for new thread
DWORD dwCreationFlags, // creation flags
LPDWORD lpThreadId // pointer to receive thread ID
);
第一個參數是指向SECURITY_ATTRIBUTES型態的結構的指針。一般取值0
第二個參數是用於新線程的初始堆棧大小,默認值爲0。在任何情況下,Windows根據需要動態延長堆棧的大小。
第三個參數是指向線程函數的指標。函數名稱沒有限制,但是必須以下列形式聲明:
DWORD WINAPI ThreadProc (PVOID pParam) ;
第四個參數爲傳遞給ThreadProc的參數。這樣主線程和從屬線程就可以共享數據。
第五個參數通常爲0,但當建立的線程不馬上執行時爲旗標CREATE_SUSPENDED。線程將暫停直到呼叫ResumeThread來恢復線程的執行爲止。
第六個參數是一個指標,指向接受執行緒ID值的變量。
代碼:
迭代模型代碼:
// 阻塞式迭代模型.cpp : 定義控制檯應用程序的入口點。
#include "stdafx.h"
#include<winsock2.h>
#include<iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define nSERPort 8000
#define nBufMaxSize 1024
//封裝的打印
void debugLog(char *logStr)
{
cout << logStr << WSAGetLastError() << endl;
}
//初始化Socket
BOOL InitSocket()
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
{
debugLog("InitSocket -> WSAStartup error");
return FALSE;
}
}
//先創建套接字 綁定本地地址 然後開始監聽
SOCKET Bind_Listen(int nBacklog)
{
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET)
{
debugLog("Bind_Listen -> socket error!");
return INVALID_SOCKET;
}
sockaddr_in saSerAddr;
saSerAddr.sin_family = AF_INET;
saSerAddr.sin_addr.s_addr = htonl(ADDR_ANY);
saSerAddr.sin_port = htons(nSERPort);
if (bind(hSocket, (LPSOCKADDR)&saSerAddr, sizeof(saSerAddr)))
{
debugLog("Bind_Listen -> bind error");
closesocket(hSocket);
return INVALID_SOCKET;
}
if (listen(hSocket, nBacklog) == SOCKET_ERROR)
{
closesocket(hSocket);
debugLog("Bind_Listen -> listen error");
return INVALID_SOCKET;
}
return hSocket;
}
//接受一個客戶端連接並返回對應的連接的套接字
SOCKET AcceptConnetion(SOCKET hScoket)
{
sockaddr_in saConAddr;
int nSize = sizeof(saConAddr);
SOCKET sd = accept(hScoket, (LPSOCKADDR)&saConAddr, &nSize);
if (sd == INVALID_SOCKET)
{
debugLog("AcceptConnetion -> connect error");
return INVALID_SOCKET;
}
return sd;
}
//處理一個客戶端的連接,實現接受和發送數據
BOOL ClientConFun(SOCKET sd)
{
char Buf[nBufMaxSize];
int nRetByte;
//循環處理數據
do
{
//接受到來自客戶端的數據
nRetByte = recv(sd, Buf, nBufMaxSize, 0);
if (nRetByte == SOCKET_ERROR)
{
debugLog("ClientConFun -> recv error!");
return FALSE;
}
else if (nRetByte != 0)
{
Buf[nRetByte] = 0;
cout << "接收到一條數據:" << Buf << endl;
int nSend = 0;
while (nSend < nRetByte)
{
//把接收到的數據回發過去
int nTemp = send(sd, &Buf[nSend], nRetByte - nSend, 0);
if (nTemp>0)
{
nSend += nTemp;
}
else if (nTemp == SOCKET_ERROR)
{
debugLog("ClientConFun -> send error");
return FALSE;
}
else
{
//send 返回0由於此時send<nretByte 也就是說
//數據還沒有發送出去,表示客戶端被意外關閉了
debugLog("ClientConFun -> send ->close error");
return FALSE;
}
}
}
} while (nRetByte != 0);
return TRUE;
}
//關閉一個連接
BOOL CloseConnect(SOCKET sd)
{
//首先發送一個TCP FIN分段,向對方表明已經完成數據發送
if (shutdown(sd, SD_SEND) == SOCKET_ERROR)
{
debugLog("CloseConnect -> shutdown error");
return FALSE;
}
char Buf[nBufMaxSize];
int nRetByte;
do
{
nRetByte = recv(sd, Buf, nBufMaxSize, 0);
if (nRetByte == SOCKET_ERROR)
{
debugLog("CloseConnect ->recv error");
break;
}
else if (nRetByte > 0)
{
debugLog("CloseConnect 錯誤的接收數據");
break;
}
} while (nRetByte != 0);
if (closesocket(sd) == SOCKET_ERROR)
{
debugLog("CloseConnect ->closeSocket error");
return FALSE;
}
return TRUE;
}
//服務器主體
void MyTcpSerFun()
{
SOCKET hSocket = Bind_Listen(1);
if (hSocket == INVALID_SOCKET)
{
debugLog("MyTcpSocket -> Bind_Listen error");
return;
}
while (TRUE)
{
//返回客戶端的套接字
SOCKET sd = AcceptConnetion(hSocket);
if (sd == INVALID_SOCKET)
{
debugLog("MyTcpSerFun ->AcceptConnection error");
break;
}
//客戶端處理
if (ClientConFun(sd) == FALSE)
{
//break;
}
//關閉一個客戶端的連接
if (CloseConnect(sd) == FALSE)
{
break;
}
}
if (closesocket(hSocket) == SOCKET_ERROR)
{
debugLog("MyTcpSerFun -> closesocket error");
return;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//初始化
InitSocket();
//業務數據處理
MyTcpSerFun();
//釋放WinSocket
WSACleanup();
system("pause");
return 0;
}
併發模型代碼:
// 阻塞式併發模型.cpp : 定義控制檯應用程序的入口點。
#include "stdafx.h"
#include<winsock2.h>
#include<iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define nSERPort 8000
#define nBufMaxSize 1024
//封裝的打印
void debugLog(char *logStr)
{
cout << logStr << WSAGetLastError() << endl;
}
//初始化Socket
BOOL InitSocket()
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
{
debugLog("InitSocket -> WSAStartup error");
return FALSE;
}
}
//先創建套接字 綁定本地地址 然後開始監聽
SOCKET Bind_Listen(int nBacklog)
{
SOCKET hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == INVALID_SOCKET)
{
debugLog("Bind_Listen -> socket error!");
return INVALID_SOCKET;
}
sockaddr_in saSerAddr;
saSerAddr.sin_family = AF_INET;
saSerAddr.sin_addr.s_addr = htonl(ADDR_ANY);
saSerAddr.sin_port = htons(nSERPort);
if (bind(hSocket, (LPSOCKADDR)&saSerAddr, sizeof(saSerAddr)))
{
debugLog("Bind_Listen -> bind error");
closesocket(hSocket);
return INVALID_SOCKET;
}
if (listen(hSocket, nBacklog) == SOCKET_ERROR)
{
closesocket(hSocket);
debugLog("Bind_Listen -> listen error");
return INVALID_SOCKET;
}
return hSocket;
}
//接受一個客戶端連接並返回對應的連接的套接字
SOCKET AcceptConnetion(SOCKET hScoket)
{
sockaddr_in saConAddr;
int nSize = sizeof(saConAddr);
SOCKET sd = accept(hScoket, (LPSOCKADDR)&saConAddr, &nSize);
if (sd == INVALID_SOCKET)
{
debugLog("AcceptConnetion -> connect error");
return INVALID_SOCKET;
}
return sd;
}
//處理一個客戶端的連接,實現接受和發送數據
BOOL ClientConFun(SOCKET sd)
{
char Buf[nBufMaxSize];
int nRetByte;
//循環處理數據
do
{
//接受到來自客戶端的數據
nRetByte = recv(sd, Buf, nBufMaxSize, 0);
if (nRetByte == SOCKET_ERROR)
{
debugLog("ClientConFun -> recv error!");
return FALSE;
}
else if (nRetByte != 0)
{
Buf[nRetByte] = 0;
cout << "接收到一條數據:" << Buf << endl;
int nSend = 0;
while (nSend < nRetByte)
{
//把接收到的數據回發過去
int nTemp = send(sd, &Buf[nSend], nRetByte - nSend, 0);
if (nTemp>0)
{
nSend += nTemp;
}
else if (nTemp == SOCKET_ERROR)
{
debugLog("ClientConFun -> send error");
return FALSE;
}
else
{
//send 返回0由於此時send<nretByte 也就是說
//數據還沒有發送出去,表示客戶端被意外關閉了
debugLog("ClientConFun -> send ->close error");
return FALSE;
}
}
}
} while (nRetByte != 0);
return TRUE;
}
//關閉一個連接
BOOL CloseConnect(SOCKET sd)
{
//首先發送一個TCP FIN分段,向對方表明已經完成數據發送
if (shutdown(sd, SD_SEND) == SOCKET_ERROR)
{
debugLog("CloseConnect -> shutdown error");
return FALSE;
}
char Buf[nBufMaxSize];
int nRetByte;
do
{
nRetByte = recv(sd, Buf, nBufMaxSize, 0);
if (nRetByte == SOCKET_ERROR)
{
debugLog("CloseConnect ->recv error");
break;
}
else if (nRetByte > 0)
{
debugLog("CloseConnect 錯誤的接收數據");
break;
}
} while (nRetByte != 0);
if (closesocket(sd) == SOCKET_ERROR)
{
debugLog("CloseConnect ->closeSocket error");
return FALSE;
}
return TRUE;
}
//線程處理業務
DWORD WINAPI ClientThreadFun(LPVOID lpParam)
{
SOCKET sd = (SOCKET)lpParam;
//客戶端處理
if (ClientConFun(sd) == FALSE)
{
//break;
}
//關閉一個客戶端的連接
if (CloseConnect(sd) == FALSE)
{
//break;
}
return 0;
}
//服務器主體
void MyTcpSerFun()
{
SOCKET hSocket = Bind_Listen(1);
if (hSocket == INVALID_SOCKET)
{
debugLog("MyTcpSocket -> Bind_Listen error");
return;
}
while (TRUE)
{
//返回客戶端的套接字
SOCKET sd = AcceptConnetion(hSocket);
if (sd == INVALID_SOCKET)
{
debugLog("MyTcpSerFun ->AcceptConnection error");
break;
}
//當接受到客戶端的請求連接,我們就爲他開一個線程
DWORD dwThreadId;
HANDLE hThread = CreateThread(0, 0, ClientThreadFun, (LPVOID)sd, 0, &dwThreadId);
if (hThread)
{
CloseHandle(hThread);
}
#if 0
//客戶端處理
if (ClientConFun(sd) == FALSE)
{
//break;
}
//關閉一個客戶端的連接
if (CloseConnect(sd) == FALSE)
{
break;
}
#endif
}
if (closesocket(hSocket) == SOCKET_ERROR)
{
debugLog("MyTcpSerFun -> closesocket error");
return;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//初始化
InitSocket();
//業務數據處理
MyTcpSerFun();
//釋放WinSocket
WSACleanup();
system("pause");
return 0;
}