把微软的那个代码改了一下。 vc 6下通过。需要添加一个很大的.txt 文件 我这里用了一个2m 的文件。我想达到的目的是。 读一个2m 的文件当有连接连上来,我就向这个端口 投递2m 的数据。 采用的是多次投递,过程在主线程里面压的。 方法很烂。 但目前看来还是管用的。 现在看来的结果是
第一 客户端没有乱序。
第二 当客户端故意断掉的时候 workthread 会有问题。因为显示了 GetQueuedCompletionStatus failed with error 64
我认为已经退出了。但服务程序还是可以继续。....怎么解决??
还有能不能给点更专业的写法?
解决方案 »
- CreateProcess创建出来的进程无法用SetWindowsHookEx勾住?
- 如何new一个一模一样的类?
- 求VC++6.0程序设计与开发技术大全-张宏林的PDF或PPT
- dsoframer open(url) url含有中文字符,,请问怎么解决???
- 我想编一个程序,使一个位图不断地旋转,哪位大虾有好的思路,或者程序借鉴一下,谢谢!
- 怎样在我对话框类中获得视图类的指针?
- VC动态库DLL的调用!
- 请问有没有图象文件格式大全!!!
- 在XP下做的程序,不用自画也也具有XP风格(在XP底下使用)?
- vc++问题!
- opengl怎样用鼠标拖动实现三维实体的旋转,平移和缩放的
- socket是一个编程接口,那么这个编程接口是怎么实现的啊?是谁来实现的?是操作系统吗?
//
// Description:
//
// This sample illustrates how to develop a simple echo server Winsock
// application using the completeion port I/O model. This
// sample is implemented as a console-style application and simply prints
// messages when connections are established and removed from the server.
// The application listens for TCP connections on port 5150 and accepts them
// as they arrive. When this application receives data from a client, it
// simply echos (this is why we call it an echo server) the data back in
// it's original form until the client closes the connection.
//
// Compile:
//
// cl -o iocmplt iocmplt.cpp ws2_32.lib
//
// Command Line Options:
//
// iocmplt.exe
//
// Note: There are no command line options for this sample.#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define PORT 5150
#define DATA_BUFSIZE 8192typedef struct
{
OVERLAPPED Overlapped; // Öصþio
WSABUF DataBuf; // WSABUF
CHAR *Buffer; // ÕæʵµÄÊý¾ÝÄÚÈÝ
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);char *Pdata = NULL;void main(void)
{
SOCKADDR_IN InternetAddr;
SOCKET Listen;
SOCKET Accept;
HANDLE CompletionPort;
SYSTEM_INFO SystemInfo;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
unsigned int i;
DWORD RecvBytes;
DWORD Flags;
DWORD ThreadID;
WSADATA wsaData;
DWORD Ret;
FILE * datafile;
unsigned int filelen = 0;
datafile = fopen("c.txt", "rb");
fseek(datafile, 0, 2);
filelen = ftell(datafile);
Pdata = new char [filelen +1];
Pdata[filelen] = 0;
fseek(datafile, 0, 0);
filelen = 0;
while ( fread(Pdata+filelen, 1000, 1, datafile) == 1)
{
filelen+=1000;
}
Pdata[filelen] = 0;
if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
{
printf("WSAStartup failed with error %d\n", Ret);
return;
}
// Setup an I/O completion port.
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);
// Create worker threads based on the number of processors available on the
// system. Create two worker threads for each processor.
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);
}
// Create a listening socket ½¨Á¢ÕìÌý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);
// °Ñ¶Ë¿Úbind ÉÏÈ¥
if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
{
printf("bind() failed with error %d\n", WSAGetLastError());
return;
}
// Prepare socket for listening
if (listen(Listen, 5) == SOCKET_ERROR)
{
printf("listen() failed with error %d\n", WSAGetLastError());
return;
}
// Accept connections and assign to the completion port.
{
if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return;
}
// accept Ò»¸öclient
unsigned int send_times = 0;
while ( send_times < filelen) {
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR,
sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return;
}
if (send_times == 0) {
if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
0) == NULL)
{
printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
return;
}
}
// Create per I/O socket information structure to associate with the
// WSARecv call below.
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)); // ½«io¶Ë¿Ú ÖÃΪ¿Õ
PerIoData->BytesSEND = 0; // send ×Ö½ÚΪ0
PerIoData->BytesRECV = 0; // ÊÕµ½×Ö½Ú
PerIoData->DataBuf.len = 1000; // BufferµÄ³¤¶È
PerIoData->Buffer = Pdata + send_times;
PerIoData->DataBuf.buf = PerIoData->Buffer; // ÊÕµ½µÄBuffer;
Flags = 0;
//
//
if (WSASend(Accept, &(PerIoData->DataBuf), 1, &PerIoData->BytesSEND, 0,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return;
}
}
send_times += 1000;
}
send_times = 0;
}
}DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID) // ·þÎñ¹¤×÷Ïß³Ì
{
HANDLE CompletionPort = (HANDLE) CompletionPortID; // ´Ó²ÎÊý»ñÈ¡Íê³É¶Ë¿Ú¶ÔÏóhandle
DWORD BytesTransferred;
LPOVERLAPPED Overlapped;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
while(TRUE) // ËÀÑ­»·
{
if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, // »ñÈ¡¶ÓÁÐÖÐiocpÖеÄ״̬
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
return 0;
}
// First check to see if an error has occured on the socket and if so
// then close the socket and cleanup the SOCKET_INFORMATION structure
// associated with the socket.
if (BytesTransferred == 0) // µ±·¢ËͽÓÊÕÊý¾ÝΪ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 ( BytesTransferred == PerIoData->BytesSEND) {
GlobalFree(PerHandleData); // Çå³ýµ¥ÏòÊý¾Ý
GlobalFree(PerIoData); // Çå³ýÊý¾Ý
continue;
}
if ( BytesTransferred < PerIoData->BytesSEND) { // ·¢Ë͵ÄÊý¾ÝСÓÚÒªË͵ÄÊý¾Ý
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
//PerIoData->BytesRECV = ;
PerIoData->BytesSEND = PerIoData->BytesSEND - BytesTransferred;
PerIoData->DataBuf.len = PerIoData->BytesSEND; // BufferµÄ³¤¶È
PerIoData->Buffer = PerIoData->Buffer + BytesTransferred;
PerIoData->DataBuf.buf = PerIoData->Buffer; // ÊÕµ½µÄBuffer;
if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &(PerIoData->BytesSEND), 0,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSASend() failed with error %d\n", WSAGetLastError());
return 0;
}
}
}
}
}
我的想法;如果第一次投递到套接字s上的数据大于了数据缓冲区,那么操作系统锁定该区域,发送,这时候如果用户再投递一次WSASend,如果这次投递数据正好可以copy到缓冲区,则,系统会按照顺序发送还是?这时候两次数据的发送顺序是???
期待高人
我的理解,重复投递,如果不人为的干预肯定会出现,000011110000客户端收到这样数据的情况??
刚才我在局域网里面模拟测试了,但是没有模拟出需要多次发送的情况。第一次发送了120M的数据,连续发送第二个的时候就会出现WSA_IO_PANDING的错误,但等到GetQue的时候都是发送完成了的返回。如果大于120M,发送,则发送直接失败,返回超出发送队列的错误。
刚和ProgrameMan(我要学汇编)兄聊了会,我也认可ProgrameMan(我要学汇编)兄的说法,发送是线性的,是可以保持发送的顺序的。但是我还是有点怀疑,如果第一次发送的0没有发送完,那我第二次发送的1是不是就一直不会从GetQue那里得到返回信息,直到第一次的0循环几次发送完毕,才轮到第二次的1返回发送状态呢??
有实际操作经验的人吗??
我的解决方案是:设置接收和发送双缓冲区,自己来控制接收和发送的顺序,每次只投递一个WSARecv和一个WSASend,这样不管实际投递是不是有序的,我都不用关心了。
期待高人给个测试的说法。
呵呵,我的代码给太多人了,我怕泛滥了,到不是怕大家看,而是怕水平不好影响了大家的思路。。