• 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( "" );
    	}
    }





  • 相关阅读:
    springboot2系列目录
    zookeeper 集群部署
    canal 配置 详细说明
    在Docker环境下部署Kafka
    Spring4新特性
    centos7 卸载 jdk
    Kafka安装
    Scala 面向对象(三):package 包 (二)
    Scala 基础(七):Scala 运算符
    Maven 专题(四):什么是Maven
  • 原文地址:https://www.cnblogs.com/arbboter/p/4225263.html
Copyright © 2020-2023  润新知