完成端口做服务器,内存一直涨,最后程序当掉!?

时间:2022-09-13 21:48:51
小弟刚接触网络编程,做一个用完成端口写的服务器,这个程序在《windows网络编程技术》上也有,然后用大量客户端连接,客户端是三次发送和三次接收,发现服务器内存一直涨,好像是连接1000个客户端后涨了252K字节,这样连接了几十万个后,内存涨了上白兆,最后程序死掉了。我仔细检查程序,该释放内存的地方也都写了,为什么会出现这种情况,请高手大虾帮帮忙,小弟感激不尽!

36 个解决方案

#1


#define PORT 8007
#define DATA_BUFSIZE 1024

#pragma comment(lib, "Ws2_32")

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

/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
CString strHello;
strHello.LoadString(IDS_HELLO);
cout << (LPCTSTR)strHello << endl;

}
count = 0;

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;    
sockaddr_in from;
    int fromlen=sizeof(from);

if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
{
printf("WSAStartup failed with error %d\n", Ret);
return 1;
}

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

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

// Close the thread handle
CloseHandle(ThreadHandle);
}

// Create a listening 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 1;


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

// Prepare socket for listening

if (listen(Listen, 5) == SOCKET_ERROR)
{
printf("listen() failed with error %d\n", WSAGetLastError());
return 1;
}

// Accept connections and assign to the completion port.

while(TRUE)
{
if ((Accept = WSAAccept(Listen, (struct sockaddr*)&from,&fromlen, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return 1;
}

// Create a socket information structure to associate with the socket
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, 
sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return 1;
}

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

    cout << "Connection from " << inet_ntoa(from.sin_addr) <<"\r\n";
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 1;
}

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

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

return nRetCode;
}

#2


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)
   {
  char *toSendtxt = new char[56];
      if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
         (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
      {
         printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
     delete []toSendtxt;
         //return 0;
      }
//   long num = ::GetCurrentThreadId();

      // 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)
      {
         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);
     delete []toSendtxt;
         continue;
      }

      // Check to see if the BytesRECV field equals zero. If this is so, then
      // this means a WSARecv call just completed so update the BytesRECV field
      // with the BytesTransferred value from the completed WSARecv() call.

      if (PerIoData->BytesRECV == 0)
      {
         PerIoData->BytesRECV = BytesTransferred;
//  cout<<"the received data is :"<<PerIoData->DataBuf.buf<<endl;

   toSendtxt = "aaaaaaaaa";


         PerIoData->BytesSEND = 0;
 
      }
      else
      {
         PerIoData->BytesSEND += BytesTransferred;
      }

      if (PerIoData->BytesSEND == 0 )//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 = toSendtxt;//PerIoData->Buffer + PerIoData->BytesSEND;
         PerIoData->DataBuf.len = strlen(toSendtxt);//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());
         delete []toSendtxt;
               //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());
         delete []toSendtxt;
               //return 0;
            }
         }
      }//else
   }//while
}
///////////////////////////////
以上是服务器的代码

#3


下面贴出客户端的代码:

#include "stdafx.h"
#include "afxsock.h"
#include "conio.h"
#include <process.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

int sock();
int logNum = 0;

#define PORT (u_short) 8007
#define DEST_IP_ADDR "192.168.1.101" //Server address    
#define NO_FLAGS_SET 0
/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
CString strHello;
strHello.LoadString(IDS_HELLO);
cout << (LPCTSTR)strHello << endl;
HANDLE hThread;
UINT dwThreadID;
for(int i = 0; i < 1; i++)
{
hThread =(HANDLE) _beginthreadex(NULL,0,ClientThread,0,0,&dwThreadID);
}
CloseHandle(hThread);
while(_getch()!=27);
}

return nRetCode;
}

#4


可能是内存碎片(代码太长,没时间看 ^_^),把每次分配的内存固定为分页大小(GetSystemInfo可以取得,通常为4k).

