分享
 
 
 

socket编程总结(2)

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

当然TCP方式的模型还有事件选择模型。

就是把所有的网络事件和我们的一个程序里定义的事件梆定。

这个有它的好处,可能可以让我们更好的写一个线程来管理

接收与发送。

现在来讲一下一个完成端口模型。

完成端口

一个完成端口其实就是一个通知队列,由操作系统把已经完成的重叠I/O请求的通知

放入其中。当某项I/O操作一旦完成,某个可以对该操作结果进行处理的工作者线程

就会收到一则通知。而套接字在被创建后,可以在任何时候与某个完成端口进行关

联。

步骤:

1、创建一个空的完成端口;

2、得到本地机器的CPU个数;

3、开启CPU*2个工作线程(又名线程池),全部都在等待完成端口的完成包;

4、创建TCP的监听socket,使用事件邦定,创建监听线程;

5、当有人连接进入的时候,将Client socket保存到一个我们自己定义的关键键,

并把它与我们创建的完成端口关联;

6、使用WSARecv和WSASend函数投递一些请求,这是使用重叠I/O的方式;

7、重复5~6;

注:1、重叠I/O的方式中,接收与发送数据包的时候,一定要进行投递请求这是

它们这个体系结构的特点

当然,在完成端口方式中,不是直接使用的WSARecv和WSASend函数进行请求

的投递的。而是使用的ReadFile,Write的方式

2、完成端口使用了系统内部的一些模型,所以我们只要按照一定的顺序调用就

可以完成了。

3、完成端口是使用在这样的情况下,有成千上万的用户连接的时候,它能够

保证性能不会降低。

#include <winsock2.h>

#include <windows.h>

#include <stdio.h>

#define PORT 5150

#define DATA_BUFSIZE 8192

//关键项

typedef struct

{

OVERLAPPED Overlapped;

WSABUF DataBuf;

CHAR Buffer[DATA_BUFSIZE];

DWORD BytesSEND;

DWORD BytesRECV;

} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;

typedef struct

{

SOCKET Socket;

} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);

void main(void)

{

SOCKADDR_IN InternetAddr;

SOCKET Listen;

SOCKET Accept;

HANDLE CompletionPort;

SYSTEM_INFO SystemInfo;

LPPER_HANDLE_DATA PerHandleData;

LPPER_IO_OPERATION_DATA PerIoData;

int i;

DWORD RecvBytes;

DWORD Flags;

DWORD ThreadID;

WSADATA wsaData;

DWORD Ret;

if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)

{

printf("WSAStartup failed with error %d\n", Ret);

return;

}

//打开一个空的完成端口

if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)

{

printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());

return;

}

// Determine how many processors are on the system.

GetSystemInfo(&SystemInfo);

// 开启cpu个数的2倍个的线程

for(i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)

{

HANDLE ThreadHandle;

// Create a server worker thread and pass the completion port to the thread.

if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,

0, &ThreadID)) == NULL)

{

printf("CreateThread() failed with error %d\n", GetLastError());

return;

}

// Close the thread handle

CloseHandle(ThreadHandle);

}

//打开一个服务器socket

if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,

WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)

{

printf("WSASocket() failed with error %d\n", WSAGetLastError());

return;

}

InternetAddr.sin_family = AF_INET;

InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

InternetAddr.sin_port = htons(PORT);

if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)

{

printf("bind() failed with error %d\n", WSAGetLastError());

return;

}

if (listen(Listen, 5) == SOCKET_ERROR)

{

printf("listen() failed with error %d\n", WSAGetLastError());

return;

}

//开始接收从客户端来的连接

while(TRUE)

{

if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)

{

printf("WSAAccept() failed with error %d\n", WSAGetLastError());

return;

}

// 创建一个关键项用于保存这个客户端的信息,用户接收发送的重叠结构,

// 还有使用到的缓冲区

if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR,

sizeof(PER_HANDLE_DATA))) == NULL)

{

printf("GlobalAlloc() failed with error %d\n", GetLastError());

return;

}

// Associate the accepted socket with the original completion port.

printf("Socket number %d connected\n", Accept);

PerHandleData->Socket = Accept;

//与我们的创建的那个完成端口关联起来,将关键项也与指定的一个完成端口关联

if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,

0) == NULL)

{

printf("CreateIoCompletionPort failed with error %d\n", GetLastError());

return;

}

// 投递一次接收,由于接收都需要使用这个函数来投递一个接收的准备

if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR, sizeof(PER_IO_OPERATION_DATA))) == NULL)

{

printf("GlobalAlloc() failed with error %d\n", GetLastError());

return;

}

ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

PerIoData->BytesSEND = 0;

PerIoData->BytesRECV = 0;

PerIoData->DataBuf.len = DATA_BUFSIZE;

PerIoData->DataBuf.buf = PerIoData->Buffer;

Flags = 0;

if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,

&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)

{

if (WSAGetLastError() != ERROR_IO_PENDING)

{

printf("WSARecv() failed with error %d\n", WSAGetLastError());

return;

}

}

}

}

//工作线程

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)

{

HANDLE CompletionPort = (HANDLE) CompletionPortID;

DWORD BytesTransferred;

LPOVERLAPPED Overlapped;

LPPER_HANDLE_DATA PerHandleData;

LPPER_IO_OPERATION_DATA PerIoData;

DWORD SendBytes, RecvBytes;

DWORD Flags;

while(TRUE)

{

//完成端口有消息来了

if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,

(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)

{

printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());

return 0;

}

//是不是有人退出了

if (BytesTransferred == 0)

{

printf("Closing socket %d\n", PerHandleData->Socket);

if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)

{

printf("closesocket() failed with error %d\n", WSAGetLastError());

return 0;

}

GlobalFree(PerHandleData);

GlobalFree(PerIoData);

continue;

}

//

if (PerIoData->BytesRECV == 0)

{

PerIoData->BytesRECV = BytesTransferred;

PerIoData->BytesSEND = 0;

}

else

{

PerIoData->BytesSEND += BytesTransferred;

}

if (PerIoData->BytesRECV > PerIoData->BytesSEND)

{

// Post another WSASend() request.

// Since WSASend() is not gauranteed to send all of the bytes requested,

// continue posting WSASend() calls until all received bytes are sent.

ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;

PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;

if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,

&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)

{

if (WSAGetLastError() != ERROR_IO_PENDING)

{

printf("WSASend() failed with error %d\n", WSAGetLastError());

return 0;

}

}

}

else

{

PerIoData->BytesRECV = 0;

// Now that there are no more bytes to send post another WSARecv() request.

Flags = 0;

ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));

PerIoData->DataBuf.len = DATA_BUFSIZE;

PerIoData->DataBuf.buf = PerIoData->Buffer;

if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,

&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)

{

if (WSAGetLastError() != ERROR_IO_PENDING)

{

printf("WSARecv() failed with error %d\n", WSAGetLastError());

return 0;

}

}

}

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有