Windows sockets网络开发-IOCP模型

1、服务器

#pragma once


#include <Windows.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

#define  LISTEN_PORT 5000
#define  BUFFER_SIZE 1024
#define  RECV_POSTED_KEY    0x0005
#define  EXIT_CODE     0x0000ffff

typedef struct st_per_io_handle_data{
	WSAOVERLAPPED overlapped;
	WSABUF wsa_buf;
	char buffer[BUFFER_SIZE];
	DWORD dwNumOfTransferred;
	DWORD flag;
	int operation_type;
}PerIOHandleData,*LPPerIOHandleData;

typedef struct st_completion_key_data{
	SOCKET sock_conn;
	SOCKADDR_STORAGE sockaddr;
}CompletionKeyData, *PCompletionKeyData;


DWORD WINAPI work_thread(void* lparam);

int iocp_server_daemon(){

	//create init io completion port with null - params
	HANDLE completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
	if(INVALID_HANDLE_VALUE == completion_port){
		printf("error:CreateIoCompletionPort failed!\n");
		
		WSACleanup();
		return -5;
	}
	//create work thread
	SYSTEM_INFO sys_info;
	GetSystemInfo(&sys_info);
	int cpu_num = sys_info.dwNumberOfProcessors;
	//thread num = cpus * 2
	for(int i = 0; i < cpu_num * 2; ++i){
		DWORD thread_id;
		HANDLE work_thread_handle = CreateThread(NULL, 0,  work_thread, completion_port, 0, &thread_id);
		CloseHandle(work_thread_handle);
	}

	WSADATA wsd;
	SOCKET sock_listen;
	int rc;

	if(WSAStartup(MAKEWORD(2,2), &wsd) != 0){
		printf("error:WSAStartup failed!\n");
		return -1;
	}
	//create socket
	sock_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(INVALID_SOCKET == sock_listen){
		printf("error:socket failed!\n");
		WSACleanup();
		return -2;
	}
	//set socket address
	sockaddr_in local_addr;
	local_addr.sin_family = AF_INET;
	local_addr.sin_port = htons(LISTEN_PORT);
	local_addr.sin_addr.s_addr = INADDR_ANY;
	//bind
	rc = bind(sock_listen, (struct sockaddr*)&local_addr, sizeof(sockaddr_in));
	if(SOCKET_ERROR == rc){
		printf("error:bind failed!\n");
		closesocket(sock_listen);
		WSACleanup();
		return -3;
	}
	//listen
	rc = listen(sock_listen, 5);
	if(SOCKET_ERROR == rc){
		printf("error:listen failed!\n");
		closesocket(sock_listen);
		WSACleanup();
		return -4;
	}

	//while - accept
	bool always_true = true;
	while (always_true){
		sockaddr_in remote;
		int remote_len = sizeof(remote);
		SOCKET sock_conn = accept(sock_listen, (sockaddr*)&remote, &remote_len);

		if(SOCKET_ERROR != sock_conn){

			 printf("Accepted client:%s:%d\n", inet_ntoa(remote.sin_addr), ntohs(remote.sin_port));
			
			 //注意:在此处用new有效
			 CompletionKeyData* pckd = new CompletionKeyData;
			 //或者用GlobalAlloc
			 //CompletionKeyData *pckd = (CompletionKeyData*)GlobalAlloc(GPTR, sizeof(CompletionKeyData));

			 ZeroMemory(pckd, sizeof(CompletionKeyData));
			 pckd->sock_conn = sock_conn;
			 memcpy(&pckd->sockaddr, &remote, sizeof(remote));
			  //绑定套接字和完成端口
			 CreateIoCompletionPort((HANDLE)sock_conn, completion_port, (ULONG_PTR)pckd, 0);

			 //也可以直接传入sock_conn
			//CreateIoCompletionPort((HANDLE)sock_conn, completion_port, (ULONG_PTR)sock_conn, 0)



			 //注意:在此处用new无效
			 //LPPerIOHandleData lpper_io_handle_data = new PerIOHandleData;
			 //为啥一定要用虚拟空间分配函数 GlobalAlloc或者HeapAlloc?
			 //LPPerIOHandleData lpper_io_handle_data = (LPPerIOHandleData)GlobalAlloc(GPTR, sizeof(PerIOHandleData));
			 //或者HeapAlloc, 推荐使用HeapAlloc
			 LPPerIOHandleData lpper_io_handle_data = (LPPerIOHandleData)HeapAlloc(GetProcessHeap(),
																				   HEAP_ZERO_MEMORY ,
																				   sizeof(PerIOHandleData));

			 ZeroMemory(&lpper_io_handle_data->overlapped, sizeof(OVERLAPPED));
			 lpper_io_handle_data->wsa_buf.buf = lpper_io_handle_data->buffer;
			 lpper_io_handle_data->wsa_buf.len = sizeof(lpper_io_handle_data->buffer);
			 lpper_io_handle_data->operation_type = RECV_POSTED_KEY;
			 //向操作系统投递重叠I/O接收数据请求
			 WSARecv(sock_conn, 
					 &lpper_io_handle_data->wsa_buf, 
					 1, 
					 &lpper_io_handle_data->dwNumOfTransferred, 
					 &lpper_io_handle_data->flag, //flag可以不用考虑,但是需要定义一个变量
					 &lpper_io_handle_data->overlapped, 
					 NULL);
		}
	}

	PostQueuedCompletionStatus(completion_port, 0, EXIT_CODE, NULL);
	CloseHandle(completion_port);
	closesocket(sock_listen);
	WSACleanup();
	return 0;
}