#5


僵尸哥讲的我也考虑过,现在我把内存分配为1k的整数倍,不知道这样会不会也有问题。不过我还是会试试4k的,谢谢先!

#6


兄弟 搞定了 不要忘记给我说说啊 谢谢  愁思我了

#7


他不可能搞定
因为根本不是那么回事

#8


char *toSendtxt = new char[56];
......
toSendtxt = "aaaaaaaaa";  //<--修改为strcpy(toSendtxt , "aaaaaaaaa");  否则toSendtxt指针指向改变当然不能正确释放了

#9


strcpy(toSendtxt , "aaaaaaaaa");我也试过的,还是没有用。不过我发现在xp下涨的很少,2003下却涨得很快,真是奇怪啊!不知道liqiang123abc有什么好的建议啊?

#10


你那个是缓存页面的问题
我以前也遇到过
很难解决

#11



不会吧,如果真是那样,那就惨了!还想请教一个问题:
typedef struct 
{
   SOCKET Socket;
   
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

上面的结构体中加入一个类的指针,比如:

typedef struct 
{
   SOCKET Socket;
   CMyClass * myclass;

} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

在debug运行,VC自己会报内存泄露,我是想让每个socket对应每个类的,这是怎么回事呢?
请高手指点!

#12



还真如liqiang123abc所说的,这个问题搞不定了!
郁闷的是同样是2003系统,一个没有问题,另一个确有问题,唉!!!

#13


这个非得对缓存页面的原理十分熟悉,
才能搞定呢

我不信你能搞定,
我的那个就没搞定,
最后不了了之。。。

#14


首先,WSASend/WSARecv调用失败需要判断是否为IO_Pending,如果不是则需要对内存进行释放;
其次,GetQueuedCompletionStatus返回为False也需要根据情况对内存进行释放,因为这当中可能是另外一个线程提交的WSASend/WSARecv,但是由于还在操作过程当中,该线程意外退出,可能会导致GetQueuedCompletionStatus返回,此时返回值为False,但是交不意味着当中的其它参数无效;
再一个,就是楼主对于Send/Recv的处理代码不明确,虽然在这个echo测试当中无关紧要,但是写这个测试不就是为了以后的成熟应用吗?所以一切都要以一个应用来考虑。

#15


不过那个struct当中包含指针的问题,应该是属于程序当中的处理问题,比如说,你的对象是在堆栈上创建的,虽然你保存了指针,但是对象在函数执行完之后就已经释放掉了,而你再去操作实指针,根本就得不到实例。

#16



对于这个问题,我已经不报希望了,多谢大家的积极讨论!!

#17


你刚学网络编程是不可能解决这个问题滴
等你以后经验多了
就能解决了

#18


两处错误:

1、toSendtxt = "aaaaaaaaa"; 改变了指针,使即将运行的delete语句无效。

2、每次循环都 char *toSendtxt = new char[56]; 但是不是每次循环都会delete的,有时delete语句没有机会执行。

#19


回复人:awu999328() ( 一级(初级)) 信誉:100  2007-7-30 11:07:28  得分:0

对于这个问题,我已经不报希望了,多谢大家的积极讨论!!
-----------------------


肯定是你分配了内存没有释放啊,怎么扯到 什么 "缓存页面"去了.

仔细检查和每个client关联的那个结构体.是否在断开连接的时候释放了内存.

#20



不建议你在循环中创建、删除,这一定会造成内存碎片。(在循环外创建然后再循环内使用不好?)


可能高手们看的问题较深,能仔细讲解缓存页面的问题。

我看到过高手写的文章关于提高完成端口性能的。

1、使用AcceptEx代替Accept
2、程序启动的时候创建一定数量的socket结构,这样就不会频繁创建于释放造成内存碎片的产生,当超过这个数量才创建与释放

#21


说个好方法,创建一个BUFFER数组,为每个线程创建一个BUFFER,然后直接取就可以用了,不需要每次都new的

#22


在分配和释放内存的地方都加上调试信息,显示指针地址。
统计下看看,是不是每个对应的都释放了。

#23


其实这个问题你根本没找到具体原因

#24


肯定是哪里内存没释放。 

 你对这问题不报希望, 那你写这些都是垃圾,而且浪费认真去看你代码的人 的宝贵时间

#25


总有地方有问题的

#26




char *toSendtxt = new char[56];
......
toSendtxt = "aaaaaaaaa"; //<--修改为strcpy(toSendtxt , "aaaaaaaaa"); 否则toSendtxt指针指向改变当然不能正确释放了

-----
正解 多几个句柄 就当掉了

#27


toSendtxt = "aaaaaaaaa";
你的toSendTxt不是NEW出来的内存么
这样一改 
那块内存肯定回收不了了
你把代码改一改
去掉所有的new和delete
直接再循环外边写chan toSendtxt[50]不就得了

#28


用 boundchecker 查一下是哪里泄露不就完了么

#29



想不到还有这么多大哥关心此问题,小弟真是很激动.
其实我不是在每次循环new那个toSendtxt,我改成下面的:
typedef struct 
{
   SOCKET Socket;
   char toSendtxt[56];
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

这样GlobalFree就会释放掉
不过这样还是有问题,自己机器跑了6天没有挂掉,而电信机房服务器不到几小时就挂了
虽然我的系统是盗版的2003 企业版

真不知如何是好啊!!!???

#30


跟盗版应该无关,模拟一下电信的环境,多测试一下。

#31


是否有考虑到可能多核及超线程的情况导致问题出现?

#32


呵呵
对你的遭遇表示同情。
但是我无能为力。。。

也遇到类似的情况。
最后还是无可奈何。
只好作罢。。。

#33


顶. 我等着看答案.

#34


"这样连接了几十万个后"

同时保持连接有几十万个?

#35


留个记号,慢慢看。关注。

#36


建议兄弟去看看我上传的资料里面有个完成端口实现的聊天室,可以支持15000人同时在线,采用内存池,内存占用很平稳.

#1


#define PORT 8007
#define DATA_BUFSIZE 1024

#pragma comment(lib, "Ws2_32")

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

/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
CString strHello;
strHello.LoadString(IDS_HELLO);
cout << (LPCTSTR)strHello << endl;

}
count = 0;

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;    
sockaddr_in from;
    int fromlen=sizeof(from);

if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
{
printf("WSAStartup failed with error %d\n", Ret);
return 1;
}

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

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

// Close the thread handle
CloseHandle(ThreadHandle);
}

