Visual C++ TCP网络编程学习
贝镜
2023-12-01
Visual C++网络编程学习
套接字地址结构体:
struct sockaddr_in{
short sin_family; //指定地址格式,用AF_INET表示TCP/IP协议
unsigned short sin_port; //端口号码
struct in_addr sin_addr; //IP地址
char sin_zero[8]; //统一结构体大小,预留下来的
}
地址设置:
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
addr.sin_addr.S_un.S_addr = inet_addr("168.192.1.10");
异步套接字 和 同步套接字
异步套接字 使socketAPI函数处于非阻塞模式, 通过消息机制,处理响应。
主要的消息有: FD_ACCEPT (客户端连接消息)
FD_READ (读取发送来的消息)
FD_CLOSE (客户端或服务器关闭消息)
FD_CONNECT (connect函数在异步情况下,不会直接返回连接成功,会将连接结果通过消息FD_CONNECT发送)
同步套接字 会使socketAOI函数处于阻塞模式,直到有函数有响应是才返回。往往使用多线程进行操作。
异步套接字处理主要流程:
创建好SOCKET后,调用:
::WSAAsyncSelect(mServerSock, m_hWnd, MY_SOCKET, FD_ACCEPT|FD_READ|FD_CLOSE)
定义消息:#define MY_SOCKET (WM_USER + 105)
在响应对话框类中添加处理函数:
在BEGIN_MESSAGE_MAP 和END_MESSAGE_MAP 之间加入:
ON_MESSAGE(MY_SOCKET, &CTCPServerDlg::OnMySock)
处理函数:
LRESULT CTCPServerDlg::OnMySock(WPARAM wParam, LPARAM lParam)
{
CString str;
char cs[100] = {0};
int row = mListCtrl.GetItemCount();
int iEvent = WSAGETSELECTEVENT(lParam); //消息类别
int iEventErr = WSAGETSELECTERROR(lParam); //错误代码
int len;
len=sizeof(SOCKADDR);
SOCKET CurSock= (SOCKET)wParam;
sockaddr_in connectAddr;
switch(iEvent) {
case FD_ACCEPT:
mAccpetSocksMap[iConnectNumber] = accept(CurSock, (sockaddr*)&connectAddr,&len);
mListCtrl.InsertItem(row,inet_ntoa(connectAddr.sin_addr));
str.Format("%d", ntohs(connectAddr.sin_port) );
mListCtrl.SetItemText(row, 1, str);
str.Format("%s-%d连接上,已有%d位客户端连接!\r\n",inet_ntoa(connectAddr.sin_addr),
ntohs(connectAddr.sin_port), iConnectNumber+1);
iConnectNumber++;
TRACE("连接了%d个客户端", iConnectNumber);
mListBox.InsertString(listCount++, str);
mListBox.SetCurSel(listCount - 1);
break;
case FD_READ:
recv(CurSock, cs, 100, 0);
sockaddr_in addr;
::getpeername(CurSock, (SOCKADDR*)&addr, &len);
str = inet_ntoa(addr.sin_addr);
str.AppendFormat("端口%d说:%s",ntohs(addr.sin_port),cs);
mListBox.InsertString(listCount++, str);
mListBox.SetCurSel(listCount - 1);
send(CurSock, "OK", 3, 0);
break;
case FD_CLOSE:
break;
case FD_CONNECT:
if(iEventErr == 0) {
KillTimer(7);
//AfxMessageBox("服务器连接成功");
mTCPStatusStatic.SetWindowText("电子标签服务器已连接");
mStationByTCP.isConnect = true;
} else {
//AfxMessageBox("服务器连接失败");
mTCPStatusStatic.SetWindowText("连接失败,正在重新连接");
}
break;
}
return TRUE;
}
注意事项:
1、在调用accept(CurSock, (sockaddr*)&connectAddr,&len)和getpeername(CurSock, (SOCKADDR*)&addr, &len)时,其第三个参数的应该用int len = sizeof(SOCKADDR)的引用,不能直接用(int *)sizeof(SOCKADDR)。
2、在异步上,判断客户端是否连接上服务器,应该用消息,在消息函数中处理FD_CONNECT情况,不能直接用connect函数返回值,其返回值为SOCKET_ERROR时,也可能连接成功。
3、异步情况上,在服务器端一般要处理FD_ACCEPT、FD_READ、FD_CLOSE消息。 而客户端一般处理FD_READ、FD_CLOSE、FD_CONNECT消息。
4、消息处理函数的两个参数分别可转换为:
WPARAM wParam; ---》SOCKET CurSock= (SOCKET)wParam;
LPARAM lParam;----》int iEvent = WSAGETSELECTEVENT(lParam);//消息类别 (包含FD_ACCEPT、FD_READ、FD_CLOSE、FD_CONNECT等)
LPARAM lParam;----》int iEventErr = WSAGETSELECTERROR(lParam); //错误代码(iEventErr ==0 表示成功)
服务器流程:
1、初始化WINSOCK---->WSAStartup(WINSOCK_VERSION, &wsaData);
2、初始化SOCKET ----->mServerSock = socket(AF_INET, SOCK_STREAM, 0);
3、设置sockaddr_in(IP和Port) 如:
localAddr.sin_family = AF_INET;
localAddr.sin_port = htons(10100);
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
4、将SOCKET和sockaddr_in绑定-->bind(mServerSock, (sockaddr*)&localAddr, sizeof(localAddr));
5、设置异步SOCKET--->::WSAAsyncSelect(mServerSock, m_hWnd, MY_SOCKET, FD_ACCEPT|FD_READ|FD_CLOSE)
6、开启监听--->::listen(mServerSock, MaxConnectNumber);
7、注册MY_SOCKET消息和其处理函数,如:
7-1、在.h文件中加入:#define MY_SOCKET (WM_USER + 101)
7-2、在类声明中加入:afx_msg LRESULT OnMySock(WPARAM wParam, LPARAM lParam);
7-3、在.cpp类实现中添加消息绑定注册宏:ON_MESSAGE(MY_SOCKET, OnMySock)//添加在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间
8、实现函数OnMySock。
9、添加发送函数---》send(mConnectSock, sendStr, 100, 0);
10、关闭服务器---》 closesocket(mServerSock);
11、关闭WINSOCK服务---》WSACleanup();
客户端流程:
1、初始化WINSOCK---->WSAStartup(WINSOCK_VERSION, &wsaData);
2、初始化SOCKET ----->mServerSock = socket(AF_INET, SOCK_STREAM, 0);
3、设置异步SOCKET--->::WSAAsyncSelect(mServerSock, m_hWnd, MY_SOCKET, FD_ACCEPT|FD_READ|FD_CLOSE)
4、设置远程服务器地址sockaddr_in remoteAddr;
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(Port);
remoteAddr.sin_addr.S_un.S_addr = inet_addr(ip);
5、注册MY_SOCKET消息和其处理函数,如:
5-1、在.h文件中加入:#define MY_SOCKET (WM_USER + 101)
5-2、在类声明中加入:afx_msg LRESULT OnMySock(WPARAM wParam, LPARAM lParam);
5-3、在.cpp类实现中添加消息绑定注册宏:ON_MESSAGE(MY_SOCKET, OnMySock)//添加在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间
6、实现函数OnMySock。//特别注意处理FD_CONNECT消息,判断连接是否成功
7、连接服务器---》::connect(mSock, (sockaddr*)&remoteAddr, sizeof(sockaddr));
8、添加发送函数---》send(mConnectSock, sendStr, 100, 0);
9、关闭服务器---》 closesocket(mServerSock);
10、关闭WINSOCK服务---》WSACleanup();