WinSock客户端服务端实现--TCP

1.服务端代码

a)初始化winsock库,在Server.cpp中添加

WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
::WSAStartup(sockVersion, &wsaData);
CserverDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
	// TODO: 在此放置处理何时用
	//  “确定”来关闭对话框的代码
}

 b)创建Server端的监听socket

void CserverDlg::OnBnClickedButton1()
{
	m_ip.ResetContent();
	m_list.ResetContent();
	// TODO: 在此添加控件通知处理程序代码
	if(m_socket == INVALID_SOCKET)  // 开启服务
	{
		// 取得端口号
		CString sPort;
		m_port.GetWindowText( sPort );
		int nPort = atoi(sPort);
		if(nPort < 1 || nPort > 65535)
		{
			m_info.AddString( "端口号错误!" );
			return;
		}

		// 创建监听套节字,使它进入监听状态
		if( !CreateAndListen(nPort) )
		{
			m_info.AddString(  "启动服务出错!" );
			return;
		}
	}
	else				// 停止服务
	{
		// 关闭所有连接
		CloseAllSocket();
		m_info.AddString( "所有连接已断开!已停止监听!" );
		// 设置相关子窗口控件状态
		m_start.SetWindowText( "开启服务" );
		m_port.EnableWindow( TRUE );
		m_msg.EnableWindow( FALSE );
		m_send.EnableWindow( FALSE );
	}
}
c)绑定地址端口等信息

BOOL CserverDlg::CreateAndListen(int nPort)
{
	if(m_socket == INVALID_SOCKET)
		::closesocket(m_socket);

	// 创建套节字
	m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(m_socket == INVALID_SOCKET)
		return FALSE;
	
	// 填写要关联的本地地址
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(nPort);
	sin.sin_addr.s_addr = INADDR_ANY;
	// 绑定端口
	if(::bind(m_socket, (sockaddr*)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
		return FALSE;
	}

	// 设置socket为窗口通知消息类型
	::WSAAsyncSelect(m_socket, m_hWnd, WM_SOCKET, FD_ACCEPT|FD_CLOSE);
	// 进入监听模式
	::listen(m_socket, 5);
	m_info.AddString( "已开启监听!监听中..." );
	m_start.SetWindowText( "关闭服务" );
	m_port.EnableWindow( FALSE );
	m_msg.EnableWindow( TRUE );
	m_send.EnableWindow( TRUE );
	return TRUE;
}
d)客户端请求连接时

BOOL CserverDlg::AddClient(SOCKET s)
{
	if( m_client<MAX_SOCKET)
	{
		// 添加新的成员
		m_arClient[m_client++] = s;
		return TRUE;
	}
	return FALSE;
}

e)客户端断开连接时

void CserverDlg::RemoveClient(SOCKET s)
{
	BOOL bFind = FALSE;
	int i=0;
	for( i=0; i<m_client; i++ )
	{
		if( m_arClient[i]==s )
		{
			bFind = TRUE;
			break;
		}
	}

	// 如果找到就将此成员从列表中移除
	if(bFind)
	{
		sockaddr_in sockAddr;
		memset(&sockAddr, 0, sizeof(sockAddr));
		int nSockAddrLen = sizeof(sockAddr);
		::getpeername(m_arClient[i], (SOCKADDR*)&sockAddr, &nSockAddrLen);
		// 转化为主机字节顺序
		int nPeerPort = ::ntohs(sockAddr.sin_port);
		// 转化为字符串IP
		CString sPeerIP = ::inet_ntoa(sockAddr.sin_addr);
		m_info.AddString( "##客户端["+sPeerIP+"]已断开连接!" );
		CString sTmp;
		for( int k=0; k<m_ip.GetCount(); k++ )
		{
			m_list.GetText( k, sTmp );
			if( sTmp==sPeerIP ) m_list.DeleteString( k );
			m_ip.GetLBText( k, sTmp ) ;
			if( sTmp==sPeerIP ) 
			{
				m_ip.DeleteString( k ); 
				break;
			}
		}
		m_client--;
		// 将此成员后面的成员都向前移动一个单位
		for(int j=i; j<m_client; j++)
		{
			m_arClient[j] = m_arClient[j+1];
		}
	}
	if( m_ip.GetCount()==0 ) m_ip.SetWindowText( "" );
}

f)消息响应函数