// Create a listening 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 1;


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

// Prepare socket for listening

if (listen(Listen, 5) == SOCKET_ERROR)
{
printf("listen() failed with error %d\n", WSAGetLastError());
return 1;
}

// Accept connections and assign to the completion port.

while(TRUE)
{
if ((Accept = WSAAccept(Listen, (struct sockaddr*)&from,&fromlen, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return 1;
}

// Create a socket information structure to associate with the socket
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, 
sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return 1;
}

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

    cout << "Connection from " << inet_ntoa(from.sin_addr) <<"\r\n";
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 1;
}

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

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

return nRetCode;
}

#2


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)
   {
  char *toSendtxt = new char[56];
      if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
         (LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
      {
         printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
     delete []toSendtxt;
         //return 0;
      }
//   long num = ::GetCurrentThreadId();

      // 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)
      {
         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);
     delete []toSendtxt;
         continue;
      }

      // Check to see if the BytesRECV field equals zero. If this is so, then
      // this means a WSARecv call just completed so update the BytesRECV field
      // with the BytesTransferred value from the completed WSARecv() call.

      if (PerIoData->BytesRECV == 0)
      {
         PerIoData->BytesRECV = BytesTransferred;
//  cout<<"the received data is :"<<PerIoData->DataBuf.buf<<endl;

   toSendtxt = "aaaaaaaaa";


         PerIoData->BytesSEND = 0;
 
      }
      else
      {
         PerIoData->BytesSEND += BytesTransferred;
      }

      if (PerIoData->BytesSEND == 0 )//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 = toSendtxt;//PerIoData->Buffer + PerIoData->BytesSEND;
         PerIoData->DataBuf.len = strlen(toSendtxt);//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());
         delete []toSendtxt;
               //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());
         delete []toSendtxt;
               //return 0;
            }
         }
      }//else
   }//while
}
///////////////////////////////
以上是服务器的代码

