(MFC实现)TCP服务器设计 - 每个客户连接对应一个线程

2 篇文章 0 订阅
2 篇文章 0 订阅

如果服务器支持线程,那么对于客户/服务进程的架构我们可以采取一个线程处理一个客户连接的设计方案。也就是每当有新的连接请求到达服务器时,服务器会新开一个子线程来专门处理这个连接的信息传递;这种方法类似于服务器为每个客户连接fork一个子进程,但这相对来说更轻量级。

开始之前在头文件中声明一下变量和函数

static DWORD WINAPI WaitProc(LPVOID lpPar);
static DWORD WINAPI RespondProc(LPVOID lpPar);
HANDLE m_hWaitThread; /* 等待连接线程 */
HANDLE m_hRespondThread; /* 通信线程句柄 */

首先,初始化服务器socket

BOOL CTCPServerDlg::InitSocket()
{
	WSADATA wsaData;
	WORD sockVersion=MAKEWORD(2,2);
	if(WSAStartup(sockVersion,&wsaData))
	{
		AfxMessageBox(_T("failed to load winsock!"),MB_OK|MB_ICONSTOP);
		return 0;
	}
	listensocketfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	saServer.sin_family=AF_INET;
	saServer.sin_addr.S_un.S_addr=inet_addr(m_servip);
	saServer.sin_port=htons(m_servport);
	bind(listensocketfd,(sockaddr*)&saServer,sizeof(saServer));
	listen(listensocketfd,50);
	return TRUE;
}
然后,在主线程中启动一个等待连接的线程

BOOL CTCPServerDlg::StartService()
{
	/* 启动等待连接线程 */
	DWORD dwThreadId = 0;

	m_hWaitThread = ::CreateThread(NULL, NULL, CTCPServerDlg::WaitProc, ((LPVOID)this), 0, &dwThreadId); 

	CloseHandle(m_hWaitThread);
	return TRUE;
}

下面是WaitProc线程函数的实现

DWORD WINAPI CTCPServerDlg::WaitProc(LPVOID lpPar)
{
	sockaddr_in remoteAddr;
	int nAddrLen = sizeof(remoteAddr);

	CTCPServerDlg *pThis = (CTCPServerDlg*)lpPar;

	CString strNotice; /* 通知消息 */

	while(1)
	{
		Sleep(10);

		/* 创建通信套接字 */
		SOCKET *pClientSocket = new SOCKET; 
		*pClientSocket = ::accept(pThis->listensocketfd, (SOCKADDR*)&remoteAddr, &nAddrLen);
		
		if(INVALID_SOCKET == *pClientSocket)
		{
			strNotice = "accept()失败,再次尝试 ...... ";
			::AfxMessageBox(strNotice);
			continue;
		}

		DWORD dwThreadId = 1;

		/* 启动相应线程与客户端通信 */
		pThis->m_hRespondThread = ::CreateThread(NULL, NULL, CTCPServerDlg::RespondProc, ((LPVOID)pClientSocket), 0, &dwThreadId); 
		
		CloseHandle(pThis->m_hRespondThread);
	}
	return 0;
}

其中,为每一个请求连接的客户端都开辟了一个新的线程来处理客户端和服务器的通信

DWORD WINAPI CTCPServerDlg::RespondProc(LPVOID lpPar)
{
	SOCKET *pClientSocket = (SOCKET*)lpPar;
	char buff[4096]; /* 接收数据缓冲区 */

	struct sockaddr_in sa; 
	int len = sizeof(sa); 
	getpeername(*pClientSocket, (struct sockaddr *)&sa, &len);  

	while(1)
	{
		Sleep(10);
		int nRecv = ::recv(*pClientSocket, buff, 4096, 0);
		if(nRecv > 0)
		{
			buff[nRecv] = '\0';
		}

        //对接收的数据进行处理
        .......
       }
}

这样便实现了服务器端的多线程模型

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,你可以参考以下代码实现: 1. 在MFC框架中,可以使用CWnd::SetTimer函数来启动定时器。例如,在窗口的OnCreate函数中添加以下代码: ``` SetTimer(1, 1000, NULL); // 每隔1秒触发一次定时器,定时器ID为1 ``` 2. 在定时器消息响应函数中,创建TCP客户端连接。例如,可以使用Winsock API中的socket, connect等函数,实现TCP客户端连接。 ``` void CYourWnd::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) // 判断是哪个定时器 { KillTimer(1); // 先停止定时器,避免重复触发 // 创建客户端socket SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket == INVALID_SOCKET) { // 处理socket创建失败的情况 return; } // 设置服务器地址和端口 SOCKADDR_IN serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8888); // 假设服务器端口为8888 serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 假设服务器IP为127.0.0.1 // 连接服务器 int ret = connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)); if (ret == SOCKET_ERROR) { // 处理连接失败的情况 closesocket(clientSocket); SetTimer(1, 1000, NULL); // 重新启动定时器,继续等待连接 return; } // 处理连接成功的情况 // TODO: 发送和接收数据 closesocket(clientSocket); } CWnd::OnTimer(nIDEvent); } ``` 注意,上述代码只是一个简单的示例,实际应用中需要根据具体需求进行修改和完善。同时,需要注意TCP连接的建立可能会比较耗时,如果频繁地启动和停止定时器可能会影响程序的性能。因此,建议在实现中加入必要的优化措施,如增加连接重试次数、设置连接超时时间等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值