afx_msg LRESULT CserverDlg::OnSocket(WPARAM wParam, LPARAM lParam)
{
	// 取得有事件发生的套节字句柄
	SOCKET s = wParam;
	// 查看是否出错
	if( WSAGETSELECTERROR(lParam) )
	{
		RemoveClient(s);
		::closesocket(s);
		return 0;
	}
	// 处理发生的事件
	switch( WSAGETSELECTEVENT(lParam) )
	{
		case FD_ACCEPT:		// 监听中的套接字检测到有连接进入
			{
				if( m_client<MAX_SOCKET )
				{
					// 接受连接请求,新的套节字client是新连接的套节字
					SOCKET client = ::accept(s, NULL, NULL);
					// 设置新的套节字为窗口通知消息类型
					int i = ::WSAAsyncSelect(client, 
						m_hWnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE );
					AddClient(client);
					// 取得对方的IP地址和端口号(使用getpeername函数)
					// Peer对方的地址信息
					sockaddr_in sockAddr;
					memset(&sockAddr, 0, sizeof(sockAddr));
					int nSockAddrLen = sizeof(sockAddr);
					::getpeername(client, (SOCKADDR*)&sockAddr, &nSockAddrLen);
					int nPeerPort = ::ntohs(sockAddr.sin_port);
					// 转化为字符串IP
					CString sPeerIP = ::inet_ntoa(sockAddr.sin_addr);
					CString info;
					info.Format( "检测到已建立新连接,客户端IP:%s", sPeerIP );
					m_list.AddString( sPeerIP );
					m_ip.AddString( sPeerIP );
					m_info.AddString( info );
					if( m_ip.GetCount()==1 ) m_ip.SetCurSel( 0 );
				}
				else
				{
					MessageBox("连接客户太多!");
				}
			}
			break;

		case FD_CLOSE:		// 检测到套接字对应的连接被关闭。
			{
				RemoveClient(s);
				::closesocket(s);
			}
			break;

		case FD_READ:		// 套接字接受到对方发送过来的数据包
			{

				// 取得对方的IP地址和端口号(使用getpeername函数)
				// Peer对方的地址信息
				sockaddr_in sockAddr;
				memset(&sockAddr, 0, sizeof(sockAddr));
				int nSockAddrLen = sizeof(sockAddr);
				::getpeername(s, (SOCKADDR*)&sockAddr, &nSockAddrLen);
				// 转化为主机字节顺序
				int nPeerPort = ::ntohs(sockAddr.sin_port);
				// 转化为字符串IP
				CString sPeerIP = ::inet_ntoa(sockAddr.sin_addr);		 

				// 接受真正的网络数据
				char szText[1024] = { 0 };
				::recv(s, szText, 1024, 0);

				// 显示给用户
				CString strItem = "客户端["+sPeerIP+ "]: " + CString(szText);
				m_info.AddString( strItem );

			}
			break;
		case FD_WRITE:

			break;
	}
	return 0;
}
g)服务器端发送数据

void CserverDlg::OnBnClickedButton3()
{
	// TODO: 在此添加控件通知处理程序代码
	if( m_ip.GetCount()==0 ) return;
	m_CurClient = INVALID_SOCKET;
	sockaddr_in sockAddr;
	memset(&sockAddr, 0, sizeof(sockAddr));
	int nSockAddrLen = sizeof(sockAddr);
	CString sPeerIP ,sCurIP;
	m_ip.GetWindowText( sCurIP );
	for( int i=0; i<m_client; i++ )
	{
		::getpeername( m_arClient[i], (SOCKADDR*)&sockAddr, &nSockAddrLen);
		// 转化为主机字节顺序
		int nPeerPort = ::ntohs(sockAddr.sin_port);
		// 转化为字符串IP
		sPeerIP = ::inet_ntoa(sockAddr.sin_addr);
		if( sPeerIP==sCurIP )
		{
			m_CurClient = m_arClient[i];
			break;
		}
	}
	if( m_CurClient==INVALID_SOCKET ) return;
	// 取得要发送的字符串
	CString sText;
	m_msg.GetWindowText( sText );

	// 添加一个“回车换行”
	// 注意,添加它并不是必须的,但是如果使用本软件作为客户端调试网络协议,
	// 比如SMTP、FTP等,就要添加它了。因为这些协议都要求使用“回车换行”作为一个命令的结束标记
	sText += "\r\n";

	// 发送数据到服务器
	if( ::send( m_CurClient, sText, sText.GetLength(), 0)!= -1 )
	{
		m_info.AddString( "本地发送->"+sPeerIP+":"+sText );
		m_msg.SetWindowText( "" );
	}
}





你可能感兴趣的:(tcp)