#3


下面贴出客户端的代码:

#include "stdafx.h"
#include "afxsock.h"
#include "conio.h"
#include <process.h>


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

int sock();
int logNum = 0;

#define PORT (u_short) 8007
#define DEST_IP_ADDR "192.168.1.101" //Server address    
#define NO_FLAGS_SET 0
/////////////////////////////////////////////////////////////////////////////
// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;

// initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
{
// TODO: change error code to suit your needs
cerr << _T("Fatal Error: MFC initialization failed") << endl;
nRetCode = 1;
}
else
{
// TODO: code your application's behavior here.
CString strHello;
strHello.LoadString(IDS_HELLO);
cout << (LPCTSTR)strHello << endl;
HANDLE hThread;
UINT dwThreadID;
for(int i = 0; i < 1; i++)
{
hThread =(HANDLE) _beginthreadex(NULL,0,ClientThread,0,0,&dwThreadID);
}
CloseHandle(hThread);
while(_getch()!=27);
}

return nRetCode;
}

#4


可能是内存碎片(代码太长,没时间看 ^_^),把每次分配的内存固定为分页大小(GetSystemInfo可以取得,通常为4k).

#5


僵尸哥讲的我也考虑过,现在我把内存分配为1k的整数倍,不知道这样会不会也有问题。不过我还是会试试4k的,谢谢先!

#6


兄弟 搞定了 不要忘记给我说说啊 谢谢  愁思我了

#7


他不可能搞定
因为根本不是那么回事

#8


char *toSendtxt = new char[56];
......
toSendtxt = "aaaaaaaaa";  //<--修改为strcpy(toSendtxt , "aaaaaaaaa");  否则toSendtxt指针指向改变当然不能正确释放了

#9


strcpy(toSendtxt , "aaaaaaaaa");我也试过的,还是没有用。不过我发现在xp下涨的很少,2003下却涨得很快,真是奇怪啊!不知道liqiang123abc有什么好的建议啊?

#10


你那个是缓存页面的问题
我以前也遇到过
很难解决

#11



