windows socket编程入门示例3

// 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;
}

你可能感兴趣的:(c/c++,windows,网络)