vs tcp的使用
本文以mfc 对话框为作为前端显示,实现tcp的连接。
1 在OnInitDialog函数里加上下面的代码,调用WSAStartup,看网上示例很多都没提到。
WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData);
如果不调用WSAStartup,bind() 函数会返回,GetLastError() 后显示原因。
2 程序的最后要释放资源
WSACleanup();
3 服务端 bind listen
tcp的服务端使用bind函数监听端口。
定义几个成员变量。
SOCKET m_SOCKET_listen;
sockaddr_in m_sockaddr_listen;
SOCKET m_SOCKET_toClient;
sockaddr_in m_sockaddr_toClient;
绑定监听端口
void CMFCApp_TcpDemo1Dlg::OnBnClickedBtnBindLocalPort() { UpdateData(TRUE); // 初始化地址端口信息 m_sockaddr_listen.sin_family = AF_INET; m_sockaddr_listen.sin_port = htons(m_n_localPort); m_sockaddr_listen.sin_addr.S_un.S_addr = INADDR_ANY; // 初始化socket m_SOCKET_listen = ::socket(AF_INET, SOCK_STREAM, 0);
// 绑定、监听 if (bindAndListen(m_SOCKET_listen, m_sockaddr_listen) == SOCKET_ERROR) { DWORD dwError = GetLastError(); UpdateTcpInfoNextLine_FormatInt(_T("监听端口失败,Error Code : [%d]"), dwError); return; } UpdateTcpInfoNextLine(_T("绑定端口并监听成功!")); ::WSAAsyncSelect(m_SOCKET_listen, this->m_hWnd, WM_SOCKET, FD_ACCEPT|FD_READ); }
int CMFCApp_TcpDemo1Dlg::bindAndListen(const SOCKET socket, const sockaddr_in addr) { if (::bind(socket, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) return SOCKET_ERROR; if (::listen(socket, 5) == SOCKET_ERROR) return SOCKET_ERROR; return 0; }
处理监听消息
#define WM_SOCKET_LISTEN WM_USER + 200 // 方法声明 afx_msg LRESULT OnSocketListen(WPARAM wParam, LPARAM lParam); // 消息映射
ON_MESSAGE(WM_SOCKET_LISTEN,&CMFCApp_TcpDemo1Dlg::OnSocketListen) // 方法实现 LRESULT CMFCApp_TcpDemo1Dlg::OnSocketListen(WPARAM wParam, LPARAM lParam) { CString str13; switch (lParam) { case FD_ACCEPT: { int size = sizeof(m_sockaddr_toClient); m_SOCKET_toClient = ::accept(m_SOCKET_listen, (sockaddr*)&m_sockaddr_toClient, &size); m_n_connectCnt = m_n_connectCnt + 1; UpdateTcpInfoNextLine_FormatInt("连接数%d", m_n_connectCnt); CString my_Str_msg; my_Str_msg.AppendFormat(_T("%s登录"), ::inet_ntoa(m_sockaddr_toClient.sin_addr)); UpdateTcpInfoNextLine(my_Str_msg); } break; case FD_READ: { char my_c_rcv[100] = {0}; CString my_Str_msg; ::recv(m_SOCKET_toClient, my_c_rcv, 100, 0); my_Str_msg += (LPCTSTR)::inet_ntoa(m_sockaddr_toClient.sin_addr); my_Str_msg += ":"; my_Str_msg += (LPCTSTR)my_c_rcv; UpdateTcpInfoNextLine(my_Str_msg); } break; } return true; }
4 客户端
连接远程服务器
void CMFCApp_TcpDemo1Dlg::OnBnClickedBtnConnect() { // 指定远程客户端的连接信息 CString str = _T("127.0.0.1"); UpdateData(TRUE); m_sai_destClient.sin_family = AF_INET; m_sai_destClient.sin_addr.S_un.S_addr = inet_addr(str.GetBuffer(1)); m_sai_destClient.sin_port = htons(m_n_destPort); m_SOCKET = ::socket(AF_INET, SOCK_STREAM, 0); if (::connect(m_SOCKET, (sockaddr*)&m_sai_destClient, sizeof(m_sai_destClient)) != SOCKET_ERROR) { UpdateTcpInfoNextLine(_T("连接成功!"));
::WSAAsyncSelect(m_SOCKET, this->m_hWnd, WM_SOCKET_CLIENT, FD_READ); } else { CString str; DWORD dwError = GetLastError(); str.Format(_T("连接失败,Error Code : [%d]"), dwError); char szError[256]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, szError, sizeof(szError), NULL); UpdateTcpInfoNextLine(str + szError); } }
发送数据
void CMFCApp_TcpDemo1Dlg::OnBnClickedBtnSend() { UpdateData(TRUE); int sendSize = ::send(m_SOCKET, m_Str_send.GetBuffer(1), m_Str_send.GetLength(), 0); UpdateTcpInfoNextLine_FormatInt(_T("sendSize, %d"), sendSize); }
接收数据,每次给服务端发送数据之后,服务端会返回客户端的内容。
在客户端connect之后,绑定消息映射。
::WSAAsyncSelect(m_SOCKET, this->m_hWnd, WM_SOCKET_CLIENT, FD_READ); (上面的代码已经绑定了),窗口消息与函数绑定的代码和服务端类似。
下面是客户端接收信息的方法。
LRESULT CMFCApp_TcpDemo1Dlg::OnSocket(WPARAM wParam, LPARAM lParam) { switch (lParam) { case FD_READ: { char my_c_rcv[100] = {0}; CString my_Str_msg(_T("server port: ")); ::recv(m_SOCKET, my_c_rcv, 100, 0); my_Str_msg += (LPCTSTR)::inet_ntoa(m_sai_destClient.sin_addr); my_Str_msg += ", server: "; my_Str_msg += (LPCTSTR)my_c_rcv; UpdateTcpInfoNextLine(my_Str_msg); } break; default: break; } return true; }