不会吧,如果真是那样,那就惨了!还想请教一个问题:
typedef struct 
{
   SOCKET Socket;
   
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

上面的结构体中加入一个类的指针,比如:

typedef struct 
{
   SOCKET Socket;
   CMyClass * myclass;

} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

在debug运行,VC自己会报内存泄露,我是想让每个socket对应每个类的,这是怎么回事呢?
请高手指点!

#12



还真如liqiang123abc所说的,这个问题搞不定了!
郁闷的是同样是2003系统,一个没有问题,另一个确有问题,唉!!!

#13


这个非得对缓存页面的原理十分熟悉,
才能搞定呢

我不信你能搞定,
我的那个就没搞定,
最后不了了之。。。

#14


首先,WSASend/WSARecv调用失败需要判断是否为IO_Pending,如果不是则需要对内存进行释放;
其次,GetQueuedCompletionStatus返回为False也需要根据情况对内存进行释放,因为这当中可能是另外一个线程提交的WSASend/WSARecv,但是由于还在操作过程当中,该线程意外退出,可能会导致GetQueuedCompletionStatus返回,此时返回值为False,但是交不意味着当中的其它参数无效;
再一个,就是楼主对于Send/Recv的处理代码不明确,虽然在这个echo测试当中无关紧要,但是写这个测试不就是为了以后的成熟应用吗?所以一切都要以一个应用来考虑。

#15


不过那个struct当中包含指针的问题,应该是属于程序当中的处理问题,比如说,你的对象是在堆栈上创建的,虽然你保存了指针,但是对象在函数执行完之后就已经释放掉了,而你再去操作实指针,根本就得不到实例。

#16



对于这个问题,我已经不报希望了,多谢大家的积极讨论!!

#17


你刚学网络编程是不可能解决这个问题滴
等你以后经验多了
就能解决了

#18


两处错误:

1、toSendtxt = "aaaaaaaaa"; 改变了指针,使即将运行的delete语句无效。

2、每次循环都 char *toSendtxt = new char[56]; 但是不是每次循环都会delete的,有时delete语句没有机会执行。

#19


回复人:awu999328() ( 一级(初级)) 信誉:100  2007-7-30 11:07:28  得分:0

对于这个问题,我已经不报希望了,多谢大家的积极讨论!!
-----------------------


肯定是你分配了内存没有释放啊,怎么扯到 什么 "缓存页面"去了.

仔细检查和每个client关联的那个结构体.是否在断开连接的时候释放了内存.

#20



不建议你在循环中创建、删除,这一定会造成内存碎片。(在循环外创建然后再循环内使用不好?)


可能高手们看的问题较深,能仔细讲解缓存页面的问题。

我看到过高手写的文章关于提高完成端口性能的。

1、使用AcceptEx代替Accept
2、程序启动的时候创建一定数量的socket结构,这样就不会频繁创建于释放造成内存碎片的产生,当超过这个数量才创建与释放

#21


说个好方法,创建一个BUFFER数组,为每个线程创建一个BUFFER,然后直接取就可以用了,不需要每次都new的

#22


在分配和释放内存的地方都加上调试信息,显示指针地址。
统计下看看,是不是每个对应的都释放了。

#23


其实这个问题你根本没找到具体原因

#24


肯定是哪里内存没释放。 

 你对这问题不报希望, 那你写这些都是垃圾,而且浪费认真去看你代码的人 的宝贵时间

#25


总有地方有问题的

#26




char *toSendtxt = new char[56];
......
toSendtxt = "aaaaaaaaa"; //<--修改为strcpy(toSendtxt , "aaaaaaaaa"); 否则toSendtxt指针指向改变当然不能正确释放了

-----
正解 多几个句柄 就当掉了

#27


toSendtxt = "aaaaaaaaa";
你的toSendTxt不是NEW出来的内存么
这样一改 
那块内存肯定回收不了了
你把代码改一改
去掉所有的new和delete
直接再循环外边写chan toSendtxt[50]不就得了

#28


用 boundchecker 查一下是哪里泄露不就完了么

#29



想不到还有这么多大哥关心此问题,小弟真是很激动.
其实我不是在每次循环new那个toSendtxt,我改成下面的:
typedef struct 
{
   SOCKET Socket;
   char toSendtxt[56];
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

这样GlobalFree就会释放掉
不过这样还是有问题,自己机器跑了6天没有挂掉,而电信机房服务器不到几小时就挂了
虽然我的系统是盗版的2003 企业版

真不知如何是好啊!!!???

#30


跟盗版应该无关,模拟一下电信的环境,多测试一下。

#31


是否有考虑到可能多核及超线程的情况导致问题出现?

#32


呵呵
对你的遭遇表示同情。
但是我无能为力。。。

也遇到类似的情况。
最后还是无可奈何。
只好作罢。。。

#33


顶. 我等着看答案.

#34


"这样连接了几十万个后"

同时保持连接有几十万个?

#35


留个记号,慢慢看。关注。

#36


建议兄弟去看看我上传的资料里面有个完成端口实现的聊天室,可以支持15000人同时在线,采用内存池,内存占用很平稳.