DWORD WINAPI work_thread(void* lparam){

	HANDLE completion_port = (HANDLE)(lparam);
	PCompletionKeyData pckd = NULL;
	LPPerIOHandleData lpper_io_handle_data = NULL;
	DWORD transferred = 0 ;
	SOCKET sock_conn;
	LPOVERLAPPED IpOverlapped = NULL; 

	while (true)
	{
		//如果主线程将sock_conn与完成端口关联时,CreateIoCompletionPort传入的是CompletionKeyData,
		//则GetQueuedCompletionStatus第三个参数应该采用PCompletionKeyData接收
		BOOL rc = GetQueuedCompletionStatus(completion_port,
											&transferred, 
											(PULONG_PTR)&pckd, //注意:这里传入的是指针地址
											(LPOVERLAPPED*)&lpper_io_handle_data, 
											INFINITE);

		//如果主线程将sock_conn与完成端口关联时,CreateIoCompletionPort传入的是sock_conn,
		//则GetQueuedCompletionStatus第三个参数直接用sock_conn接收
		//BOOL rc = GetQueuedCompletionStatus(completion_port, 
		//									&transferred,
		//									(PULONG_PTR)&sock_conn,
		//									(LPOVERLAPPED*)&lpper_io_handle_data, 
		//									INFINITE);

		//至于第四个参数 LPOVERLAPPED*,也可以采用下面方法
		//BOOL rc = GetQueuedCompletionStatus(completion_port, 
		//									&transferred, 
		//									(PULONG_PTR)&sock_conn,
		//									(LPOVERLAPPED*)&IpOverlapped, 
		//									INFINITE);
		//lpper_io_handle_data = (LPPerIOHandleData) CONTAINING_RECORD(IpOverlapped, 
		//															 PerIOHandleData, 
		//															 overlapped);

		if(0 == rc){
			DWORD err = GetLastError();
			return -1;
		}
		if(EXIT_CODE == transferred)
			return -1;

		if(lpper_io_handle_data->operation_type == RECV_POSTED_KEY){

			sock_conn = pckd->sock_conn;

			if(0 == transferred){

				closesocket(sock_conn);
				//GlobalFree(pckd);
				delete pckd;
				//delete lpper_io_handle_data;
				//GlobalFree(lpper_io_handle_data);
				HeapFree(GetProcessHeap(), 0, lpper_io_handle_data);
				continue;
			}
			
			int recv_len = transferred;
			int left_len = recv_len;
			int sent_len = 0;
			char *send_buffer = lpper_io_handle_data->buffer;
			while(left_len > 0){
					//echo data from client
				sent_len = 	send(sock_conn, send_buffer, left_len, 0);
				if(SOCKET_ERROR == sent_len){
					break;
				}
				left_len-=sent_len;
				send_buffer+=sent_len;
			}

			//printf("recv client data: %s \n", lpper_io_handle_data->buffer);

			//prepare next io request
			lpper_io_handle_data->wsa_buf.buf = lpper_io_handle_data->buffer;
			lpper_io_handle_data->wsa_buf.len = sizeof(lpper_io_handle_data->buffer);
			lpper_io_handle_data->operation_type = RECV_POSTED_KEY;
			ZeroMemory(&lpper_io_handle_data->overlapped, sizeof(OVERLAPPED));
			WSARecv(sock_conn, 
					&lpper_io_handle_data->wsa_buf, 
					1, 
					&lpper_io_handle_data->dwNumOfTransferred, 
					&lpper_io_handle_data->flag, //flag可以不用考虑,但是需要定义一个变量
					&lpper_io_handle_data->overlapped, 
					NULL);

		}
	}
	

	return 0;
}

2、客户端

// vs_demo.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

#define  LISTEN_PORT 5000
#define  BUFFER_SIZE 1024

int client_daemon(){
	WSADATA wsd;
	if(WSAStartup(MAKEWORD(2,2),&wsd) !=0){
		printf("error:WSAStartup failed!\n");
		return -1;
	}
	SOCKET sock_conn;
	sock_conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(INVALID_SOCKET == sock_conn){
		printf("error:socket failed!\n");
		WSACleanup();
		return -2;
	}
	//server address
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(LISTEN_PORT);
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	//connect
	int rc = connect(sock_conn, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if(SOCKET_ERROR == rc){
		printf("error:connect failed!\n");
		closesocket(sock_conn);
		WSACleanup();
		return -3;
	}
	while (1)
	{
		//send data
		char buffer[BUFFER_SIZE] = {0};
		strcpy_s(buffer, sizeof(buffer), "Hello,Server!");
		rc = send(sock_conn, buffer,(int) strlen(buffer), 0);
		//recv response
		char buffer1[BUFFER_SIZE] = {0};
		//	strcpy_s(buffer1, sizeof(buffer1), "this is tcp data!");
		rc = recv(sock_conn, buffer1, sizeof(buffer1), 0);
		printf("from server:%s\n", buffer1);
		Sleep(500);
	}
	


	if(SOCKET_ERROR == rc){
		printf("error:send failed!\n");
		closesocket(sock_conn);
		WSACleanup();
		return -4;
	}

	//close socket
	closesocket(sock_conn);
	WSACleanup();
	return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
	client_daemon();
	getchar();
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值