一位大三的师姐叫我帮她看一下这个ftp服务器的代码,小弟才疏学浅看不懂呀,请各位大侠请教一下,谢谢了!
void main(void)
{
WSADATA wsaData;
SOCKET sListen, sAccept;
SOCKADDR_IN inetAddr;
DWORD dwFlags;
DWORD dwThreadId;
DWORD dwRecvBytes;
INT nRet; InitializeCriticalSection(&g_cs);
if (( nRet = WSAStartup(0x0202,&wsaData)) != 0 ) {
printf("错误:WSAStartup failed with error %d\n", nRet);
return;
} // 先取得本地地址
sprintf( g_szLocalAddr,"%s",GetLocalAddress() );
//使用重叠IO模型,设置重叠标志
if ((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("错误:Failed to get a socket %d\n", WSAGetLastError());
WSACleanup();
return;
} inetAddr.sin_family = AF_INET;
inetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
inetAddr.sin_port = htons(FTP_PORT);
if (bind(sListen, (PSOCKADDR) &inetAddr, sizeof(inetAddr)) == SOCKET_ERROR)
{
printf("错误:bind() failed with error %d\n", WSAGetLastError());
return;
} if (listen(sListen, SOMAXCONN))
{
printf("错误:listen() failed with error %d\n", WSAGetLastError());
return;
} printf("Mini Ftpserver已经启动 \n");
printf("Mini Ftpserver开始侦听 \n");
/*
if ((sAccept = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("错误:Failed to get a socket %d\n", WSAGetLastError());
return;
}
*/
//创建第一个手动重置对象
if ((g_events[0] = WSACreateEvent()) == WSA_INVALID_EVENT)
{
printf("错误:WSACreateEvent failed with error %d\n", WSAGetLastError());
return;
} // 创建一个线程处理请求
if (CreateThread(NULL, 0, ProcessTreadIO, NULL, 0, &dwThreadId) == NULL)
{
printf("错误:CreateThread failed with error %d\n", GetLastError());
return;
} g_dwEventTotal = 1; while(TRUE)
{
//处理入站连接
if ((sAccept = accept(sListen, NULL, NULL)) == INVALID_SOCKET)
{
printf("错误:accept failed with error %d\n", WSAGetLastError());
return;
} //回传欢迎消息
if( !WelcomeInfo( sAccept ) ) break;
//设置ftp根目录
if( !SetCurrentDirectory( DEFAULT_HOME_DIR ) ) break; //操作临界区,防止出错
EnterCriticalSection(&g_cs);
//创建一个新的SOCKET_INF结构处理接受的数据socket.
if ((g_sockets[g_dwEventTotal] = (LPSOCKET_INF)GlobalAlloc(GPTR,sizeof(SOCKET_INF))) == NULL)
{
printf("错误:GlobalAlloc() failed with error %d\n", GetLastError());
return;
} //初始化新的SOCKET_INF结构
char buff[DATA_BUFSIZE]; memset( buff,0,DATA_BUFSIZE );
g_sockets[g_dwEventTotal]->wsaBuf.buf = buff;
g_sockets[g_dwEventTotal]->wsaBuf.len = DATA_BUFSIZE;
g_sockets[g_dwEventTotal]->s = sAccept;
memset(&(g_sockets[g_dwEventTotal]->o),0, sizeof(OVERLAPPED));
g_sockets[g_dwEventTotal]->dwBytesSend = 0;
g_sockets[g_dwEventTotal]->dwBytesRecv = 0;
g_sockets[g_dwEventTotal]->nStatus = WSA_RECV; // 接收
//创建事件
if ((g_sockets[g_dwEventTotal]->o.hEvent = g_events[g_dwEventTotal] =
WSACreateEvent()) == WSA_INVALID_EVENT)
{
printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());
return;
} //发出接受请求
dwFlags = 0;
if (WSARecv(g_sockets[g_dwEventTotal]->s,
&(g_sockets[g_dwEventTotal]->wsaBuf), 1, &dwRecvBytes, &dwFlags,
&(g_sockets[g_dwEventTotal]->o), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("错误:WSARecv() failed with error %d\n", WSAGetLastError());
return;
}
}
g_dwEventTotal++; //离开临界区
LeaveCriticalSection(&g_cs); //使第一个事件有信号。使工作者线程处理其他的事件
if (WSASetEvent(g_events[0]) == FALSE)
{
printf("错误:WSASetEvent failed with error %d\n", WSAGetLastError());
return;
}
}
}有三个问题:1、为什么要两次创建WSACreateEvent ?一个在57行,另一个107行。
2、主线程main都进入死循环 while(TRUE)了,那又怎么可以进入到工作线程ProcessTreadIO 呀?
3、为什么要在主线程 main 的最后WSASetEvent(g_events[0])使g_events[0]有信号呢???
void main(void)
{
WSADATA wsaData;
SOCKET sListen, sAccept;
SOCKADDR_IN inetAddr;
DWORD dwFlags;
DWORD dwThreadId;
DWORD dwRecvBytes;
INT nRet; InitializeCriticalSection(&g_cs);
if (( nRet = WSAStartup(0x0202,&wsaData)) != 0 ) {
printf("错误:WSAStartup failed with error %d\n", nRet);
return;
} // 先取得本地地址
sprintf( g_szLocalAddr,"%s",GetLocalAddress() );
//使用重叠IO模型,设置重叠标志
if ((sListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("错误:Failed to get a socket %d\n", WSAGetLastError());
WSACleanup();
return;
} inetAddr.sin_family = AF_INET;
inetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
inetAddr.sin_port = htons(FTP_PORT);
if (bind(sListen, (PSOCKADDR) &inetAddr, sizeof(inetAddr)) == SOCKET_ERROR)
{
printf("错误:bind() failed with error %d\n", WSAGetLastError());
return;
} if (listen(sListen, SOMAXCONN))
{
printf("错误:listen() failed with error %d\n", WSAGetLastError());
return;
} printf("Mini Ftpserver已经启动 \n");
printf("Mini Ftpserver开始侦听 \n");
/*
if ((sAccept = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,
WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("错误:Failed to get a socket %d\n", WSAGetLastError());
return;
}
*/
//创建第一个手动重置对象
if ((g_events[0] = WSACreateEvent()) == WSA_INVALID_EVENT)
{
printf("错误:WSACreateEvent failed with error %d\n", WSAGetLastError());
return;
} // 创建一个线程处理请求
if (CreateThread(NULL, 0, ProcessTreadIO, NULL, 0, &dwThreadId) == NULL)
{
printf("错误:CreateThread failed with error %d\n", GetLastError());
return;
} g_dwEventTotal = 1; while(TRUE)
{
//处理入站连接
if ((sAccept = accept(sListen, NULL, NULL)) == INVALID_SOCKET)
{
printf("错误:accept failed with error %d\n", WSAGetLastError());
return;
} //回传欢迎消息
if( !WelcomeInfo( sAccept ) ) break;
//设置ftp根目录
if( !SetCurrentDirectory( DEFAULT_HOME_DIR ) ) break; //操作临界区,防止出错
EnterCriticalSection(&g_cs);
//创建一个新的SOCKET_INF结构处理接受的数据socket.
if ((g_sockets[g_dwEventTotal] = (LPSOCKET_INF)GlobalAlloc(GPTR,sizeof(SOCKET_INF))) == NULL)
{
printf("错误:GlobalAlloc() failed with error %d\n", GetLastError());
return;
} //初始化新的SOCKET_INF结构
char buff[DATA_BUFSIZE]; memset( buff,0,DATA_BUFSIZE );
g_sockets[g_dwEventTotal]->wsaBuf.buf = buff;
g_sockets[g_dwEventTotal]->wsaBuf.len = DATA_BUFSIZE;
g_sockets[g_dwEventTotal]->s = sAccept;
memset(&(g_sockets[g_dwEventTotal]->o),0, sizeof(OVERLAPPED));
g_sockets[g_dwEventTotal]->dwBytesSend = 0;
g_sockets[g_dwEventTotal]->dwBytesRecv = 0;
g_sockets[g_dwEventTotal]->nStatus = WSA_RECV; // 接收
//创建事件
if ((g_sockets[g_dwEventTotal]->o.hEvent = g_events[g_dwEventTotal] =
WSACreateEvent()) == WSA_INVALID_EVENT)
{
printf("WSACreateEvent() failed with error %d\n", WSAGetLastError());
return;
} //发出接受请求
dwFlags = 0;
if (WSARecv(g_sockets[g_dwEventTotal]->s,
&(g_sockets[g_dwEventTotal]->wsaBuf), 1, &dwRecvBytes, &dwFlags,
&(g_sockets[g_dwEventTotal]->o), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("错误:WSARecv() failed with error %d\n", WSAGetLastError());
return;
}
}
g_dwEventTotal++; //离开临界区
LeaveCriticalSection(&g_cs); //使第一个事件有信号。使工作者线程处理其他的事件
if (WSASetEvent(g_events[0]) == FALSE)
{
printf("错误:WSASetEvent failed with error %d\n", WSAGetLastError());
return;
}
}
}有三个问题:1、为什么要两次创建WSACreateEvent ?一个在57行,另一个107行。
2、主线程main都进入死循环 while(TRUE)了,那又怎么可以进入到工作线程ProcessTreadIO 呀?
3、为什么要在主线程 main 的最后WSASetEvent(g_events[0])使g_events[0]有信号呢???
解决方案 »
- Window API编程,如何在DialogBox产生的模态对话框上面的控件上绘图
- 树形控件右击弹出菜单,点击菜单的重命名可以对树形控件的值进行就修改,怎么做
- 如何给控件添加默认没有的消息响应函数
- CTreeCtrl节点上的图标怎么变透明?
- 求CAD二次开发,公司或个人均可
- 关于XML里TEXT的解析
- 全局变量的问题!应该是很简单的!昏!
- 我制定的这个异步通信帧格式可行吗?该如何改进?
- $$$$刚完成的液晶取字模工具-------字模精灵(Version1.0)!欢迎试用!请多提宝贵意见!
- 请教VC高手
- 【求教】MFC中将多个数组赋值到一个EDIT BOX中
- 关于进程内核对象和线程内核对象的问题
DWORD WINAPI ProcessTreadIO(LPVOID lpParameter)
{
DWORD dwFlags;
LPSOCKET_INF pSI;
DWORD dwBytesTransferred;
DWORD i; //处理异步的WSASend, WSARecv等请求等
while(TRUE)
{
if ((g_index = WSAWaitForMultipleEvents(g_dwEventTotal, g_events, FALSE,
WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED)
{
printf("错误:WSAWaitForMultipleEvents failed %d\n", WSAGetLastError());
return 0;
}
if ((g_index - WSA_WAIT_EVENT_0) == 0)
{
WSAResetEvent(g_events[0]);
continue;
} pSI = g_sockets[g_index - WSA_WAIT_EVENT_0];
WSAResetEvent(g_events[g_index - WSA_WAIT_EVENT_0]); if (WSAGetOverlappedResult(pSI->s, &(pSI->o), &dwBytesTransferred,
FALSE, &dwFlags) == FALSE || dwBytesTransferred == 0)
{
printf("Closing socket %d\n", pSI->s); if (closesocket(pSI->s) == SOCKET_ERROR)
{
printf("错误:closesocket() failed with error %d\n", WSAGetLastError());
} GlobalFree(pSI); WSACloseEvent(g_events[g_index - WSA_WAIT_EVENT_0]); // Cleanup g_sockets and g_events by removing the socket event handle
// and socket information structure if they are not at the end of the
// arrays. EnterCriticalSection(&g_cs); if ((g_index - WSA_WAIT_EVENT_0) + 1 != g_dwEventTotal)
for (i = g_index - WSA_WAIT_EVENT_0; i < g_dwEventTotal; i++)
{
g_events[i] = g_events[i + 1];
g_sockets[i] = g_sockets[i + 1];
} g_dwEventTotal--; LeaveCriticalSection(&g_cs); continue;
} // 已经有数据传递
if( pSI->nStatus == WSA_RECV )
{
memcpy( &pSI->buffRecv[pSI->dwBytesRecv],pSI->wsaBuf.buf,dwBytesTransferred);
pSI->dwBytesRecv += dwBytesTransferred;
printf( "接受Luo:%s\n",pSI->buffRecv);
if( pSI->buffRecv[pSI->dwBytesRecv-2] == '\r' // 要保证最后是\r\n
&& pSI->buffRecv[pSI->dwBytesRecv-1] == '\n'
&& pSI->dwBytesRecv > 2 )
{
if( !g_bLoggedIn )
{
if( LoginIn(pSI) == LOGGED_IN )
g_bLoggedIn = TRUE;
}
else
{
if(DealCommand( pSI )==FTP_QUIT)
continue;
}
// 缓冲区清除
memset( pSI->buffRecv,0,sizeof(pSI->buffRecv) );
pSI->dwBytesRecv = 0;
}
}
else
{
pSI->dwBytesSend += dwBytesTransferred;
}
// 继续接收以后到来的数据
if( RecvReq( pSI ) == -1 )
return -1;
}
return 0;
}
第一个event感觉是给listener用的,但使用的是accept,所以没用上。
来一个连接就SetCurrentDirectory显然不对,后来者会冲掉面前的目录设置(除非你这个ftp服务器不能更改目录)。
至于说主函数里面一个死循环,这个没有问题,死的空循环才有问题(cpu耗尽)。一般持续运行的程序都是一个死循环,做得好的加一个退出命令,稍差一点的直接ctrl+c结束,后者也并没有什么问题。
多线程是并发的,如果第一个线程要退出才能进入第二个线程的话,就不叫并发了,所以只要CreateThread之后,ProcessTreadIO就开始运行了(可能滞后一定时间,由操作系统调度决定)
ProcessTreadIO 中的, 既然是进入了main的while(true)循环,那怎么还可以到 工作者线程处理函数
ProcessTreadIO 中的 ???
那这个程序main函数的第一个g_events[0] = WSACreateEvent() 用来干嘛的???还有main函数最后的 WSASetEvent(g_events[0])的有什么用呀???
而且原本程序的main函数是有两个WSASocket的,第一个 sListen = WSASocket 第二个 sAccept = WSASocket,只是我把第二个sAccept = WSASocket注释掉了而已,因为我觉得没必要再WSASocket(注释掉,程序运行接收数据都没有问题的)我这样认为对不对 ???
g_events[0]这个事件起到一个通知的作用,每次有新的连接建立,建立一个新的事件放入g_events[g_dwEventTotal],但是线程中的等待事件函数WSAWaitForMultipleEvents并没有包括这个新加入的事件,所以会出现万一其它连接都没有事件,只有新加入的连接有读取事件,就无法得到处理;因此通过每次循环中对g_events[0]置位使得WSAWaitForMultipleEvents退出,然后再
if ((g_index - WSA_WAIT_EVENT_0) == 0)
{
WSAResetEvent(g_events[0]);
continue;
}
线程中重新开始等待事件,而此时等待的事件中就包括了新加入的那个事件。你这个程序有一个问题就是:
//初始化新的SOCKET_INF结构
char buff[DATA_BUFSIZE]; memset( buff,0,DATA_BUFSIZE );
g_sockets[g_dwEventTotal]->wsaBuf.buf = buff;
这里缓冲区是局部变量,所以在循环结束就失效了,下次在线程中去访问就会出问题。所以应该新分配一块缓冲区:
//初始化新的SOCKET_INF结构
// char buff[DATA_BUFSIZE]; memset( buff,0,DATA_BUFSIZE );
g_sockets[g_dwEventTotal]->wsaBuf.buf = new char[DATA_BUFSIZE];
memset(g_sockets[g_dwEventTotal]->wsaBuf.buf,0,DATA_BUFSIZE );
当然要释放该块内存,在:
delete[] pSI->wsaBuf.buf; //这里释放
GlobalFree(pSI);
http://download.csdn.net/detail/geoff08zhang/4571358
if ((g_index - WSA_WAIT_EVENT_0) == 0)
{
WSAResetEvent(g_events[0]);
continue;
}
线程中重新开始等待事件,而此时等待的事件中就包括了新加入的那个事件。
作者为什么要这么做呢???请大神详细说下这两个线程的关系咯...拜读!
变量作用域并不是退出一个函数才是一个作用域,一个花括号{}包含的块就是一个作用域,也就是说你每次循环都是重新声明了一次char buff[DATA_BUFSIZE];暂且不论生命周期和作用域,就算编译器每次都在堆上分配同样的地址,这一般是成立的,所以没有破坏内存的情况出现,但是你其实只有一个缓存!所有的读写都在这个内存区进行,这是什么情况?反正等你试了就知道了,数据完全乱了。至于两个线程关系不是已经很明了了么?……
主线程监听端口,等待客户端连接,连接来了就把新的Socket(accept生成的)放入g_sockets数组(g_sockets[g_dwEventTotal]->s),同时用一个事件g_events[g_dwEventTotal]与辅助线程同步;辅助线程通过等待读取事件的到来,有了读取事件就去读相应的Socket中的数据。作者为什么这么做就不知道了,我觉得很傻,可是也是有效的办法。
作者为什么这么做就不知道了,我觉得很傻,可是也是有效的办法。我看了一下WSAWaitForMultipleEvents的说明,用双WSAWaitForMultipleEvents机制也行的(先fWaitAll参数设为false,再设为TRUE以确定每个事件的状态),不知道大牛你可有你的更好的办法还有哦,这里:
在main中,用GlobalAlloc分配内存给SOCKET_INF结构体。
//创建一个新的SOCKET_INF结构处理接受的数据socket.
if ((g_sockets[g_dwEventTotal] = (LPSOCKET_INF)GlobalAlloc(GPTR,sizeof(SOCKET_INF))) == NULL)
{
printf("错误:GlobalAlloc() failed with error %d\n", GetLastError());
return;
}
在 工作线程 中用GlobalFree释放内存, 然后我分别用new 和delete 代替两者,程序编译刚运行的时候没问题,可是一接收数据的时候程序就崩溃了,这是什么情况???GlobalAlloc和new 不是都是从程序的堆上分配内存么?那用起来就没有什么问题呀,是不是?
buff 要么全局 要么 从堆分配
memcpy( &pSI->buffRecv[pSI->dwBytesRecv],pSI->wsaBuf.buf,dwBytesTransferred);
main中 char buff[DATA_BUFSIZE]没有错呀,buff 在这里不用全局 或者 从堆分配呀。
main中 char buff[DATA_BUFSIZE]缓存区只是存储数据的一个过渡作用,然后他就把数据存储到全局数据g_sockets[g_dwEventTotal]->wsaBuf.buf = buff 了。在 ProcessThreadIO 中使用已经存储好数据不是从main中的buff取的,而是在全局缓冲区取的 memcpy( &pSI->buffRecv[pSI->dwBytesRecv],pSI->wsaBuf.buf,dwBytesTransferred);所以 main中 char buff[DATA_BUFSIZE]没有错呀,buff 在这里不用全局 或者 从堆分配。
小弟我这样分析对的吧???
pSI结构贴出来
memcpy( &pSI->buffRecv[pSI->dwBytesRecv],pSI->wsaBuf.buf,dwBytesTransferred)
按照上下文 pSI->wsaBuf.buf 为指针 buff为局部变量 ,这里跟SI->buffRecv[pSI->dwBytesRecv]是否为数组 指针 没关系
还有哦,主线程main的WSARecv 和 工作处理线程ProcessTreadIO的WSARecv 同时面对请求到来的时候不会冲突的吗???
可是《vc++深入详解》这本书讲到主线程和其他线程在单CPU平台上是交替运行的哦。这怎么解释 ???
不是啊,你先仔细看看那代码,两个线程都有WSARecv() !!!我这里的疑问是:当客户端第一次发命令给服务器(也就是客户端传用户名给服务器),这时候是主线程main 的WSARecv()接收的。 然后当客户端第二次发命令给服务器(也就是客户端传密码给服务器),这时候就是处理线程ProcessTreadIO 的WSARecv()接收了,而不是主线程main 的WSARecv()接收了为什么主线程main的WSARecv() 不能再接收 客户端发来的命令的了????
如果我把处理线程ProcessTreadIO 的WSARecv()注释掉,客户端就不能再传命令给服务器了(只能第一次传送),为什么呀???
//接受数据
int RecvReq( LPSOCKET_INF pSI )
{
static DWORD dwRecvBytes = 0;
pSI->nStatus = WSA_RECV; DWORD dwFlags = 0;
// memset(&(pSI->o), 0,sizeof(WSAOVERLAPPED));
ZeroMemory(&(pSI->o),sizeof(pSI->o));
pSI->o.hEvent = g_events[g_index - WSA_WAIT_EVENT_0];
pSI->wsaBuf.len = DATA_BUFSIZE; if (WSARecv(pSI->s, &(pSI->wsaBuf), 1, &dwRecvBytes,
&dwFlags,&(pSI->o), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() faileddd with error %d\n", WSAGetLastError());
return -1;
}
}
return 0;
}问题在楼上!
这种事件通知方式,对内核编程不熟悉是很难理解,因为不是顺序的流程;建议刚开始学Socket先从阻塞方式学,慢慢深入了再去研究更高深的知识。