socket编程,简单多线程服务端测试程序
前些天重温了MSDN关于socket编程的WSAStartup、WSACleanup、socket、closesocket、bind、listen、accept、recv、send等函数的介绍,今天写了一个CUI界面的测试程序(依赖MFC)作为补充。程序功能简介如下:
1:一个线程做监听用。
2:监听线程收到客户端连接后,创建新线程接收客户端数据。所有对客户端线程将加入容器,以便管理。
3:服务端打印所有客户端发来的信息。
4:服务端CUI界面输入数字0,将关闭所有连接线程,释放socket,并退出程序。
程序实现依赖类:
1:MFC
2:CSingleton模板,我关于singleton实现的文章中有源码。http://www.cnblogs.com/hgwang/p/6085922.html
3:CThreadLockCs,CRITICAL_SECTION封装类,我关于singleton实现的文章中有源码。http://www.cnblogs.com/hgwang/p/6085922.html。
4:windows socket库,我关于windows socket的文章中有介绍及调用方法,http://www.cnblogs.com/hgwang/p/6074038.html。
下面是该测试程序服务端封装类的源码:
Listen.h头文件
#pragma once #ifndef WHG_LISTEN
#define WHG_LISTEN #include "Singleton.h" class CListen
{
public:
CListen(void);
~CListen(void);
//类入口点,会创建监听线程
void SetAddress(unsigned short port,std::string ip="");
//用于线程访问关闭socket,并为CListen记录关闭信息
void DeleteLink(SOCKET s);
private:
//线程和CListen类共享的任务信息
struct ThreadSocketInfo
{
CWinThread* pWt;
SOCKET* pS;
CListen* pListen;
ThreadSocketInfo()
{
pListen = NULL;
pWt = NULL;
pS = NULL;
}
~ThreadSocketInfo()
{
pListen = NULL;
pWt = NULL;
pS = NULL;
}
};
//监听socket
SOCKET listensocket;
//监听线程指针
CWinThread* m_pListenThread;
//任务信息列表
std::vector<ThreadSocketInfo> vec_WorkThreads;
//当前监听ip和端口
unsigned short m_port;
std::string m_ip;
//线程互斥访问锁
CThreadLockCs m_tlcs; private:
//监听线程工作对象
static UINT AFX_CDECL ListenThread(LPVOID);
//对客户端线程工作对象
static UINT AFX_CDECL WorkThread(LPVOID);
//bind、listen、accept实现
void Address(unsigned short port,std::string ip="");
}; //申明singleton的监听线程访问对象,全局唯一实例
typedef CSingleton<CListen> LISTEN; #endif
Listen.cpp:
#include "StdAfx.h"
#include "Listen.h" CListen::CListen(void)
:m_pListenThread(NULL)
{
listensocket = ;
WSAData wsa;
if (WSAStartup(MAKEWORD(,),&wsa) != )
{
cout<<"WSAStartup "<<endl;
WSACleanup();
}
else
{
listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (listensocket == INVALID_SOCKET)
{
cout<<"socket() error,error code "<<WSAGetLastError()<<endl;
}
}
} CListen::~CListen(void)
{
//要先关闭对客户端连接,再关闭监听socket
if (vec_WorkThreads.size() > )
{
std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin();
for (;iter!=vec_WorkThreads.end();iter++)
{
SOCKET* s = (*iter).pS;
if (s)
{
closesocket(*s);
delete s;
(*iter).pS = NULL;
}
CWinThread* pwt = (*iter).pWt;
if (pwt)
{
WaitForSingleObject(pwt->m_hThread,INFINITE);
delete pwt;
(*iter).pWt = NULL;
}
}
}
if (listensocket)
{
closesocket(listensocket);
}
WSACleanup();
cout<<"WSACleanup "<<endl; if (m_pListenThread)
{
WaitForSingleObject(m_pListenThread->m_hThread,INFINITE);
delete m_pListenThread;
}
vec_WorkThreads.clear();
} //监听线程工作对象
UINT CListen::ListenThread(LPVOID param)
{
CListen* p = (CListen*)param;
if (p)
{
p->Address(p->m_port,p->m_ip);
}
return ;
} //对客户端线程工作对象
UINT CListen::WorkThread(LPVOID param)
{
ThreadSocketInfo* tsi = (ThreadSocketInfo*)param;
SOCKET acp = *(tsi->pS);
CListen* pListen = tsi->pListen;
delete tsi; do
{
char buf[];
int len = recv(acp,buf,,);
if (len == )
{
cout<<"connection has been closed "<<endl;
break;
}
else if (len == SOCKET_ERROR)
{
cout<<"recv error,error code "<<WSAGetLastError()<<endl;
break;
}
else
{
char* outbuf = new char[len+];
memcpy(outbuf,buf,len);
outbuf[len] = ;
cout<<"recv data,"<<outbuf<<endl;
delete outbuf;
}
} while ();
//删除当前连接记录
pListen->DeleteLink(acp);
return ;
} //用于线程访问关闭socket,并为CListen记录关闭信息
void CListen::DeleteLink(SOCKET s)
{
m_tlcs.lock();
std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin();
for (;iter!=vec_WorkThreads.end();iter++)
{
SOCKET* ss = (*iter).pS;
if (ss && *ss == s)
{
closesocket(s);
delete ss;
iter->pS = NULL;
}
}
m_tlcs.unlock();
} //类入口点,会创建监听线程
void CListen::SetAddress(unsigned short port,std::string ip)
{
//监听线程只允许启动一次
if (m_pListenThread == NULL)
{
m_ip = ip;
m_port = port;
m_pListenThread = AfxBeginThread(ListenThread,this,THREAD_PRIORITY_NORMAL,,CREATE_SUSPENDED,NULL);
if (m_pListenThread)
{
m_pListenThread->m_bAutoDelete = FALSE ;
m_pListenThread->ResumeThread();
}
}
} //bind、listen、accept实现
void CListen::Address(unsigned short port,std::string ip)
{
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_port = htons(port);
if (ip.empty())
{
service.sin_addr.s_addr = INADDR_ANY;
}
else
{
service.sin_addr.s_addr = inet_addr(ip.c_str());
} if (bind(listensocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
{
cout<<"bind() error,error code "<<WSAGetLastError()<<endl;
return;
}
cout<<"bind "<<endl; if (listen(listensocket,SOMAXCONN) == SOCKET_ERROR)
{
cout<<"listen() error,error code "<<WSAGetLastError()<<endl;
return;
}
cout<<"listen "<<endl; while ()
{
sockaddr_in recvLinkAddr;
int recvAddr = sizeof(recvLinkAddr);
SOCKET acp = accept(listensocket,(sockaddr*)&recvLinkAddr,&recvAddr);
if (acp == INVALID_SOCKET)
{
cout<<"accept error,error code "<<WSAGetLastError()<<endl;
return;
}
cout<<"获取新的连接请求,ip:"<<inet_ntoa(recvLinkAddr.sin_addr)<<",port:"<<recvLinkAddr.sin_port<<endl; SOCKET* s = new SOCKET(acp);
ThreadSocketInfo* tsi = new ThreadSocketInfo;
tsi->pListen = this;
tsi->pS = s;
CWinThread* workthread = AfxBeginThread(WorkThread,tsi,THREAD_PRIORITY_NORMAL,,CREATE_SUSPENDED,NULL);
if (workthread)
{
workthread->m_bAutoDelete = FALSE;
workthread->ResumeThread();
tsi->pWt = workthread;
ThreadSocketInfo t = *tsi;
m_tlcs.lock();
vec_WorkThreads.push_back(t);
m_tlcs.unlock();
}
}
}
客户端代码:
// WinsockClient.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"input id:";
std::string str;
cin>>str; WSAData wsa;
if (WSAStartup(MAKEWORD(,),&wsa) != )
{
WSACleanup();
return ;
}
SOCKET cnetsocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
do
{
if (cnetsocket == INVALID_SOCKET)
break;
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons();
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
{
break;
}
str += " : windows socket test!";
while ()
{
int len = send(cnetsocket,str.c_str(),str.length(),);
cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl;
if (len < str.length())
{
cout<<"data send uncompleted"<<endl;
str = str.substr(len+,str.length());
len = send(cnetsocket,str.c_str(),str.length(),);
cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl;
}
else if (len == SOCKET_ERROR)
{
break;
}
Sleep();
}
} while ();
closesocket(cnetsocket);
WSACleanup(); return ;
}
main函数:
// MultithreadServer.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include "MultithreadServer.h" #include "Listen.h"
#include "Singleton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = ; // initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), ))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = ;
}
LISTEN::Instance()->SetAddress(,"127.0.0.1"); int outId;
cin>>outId;
if (outId == )
{
LISTEN::Close();
}
return nRetCode;
}
测试结果:
1:4个客户端连接
2:客户端4关闭连接
3:输入0,关闭整个服务端,自动断开1.2.3的客户端
这里面涉及到几个错误代码,中文说明如下:
1:10054,远程主机强迫关闭了一个现有的连接。
2:10053,你的主机中的软件中止了一个已建立的连接。
3: 10004,一个*操作被对 WSACancelBlockingCall 的调用中断。
至此,程序正常结束。