WSAAsyncSelect模型

Table of Contents

    这个模型跟窗口有关,它使用消息来进行通知的。如下是对话框客户端关键代码,连接的是一个echo服务端。

    测试文件的传输,将文件发送出去,然后将读取的数据写入文件。

    读写文件类WinFile参考: http://blog.csdn.net/tujiaw/article/details/17840823

    Buffer类参考:http://blog.csdn.net/tujiaw/article/details/8872865

    1. 自定义消息: #define WM_ASYNC_SELECT (WM_USER + 103)

    2. 消息映射: ON_MESSAGE(WM_ASYNC_SELECT, &CSmallFileDlg::OnAsyncSelect)

    afx_msg LRESULT CSmallFileDlg::OnAsyncSelect(WPARAM wParam, LPARAM lParam)
    {
    	switch (WSAGETSELECTEVENT(lParam))
    	{
    	case FD_CONNECT:
    		if (WSAGETSELECTERROR(lParam))
    		{
    			MessageBox(GetErrorString(WSAGETSELECTERROR(lParam), L"FD_CONNECT"));
    		}
    		else
    		{
    			MessageBox(L"连接成功");
    		}
    		break;
    
    
    	case FD_READ:
    		{
    			recvBuffer();
    			static WinFile fr(L"C:\\Users\\jiaw\\Desktop\\2.flv", false);
    			static size_t totalWritten = 0;
    			size_t currentWritten;
    			int ret = fr.write(m_input.peek(), m_input.readableBytes(), ¤tWritten);
    			if (0 != ret) {
    				MessageBox(GetErrorString(ret, L"FD_READ"));
    				::shutdown(m_socket, SD_BOTH);
    				::closesocket(wParam);
    			} else {
    				totalWritten += currentWritten;
    				m_input.retrieve(currentWritten);
    				fr.seek(totalWritten);
    			}
    		}
    		break;
    
    
    	case FD_WRITE:
    		sendBuffer(); // send当网络缓冲区满的时候会产生WSAEWOULDBLOCK错误码,此时触发写事件继续写入
    		break;
    
    
    	case FD_CLOSE:
    		if (WSAGETSELECTERROR(lParam))
    		{
    			MessageBox(GetErrorString(WSAGetLastError(), L"FD_CLOSE"));
    		}
    		::shutdown(m_socket, SD_BOTH);
    		::closesocket(wParam);
    		MessageBox(L"close");
    		break;
    	}
    
    
    	return 0;
    }
    
    
    void CSmallFileDlg::OnBnClickedConnect()
    {
    	const char *ip = "192.168.239.150";
    	unsigned short port = 5000;
    	struct sockaddr_in sin;
    	memset(&sin, 0, sizeof(sin));
    	sin.sin_family = AF_INET;
    	sin.sin_addr.S_un.S_addr = inet_addr(ip);
    	sin.sin_port = htons(port);
    	m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if (INVALID_SOCKET == m_socket) {
    		MessageBox(GetErrorString(WSAGetLastError(), L"socket"));
    		return;
    	}
    
    	// 会自动将socket设为非阻塞
    	if (SOCKET_ERROR == WSAAsyncSelect(m_socket, m_hWnd, WM_ASYNC_SELECT, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE)) {
    		MessageBox(GetErrorString(WSAGetLastError(), L"WSAAsyncSelect"));
    		::closesocket(m_socket);
    	}
    
    
    	if (SOCKET_ERROR == ::connect(m_socket, (sockaddr*)&sin, sizeof(sin))) {
    		int errCode = WSAGetLastError();
    		if (WSAEWOULDBLOCK != errCode) { // 非阻塞连接处理WSAEWOULDBLOCK
    			MessageBox(GetErrorString(WSAGetLastError(), L"connect"));
    			::closesocket(m_socket);
    			return;
    		}
    	}
    }
    
    void CSmallFileDlg::OnBnClickedDisconnect()
    {
    	::shutdown(m_socket, SD_BOTH);
    	::closesocket(m_socket);
    }
    
    void CSmallFileDlg::OnBnClickedSend()
    {
    	WinFile fr(L"C:\\Users\\jiaw\\Desktop\\1.flv", true);
    	char buf[65536];
    	size_t result;
    	DWORD total = fr.size();
    	DWORD nread = 0;
    	while (nread != total) {
    		int ret = fr.read(buf, sizeof(buf), &result);
    		if (0 != ret) {
    			MessageBox(GetErrorString(ret));
    			break;
    		}
    
    
    		nread += result;
    		fr.seek(nread); // 设置下次的读取位置,从0开始偏移
    
    
    		m_output.append(buf, result);
    		sendBuffer();
    	}
    }
    
    void CSmallFileDlg::sendBuffer()
    {
    	int ret = ::send(m_socket, m_output.peek(), m_output.readableBytes(), 0);
    	if (ret > 0)
    	{
    		m_output.retrieve(ret);
    		m_sendNum += ret; // 发送消息计数
    		UpdateData(FALSE);
    	}
    	else if (SOCKET_ERROR == ret)
    	{
    		int errCode = WSAGetLastError();
    		if (WSAEWOULDBLOCK != errCode)
    		{
    			MessageBox(GetErrorString(errCode), L"send");
    		}
    	}
    }
    
    void CSmallFileDlg::recvBuffer()
    {
    	char buf[65536] = {0};
    	int ret = ::recv(m_socket, buf, sizeof(buf) - 1, 0);
    	if (ret > 0)
    	{
    		m_recvNum += ret; // 接收消息计数
    		UpdateData(FALSE);
    		m_input.append(buf, ret);
    	}
    	else if (0 == ret)
    	{
    		MessageBox(L"连接优雅的关闭");
    	}
    	else
    	{
    		MessageBox(GetErrorString(WSAGetLastError(), L"recv"));
    	}
    }
    
    
    // 产生随机字符串
    std::string BuildRandString(int num)
    {
    	static unsigned int s_add = 0;
    	std::string ret;
    	srand((unsigned int)time(NULL) + (s_add++));
    	for (int i=0; i<num; )
    	{
    		char buf[17] = {0};
    		_itoa_s(rand(), buf, 0x10);
    		ret += buf;
    		i += strlen(buf);
    	}
    	return ret.substr(0, num);
    }
    
    void DebugLog(const char *fmt, ...)
    {
    	va_list ap;
    	va_start(ap, fmt);
    	char buf[1024] = {0};
    	vsnprintf_s(buf, sizeof(buf), fmt, ap);
    	MessageBoxA(NULL, buf, "debug log", MB_OK);
    	va_end(ap);
    }