// Lock.h
#ifndef _Lock_H
#define _Lock_H
#include
class CriticalSection
{
private:
CRITICAL_SECTION g_cs; //临界区
public:
CriticalSection();
~CriticalSection();
void Lock();
void UnLock();
};
#endif
// Lock.cpp
#include "Lock.h"
CriticalSection::CriticalSection()
{
InitializeCriticalSection(&g_cs); //必须先初始化临界区
}
CriticalSection::~CriticalSection()
{
DeleteCriticalSection(&g_cs); //删除临界区
}
void CriticalSection::Lock()
{
EnterCriticalSection(&g_cs); //进入临界区(申请钥匙,得到钥匙)
}
void CriticalSection::UnLock()
{
LeaveCriticalSection(&g_cs); //离开(放弃钥匙,不再拥有)
}
// tcpSocketSelect.cpp
#include
#include
#include "Lock.h"
#pragma comment(lib,"ws2_32.lib")
#define IP_ADDRESS "127.0.0.1"
#define PORT 5678
#define MSGSIZE 8192 // window操作系统默认socket收发缓存大小是8K
static volatile bool isRunning;
static int volatile g_iTotalConn = 0;
static SOCKET g_CliSocketArr[FD_SETSIZE];// FD是File Describle文件描述符,也就是socket文件描述符(句柄)
CriticalSection criticalSectionLock;
class SelectModel
{
public:
static DWORD WINAPI WorkerThread(LPVOID lpParameter); // 服务器端工作线程
int Process();
};
int SelectModel::Process()
{
int retCode;
WORD wVersionRequested;
WSADATA wsaData;
SOCKET sListen, sClient;
SOCKADDR_IN local, client;
int iaddrSize = sizeof(SOCKADDR_IN);
DWORD dwThreadId;
// Initialize Windows socket library
wVersionRequested = MAKEWORD(2, 2);
retCode = WSAStartup(wVersionRequested, &wsaData);
if (retCode != 0)
{
fprintf(stderr, "Load WSADTATA failed!\n");
return -1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
fprintf(stderr, "WSADTATA version error!\n");
WSACleanup();
return -1;
}
// Create listening socket
sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sListen == INVALID_SOCKET)
{
fprintf(stderr, "invalid socket !\n");
WSACleanup();
return -1;
}
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
int opt = 1;
retCode = setsockopt(sListen, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
if (retCode < 0)
{
printf("setsockopt Failed.\n");
closesocket(sListen);
WSACleanup();
return -1;
}
retCode = bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));
if (retCode == SOCKET_ERROR)
{
printf("bind error !");
closesocket(sListen);
WSACleanup();
return -1;
}
retCode = listen(sListen, 16);
if (retCode == SOCKET_ERROR)
{
printf("listen error !");
closesocket(sListen);
WSACleanup();
return -1;
}
isRunning = true;
// Create worker thread
HANDLE serviceSubThread = CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
if (serviceSubThread == NULL)
{
fprintf(stderr, "Create service sub thread failed!\n");
goto END;
}
fprintf(stderr, "Server is running......\n");
while (TRUE)
{
sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
if (sClient == INVALID_SOCKET)
{
fprintf(stderr, "Invalid socket, continue to accept new socket!\n");
continue;
}
criticalSectionLock.Lock();
if (g_iTotalConn < FD_SETSIZE)
{
g_CliSocketArr[g_iTotalConn++] = sClient;
printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
}
else
{
closesocket(sClient);
printf("refuse client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
}
criticalSectionLock.UnLock();
}
END:
closesocket(sListen);
WSACleanup();
return 0;
}
DWORD SelectModel::WorkerThread(LPVOID lpParam)
{
int i = 0;
fd_set fdread;
int ret = 0;
struct timeval tv = { 1, 0 };// 1是阻塞等待1秒钟返回一次,后面的0是0毫秒
char szMessage[MSGSIZE];
while (isRunning)
{
FD_ZERO(&fdread);//将fdread初始化空集
int totalConn = g_iTotalConn;
for (i = 0; i < totalConn; i++)// 可以在这里分段处理64个,用以支持多于64个的连接select.
FD_SET(g_CliSocketArr[i], &fdread);//将要检查的套接口加入到集合中
ret = select(MSGSIZE + 1, &fdread, NULL, NULL, &tv);//每隔一段时间,检查可读性的套接口,将可读的拷贝到fdread里面
if (ret == 0)
continue;
for (i = 0; i < totalConn; i++)
{
if (FD_ISSET(g_CliSocketArr[i], &fdread)) //如果可读
{
// A read event happened on g_CliSocketArr
ret = recv(g_CliSocketArr[i], szMessage, MSGSIZE, 0);
if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
{
// Client socket closed
printf("Client socket %d closed.\n", g_CliSocketArr[i]);
closesocket(g_CliSocketArr[i]);
criticalSectionLock.Lock();
for (int j = i; j < g_iTotalConn - 1; j++)
g_CliSocketArr[j] = g_CliSocketArr[j + 1];
g_iTotalConn--;
i--;
totalConn--;
criticalSectionLock.UnLock();
}
else
{
printf("ret = %d \n", ret);
if (ret == MSGSIZE || ret < 0)
{
fprintf(stderr, "an error happened.\n");
closesocket(g_CliSocketArr[i]);
criticalSectionLock.Lock();
for (int j = i; j < g_iTotalConn - 1; j++)
g_CliSocketArr[j] = g_CliSocketArr[j + 1];
g_iTotalConn--;
i--;
totalConn--;
criticalSectionLock.UnLock();
}
else
{
szMessage[ret] = '\0';
// 根据需要处理接收到的消息
printf("Received a message from client:%s\n", szMessage);
// 根据需要是否需要发送消息
send(g_CliSocketArr[i], szMessage, strlen(szMessage), 0);
}
}
}// 可读
//else if (FD_ISSET(g_CliSocketArr[i], &fdread))
}// for
}// while
return 0;
}
int main()
{
SelectModel tcpServer;
tcpServer.Process();
return 0;
}
// tcpSocketSelectClient.cpp
#include
#include
#pragma comment(lib,"ws2_32.lib")
#define IP_ADDRESS "127.0.0.1"
#define PORT 5678
#define MSGSIZE 8192 // window操作系统默认socket收发缓存大小是8K
WSADATA wsaData;
SOCKET sClient;
SOCKADDR_IN server;
char szMessage[MSGSIZE];
static void CheckBuffer(SOCKET &socket)
{
//window 7,sock2,默认内核发送缓存和接收缓存都是8K.
int sendbuflen = 0;
int len = sizeof(sendbuflen);
getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char*)&sendbuflen, &len);
printf("default,sendbuf:%d\n", sendbuflen);
getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&sendbuflen, &len);
printf("default,recvbuf:%d\n", sendbuflen);
/*sendbuflen = 10240;
setsockopt(clientSocket, SOL_SOCKET, SO_SNDBUF, (void*)&sendbuflen, len); */
}
static bool LoadWSAData(WSADATA &wsaData)
{
// Initialize Windows socket library
WORD wVersionRequested = MAKEWORD(2, 2);
// MAKEWORD的作用,类似下面
WORD wHigh = 2;
WORD wLow = 2;
WORD wAll = ((wHigh << 8) | wLow);
// 初始化只需要传入版本号,和WSADATA就可以了
int reqErr = ::WSAStartup(wVersionRequested, &wsaData);
if (reqErr != 0)
{
printf("加载请求指定版本的windows socket api DLL 失败");
return false;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
printf("Could not find a usable version of Winsock.dll\n");
::WSACleanup();
return false;
}
else
{
printf("The Winsock 2.2 dll was found okay\n");
return true;
}
}
static void ReleaseWSAData()
{
::WSACleanup();
}
int main()
{
int nLen = sizeof(SOCKADDR_IN);
if (!LoadWSAData(wsaData))
{
fprintf(stderr, "Load WSADTATA failed!\n");
return 0;
}
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 协议族,socket类型,具体协议
if (INVALID_SOCKET == sClient)
{
ReleaseWSAData();
printf("Get Socket Error: INVALID_SOCKET.\n");
return 0;
}
CheckBuffer(sClient);
memset(&server, 0, sizeof(SOCKADDR_IN));
server.sin_family = AF_INET;
server.sin_addr.S_un.S_addr = inet_addr(IP_ADDRESS);
server.sin_port = htons(PORT);
int nConnect = connect(sClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));
if (SOCKET_ERROR == nConnect)
{
printf("Socket connnect Error:%d\n", WSAGetLastError());
closesocket(sClient);
ReleaseWSAData();
return 0;
}
int messageLength = 0;
while (TRUE)
{
memset(szMessage, 0, sizeof(szMessage));
printf("请输入发送到服务器的消息:");
fgets(szMessage, sizeof(szMessage), stdin);
messageLength = strlen(szMessage);
if (messageLength < 2)
{
fprintf(stderr, "你没有输入消息!\n");
continue;
}
int retCode = send(sClient, szMessage, strlen(szMessage), 0);// strlen求得的字符串长度不包含'\0'
if (SOCKET_ERROR == retCode)
printf("Send Copy data kernel buffer is too small or network shutdown!\n");
if (retCode == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
{
fprintf(stderr, "Server has disconneted, send data to server failed!\n");
break;
}
else if (retCode == SOCKET_ERROR)
{
printf("Send data to Server failed!\n");
break;
}
if (retCode != SOCKET_ERROR)
printf("Send data to Server finished.\n");
int nRecvRes = recv(sClient, szMessage, MSGSIZE, 0);
if (nRecvRes > 0)
{
szMessage[nRecvRes] = '\0';
printf("接收到服务器发来的消息 : %s\n", szMessage);
}
else if (nRecvRes == 0 || (nRecvRes == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET))
{
printf("Server Connection Close.\n");
break;
}
else if (nRecvRes == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
{
fprintf(stdout, "非阻塞类型返回\n");
continue;
}
else
{
printf("Unknow recv error code: %d\n", WSAGetLastError());
break;
}
}
closesocket(sClient);
ReleaseWSAData();
return 0;
}