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