Select模型(一)——封包发送内容
针对以前程序,想加入一个新功能,客户端要求做加减乘除运算,并传递要进行运算的数值,然后服务器端进行处理。
于是我写了下面的代码:
服务器:
#include<iostream>
#include <WinSock2.h>
#include "CalcTypeDef.h"
using namespace std;
DWORD GetValidIP()
{
DWORD dwIP = inet_addr("127.0.0.1");
//获取主机名字
char szHostName[128];
int nRet = -1;
struct hostent* pHostTent = NULL;
nRet = gethostname(szHostName,sizeof(szHostName));
if (nRet!=0)
{
return dwIP;
}
pHostTent = gethostbyname(szHostName);
if (pHostTent!=NULL)
{
//获取可用ip地址
if (pHostTent->h_addr_list[0]!=NULL)
{
dwIP = *(DWORD*)(pHostTent->h_addr_list[0]);
return dwIP;
}
}
return dwIP;
}
DWORD WINAPI DealClientProc(LPVOID lpParam);
int main()
{
//开启网络库
WSADATA wsaData;
WORD wVersion = MAKEWORD(2,2);
if (WSAStartup(wVersion,&wsaData)!=0)
{
cout<<"open network library failed!"<<endl;
return -1;
}
//校验版本
if (HIBYTE(wsaData.wVersion)!=2 || LOBYTE(wsaData.wVersion)!=2)
{
cout<<"check the version failed!"<<endl;
return -1;
}
//创建套接字,测试ipv6
SOCKET ListenSocket = socket(AF_INET,SOCK_STREAM,0);
if (INVALID_SOCKET == ListenSocket)
{
WSACleanup();
cout<<"create listen socket failed!"<<endl;
return -1;
}
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = htons(9527);
//获取主机可用IP
SockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int nRet = bind(ListenSocket,(sockaddr*)&SockAddr,sizeof(SockAddr));
if (SOCKET_ERROR == nRet)
{
int nErr = GetLastError();
closesocket(ListenSocket);
WSACleanup();
cout<<"bind failed!"<<endl;
return -1;
}
if (SOCKET_ERROR == listen(ListenSocket,3))
{
closesocket(ListenSocket);
WSACleanup();
cout<<"listen failed!"<<endl;
return -1;
}
//循坏接收客户端的连接
sockaddr_in sockaddr_connect;
int nLen = sizeof(sockaddr_connect);
//保存所有客户端连接的集合
fd_set setClient;
FD_ZERO(&setClient);
BOOL bFlag = TRUE;
while (bFlag)
{
SOCKET sockConnect;
//判断是否有网络事件发生,比如有客户端进行连接,或者有数据可读取
fd_set setRead;
FD_ZERO(&setRead);
//将客户端socket和侦听socket加入要侦听的集合
setRead = setClient;
FD_SET(ListenSocket,&setRead);
//超时时间
timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
int nSelect = select(0,&setRead,NULL,NULL,&tv);
if (SOCKET_ERROR == nSelect)
{
continue;
}
else if (0==nSelect)
{
Sleep(5);
continue;
}
//判断客户端socket是否还在集合之中
for (int i=0;i<setClient.fd_count;i++)
{
//在集合之中
if (FD_ISSET(setClient.fd_array[i],&setRead))
{
STRU_CALCTYPE_ADD oRecvData;
int nRet = recv(setClient.fd_array[i],(char*)&oRecvData,sizeof(oRecvData),0);
if (SOCKET_ERROR==nRet)
{
break;
bFlag = FALSE;
}
else if (0 == nRet)
{
break;
bFlag = FALSE;
}
//获取运算类型
WORD wType = *(WORD*)&oRecvData;
int nSum = 0;
switch (wType)
{
case enum_calctype_add:
nSum = oRecvData.m_nFirstData+oRecvData.m_nSecondData;
cout<<"Recv Data:"<<oRecvData.m_nFirstData<<"+"<<oRecvData.m_nSecondData<<" = "<<nSum<<endl;
break;
case enum_calctype_sub:
nSum = oRecvData.m_nFirstData-oRecvData.m_nSecondData;
cout<<"Recv Data:"<<oRecvData.m_nFirstData<<"-"<<oRecvData.m_nSecondData<<" = "<<nSum<<endl;
break;
case enum_calctype_mul:
nSum = oRecvData.m_nFirstData*oRecvData.m_nSecondData;
cout<<"Recv Data:"<<oRecvData.m_nFirstData<<"*"<<oRecvData.m_nSecondData<<" = "<<nSum<<endl;
break;
case enum_calctype_div:
nSum = oRecvData.m_nFirstData/oRecvData.m_nSecondData;
cout<<"Recv Data:"<<oRecvData.m_nFirstData<<"/"<<oRecvData.m_nSecondData<<" = "<<nSum<<endl;
break;
}
//send(setClient.fd_array[i],(const char*)&nSum,sizeof(nSum),0);
}
}
//判断是否有连接
if (!FD_ISSET(ListenSocket,&setRead))
{
cout<<"the ListenSocket not in the setRead"<<endl;
continue;
}
sockConnect = accept(ListenSocket,(sockaddr*)&sockaddr_connect,&nLen);
if (INVALID_SOCKET == sockConnect)
{
cout<<"accept failed!"<<endl;
continue;
}
//将新连接的socket加入到setClient集合中
FD_SET(sockConnect,&setClient);
FD_ZERO(&setRead);
}
//关闭连接
closesocket(ListenSocket);
//关闭网络库
WSACleanup();
system("pause");
return 0;
}
客户端:
#include<iostream>
#include <WinSock2.h>
#include "CalcTypeDef.h"
using namespace std;
DWORD GetValidIP()
{
DWORD dwIP = inet_addr("127.0.0.1");
//获取主机名字
char szHostName[128];
int nRet = -1;
struct hostent* pHostTent = NULL;
nRet = gethostname(szHostName,sizeof(szHostName));
if (nRet!=0)
{
return dwIP;
}
pHostTent = gethostbyname(szHostName);
if (pHostTent!=NULL)
{
//获取可用ip地址
if (pHostTent->h_addr_list[0]!=NULL)
{
dwIP = *(DWORD*)(pHostTent->h_addr_list[0]);
return dwIP;
}
}
return dwIP;
}
int main()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
cout<<"open the sock library failed!"<<endl;
return -1;
}
//校验版本
if (HIBYTE(wsaData.wVersion)!=2 || LOBYTE(wsaData.wVersion)!=2)
{
cout<<"the version failed!"<<endl;
return -1;
}
//创建套接字
SOCKET ClientSock = socket(AF_INET,SOCK_STREAM,0);
if (INVALID_SOCKET == ClientSock)
{
WSACleanup();
cout<<"create listen socket failed!"<<endl;
return -1;
}
SOCKADDR_IN SockAddr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = htons(9527);
SockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
int nRet = connect(ClientSock,(const sockaddr*)&SockAddr,sizeof(SockAddr));
if(SOCKET_ERROR == nRet)
{
int nErr = GetLastError();
cout<<"connect failed!"<<endl;
closesocket(ClientSock);
WSACleanup();
return -1;
}
BOOL flag = TRUE;
STRU_CALCTYPE_ADD oAddData;
oAddData.m_wType = enum_calctype_sub;
while (flag)
{
cin>>oAddData.m_nFirstData>>oAddData.m_nSecondData;
//双方协调退出条件
if (oAddData.m_nFirstData==0 && oAddData.m_nSecondData == 0)
{
flag = FALSE;
//send(ClientSock,(const char*)SendBuffer,sizeof(SendBuffer),0);
continue;
}
//发送数据
send(ClientSock,(const char*)&oAddData,sizeof(oAddData),0);
cout<<"send to Server success!"<<endl;
////接收数据
/*int nSum = 0;
int nRecv = recv(ClientSock,(char*)&nSum,sizeof(nSum),0);
if (nRecv>0)
{
cout<<"RecvData:"<<nSum<<endl;
continue;
}
else if (nRecv==0)
{
cout<<"the remote is peaceful closed!"<<endl;
break;
}
else
{
cout<<"force Closed!"<<endl;
break;
}*/
Sleep(10);
}
//关闭连接
::closesocket(ClientSock);
//关闭网络库
WSACleanup();
system("pause");
return 0;
}
Common.h文件
#pragma once
//枚举运算类型
enum eCalcType
{
enum_calctype_add,
enum_calctype_sub,
enum_calctype_mul,
enum_calctype_div,
};
struct STRU_CALCTYPE_ADD//加法结构体
{
WORD m_wType;
int m_nFirstData;
int m_nSecondData;
};
struct STRU_CALCTYPE_SUB//减法结构体
{
WORD m_wType;
int m_nFirstData;
int m_nSecondData;
};
struct STRU_CALCTYPE_MUL//乘法结构体
{
WORD m_wType;//运算类型
int m_nFirstData;
int m_nSecondData;
};
struct STRU_CALCTYPE_DIV//除法结构体
{
WORD m_wType;
int m_nFirstData;
int m_nSecondData;
};
但是这样写存在几点问题:
1、结构体中存在虚函数影响运算类型字段的获取。如果在结构体里面有虚函数的指针,那么这个结构体的前两个字节就是指向虚函数地址的指针,因此在获取结构体中运算类型的时候将会出错。
2、结构体字节的对齐方式影响着结构体大小。计算结构体长度时,遵循:
Struct Node
{
char chNum;
int nNum;
short sNum;
};
此时,以结构中的占内存空间大的数据类型为单位,Node结构体的大小是12个字节,而如果采用一个字节对齐的方式,那么就是7个字节。
如果
Struct Node
{
char chNum;
short sNum;
int nNum;
};
前面的char和short填充一个int的内存,然后nNum这个int又扩展了另一个内存,Node结构体的大小是8个字节。在进行网络通信的时候,客户端和服务器两者的字节对齐方式可能是不一样的,那么就可能导致在客户端获取的结构体的大小与在服务端获取的结构体大小不一样。从而传输字节不一致,是程序员应该考虑到的事。
3、可以提取出基类数据包呢?好好观察下STRU_CALCTYPE_ADD、STRU_CALCTYPE_SUB、STRU_CALCTYPE_MUL、STRU_CALCTYPE_DIV这几个结构体,我们会发现 WORD m_wType;运算类型这个变量可以提取到基类数据包中来,并且m_wType变量应该是受保护的,尽量避免外界来修改它,增加程序的安全性。
这三个问题,大家想想怎么来解决?欢迎大家在下面留言,有什么不对的地方或者表述不清楚的地方,请大家指教。嘻嘻