0
点赞
收藏
分享

微信扫一扫

基于Windows Socket的安全通信(C++实现,附源码)


先了解一下Socket的相关函数原型

 



​//加载套接字库​



​int​​  ​​PASCAL FAR WSAStartup(​​ ​​WORD​​  ​​wVersionRequired, LPWSADATA lpWSAData);​



​//释放套接字库资源​



​int​​  ​​PASCAL FAR WSACleanup(​​ ​​void​​ ​​);​



​//创建套接字​



​SOCKET PASCAL FAR socket (​​ ​​int​​  ​​af,​​ ​​int​​  ​​type,​​ ​​int​​  ​​protocol);​



​//关闭套接字​



​int​​  ​​PASCAL FAR closesocket (SOCKET s);​



​//绑定一个IP地址和端口​



​int​​  ​​PASCAL FAR bind (SOCKET s, ​​ ​​const​​  ​​struct​​  ​​sockaddr FAR *addr, ​​ ​​int​​  ​​namelen);​



​//将套接字置为监听状态​



​int​​  ​​PASCAL FAR listen (SOCKET s, ​​ ​​int​​  ​​backlog);​



​//接受客户端连接请求,并返回新创建的套接字​



​SOCKET PASCAL FAR accept (SOCKET s, ​​ ​​struct​​  ​​sockaddr FAR *addr, ​​ ​​int​​  ​​FAR *addrlen);​



​//尝试将本地套接字连接至服务器​



​int​​  ​​PASCAL FAR connect (SOCKET s, ​​ ​​const​​  ​​struct​​  ​​sockaddr FAR *name, ​​ ​​int​​  ​​namelen);​



​//发送数据​



​int​​  ​​PASCAL FAR send (SOCKET s, ​​ ​​const​​  ​​char​​  ​​FAR * buf, ​​ ​​int​​  ​​len,​​ ​​int​​  ​​flags);​



​//接收数据​



​int​​  ​​PASCAL FAR recv (SOCKET s, ​​ ​​char​​  ​​FAR * buf, ​​ ​​int​​  ​​len,​​ ​​int​​  ​​flags);​



 

使用Socket的程序在使用Socket之前必须调用WSAStartup函数来绑定Socket库

在Constructor中添加如下代码

 



​int​​  ​​error;​



​WORD​​  ​​wVersionRequested;​



​WSADATA wsaData;​



​wVersionRequested = MAKEWORD(2, 1);​​ ​​//加载2.1版本的Socket库​



​if​​ ​​(error = WSAStartup(wVersionRequested, &wsaData))​



​{​



​AfxMessageBox(​​ ​​"Link Socket Library Failed!"​​ ​​);​



​exit​​ ​​(0);​



​}​



应用程序完成对Socket的使用后应当调用WSACleanup函数来释放Socket库占用的系统资源

 

在析构函数冲添加如下代码

 



​WSACleanup();​



 

 

Socket通信流程

实现安全通信,应采用面向连接的TCP/IP协议来保证连接的可靠性

面向连接的套接字的系统调用时序图


基于Windows Socket的安全通信(C++实现,附源码)_套接字

 

添加成员变量及初始化

 



​//服务器端:​



​  SOCKET Listener,toClient;​​ ​​//用于监听的套接字和连接至客户端的套接字(只是为了实现通信模型,所以不考虑多客户端)​



​bool​​  ​​listening, connected;​​ ​​//指示监听和连接的状态​



​  AES aes;​​ ​​//加密/解密模块​



 



​CTestSocketServerDlg::CTestSocketServerDlg(CWnd* pParent):​



​CDialog(CTestSocketServerDlg::IDD, pParent), ​



​aes((unsigned​​ ​​char​​  ​​*)​​ ​​"0123456789abcdef"​​ ​​),​



​listening(​​ ​​false​​ ​​),​



​connected(​​ ​​false​​ ​​)​



​{​



​//Constructor of Server​



​}​



 



​//客户端:​



​  SOCKET toServer;​​ ​​//连接至服务器端的套接字​



​bool​​  ​​connected;​​ ​​//指示连接状态​



​  AES aes;​​ ​​//加密/解密模块​



 



​CTestSocketClientDlg::CTestSocketClientDlg(CWnd* pParent):​



​CDialog(CTestSocketClientDlg::IDD, pParent), ​



​aes((unsigned​​ ​​char​​  ​​*)​​ ​​"0123456789abcdef"​​ ​​),​



​connected(​​ ​​false​​ ​​)​



​{​



​//Constructor of Client​



​}​



 

为“Start/Stop”按钮注册单击事件处理服务器端初始化及关闭操作

 



​void​​  ​​CTestSocketServerDlg::OnBtnStart()​



​{​



​if​​ ​​(connected || listening)​​ ​​//若正在监听或已连接则关闭服务器​



​{​



​connected = ​​ ​​false​​ ​​;​



​listening = ​​ ​​false​​ ​​;​



​closesocket(toClient);​



​closesocket(Listener);​



​m_chat += ​​ ​​"Socket Server Stopped!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



 



​UpdateData(​​ ​​true​​ ​​);​



​//创建监听Socket​



​struct​​  ​​protoent *ppe;​



​ppe = getprotobyname(​​ ​​"tcp"​​ ​​);​



​if​​ ​​((Listener = socket(PF_INET, SOCK_STREAM, ppe->p_proto)) == INVALID_SOCKET)​



​{​



​m_chat += ​​ ​​"Initialize Socket Listener Failed!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



 



​//绑定IP及端口​



​struct​​  ​​sockaddr_in saddr;​



​saddr.sin_family = AF_INET;​



​saddr.sin_port = htons(m_port);​



​saddr.sin_addr.s_addr = htonl(INADDR_ANY);​



​if​​ ​​(bind(Listener, (​​ ​​struct​​  ​​sockaddr *)&saddr, ​​ ​​sizeof​​ ​​(saddr)))​



​{​



​m_chat += ​​ ​​"Bind to IPEndPoint Failed! (Port in use?)\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



​//开始监听,队列长度1(不考虑多客户端)​



​if​​ ​​(listen(Listener, 1))​



​{​



​m_chat += ​​ ​​"Listen Failed!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



​m_chat += ​​ ​​"Socket Server Started!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



 



​listening = ​​ ​​true​​ ​​;​



​AfxBeginThread(Wait4Client,​​ ​​this​​ ​​);​​ ​​//另起线程等待客户端连接​



​}​



 

接收来自客户端的连接请求

 



​UINT​​  ​​Wait4Client(​​ ​​LPVOID​​  ​​pParam)​



​{​



​CTestSocketServerDlg * c = (CTestSocketServerDlg *) pParam;​



​struct​​  ​​sockaddr_in caddr;​



​int​​  ​​caddrlen = ​​ ​​sizeof​​ ​​(caddr);​



​c->toClient = accept(c->Listener, (​​ ​​struct​​  ​​sockaddr *)&caddr, &caddrlen);​



 



​if​​ ​​(c->toClient == INVALID_SOCKET)​​ ​​//异常处理​



​{​



​if​​ ​​(!c->listening)​​ ​​return​​  ​​0;​​ ​​//服务器端主动关闭,则直接退出​



​c->m_chat += ​​ ​​"Connect Failed!\r\n"​​ ​​;​



​c->UpdateData(​​ ​​false​​ ​​);​



​return​​  ​​-1;​



​}​



​else​



​{​



​c->connected = ​​ ​​true​​ ​​;​​ ​​//连接建立,另起线程用于接收信息​



​AfxBeginThread(ReceiveMessage, c);​



​c->m_chat += ​​ ​​"Client: "​​ ​​;​



​c->m_chat += inet_ntoa(caddr.sin_addr);​



​c->m_chat += ​​ ​​" Connected!\r\n"​​ ​​;​



​c->m_ip = inet_ntoa(caddr.sin_addr);​



​c->UpdateData(​​ ​​false​​ ​​);​



​}​



​return​​  ​​0;​



​}​



 

客户端只需要创建Socket并尝试与服务器连接

为“Connect/Disconnect”按钮注册单击事件

 



​void​​  ​​CTestSocketClientDlg::OnBtnConnect()​



​{​



​if​​ ​​(connected)​​ ​​//如果已连接,则断开​



​{​



​connected = ​​ ​​false​​ ​​;​



​closesocket(toServer);​



​m_chat += ​​ ​​"Disconnect to Server!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



 



​UpdateData(​​ ​​true​​ ​​);​



​//创建Socket​



​struct​​  ​​protoent *ppe;​



​ppe = getprotobyname(​​ ​​"tcp"​​ ​​);​



​if​​ ​​((toServer = socket(PF_INET, SOCK_STREAM, ppe->p_proto)) == INVALID_SOCKET)​



​{​



​m_chat += ​​ ​​"Initialize Socket Listener Failed!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



​//尝试连接服务器​



​struct​​  ​​sockaddr_in saddr;​



​saddr.sin_family = AF_INET;​



​saddr.sin_port = htons(m_port);​



​saddr.sin_addr.s_addr = inet_addr(m_ip);​



​if​​ ​​(connect(toServer, (​​ ​​struct​​  ​​sockaddr *)&saddr, ​​ ​​sizeof​​ ​​(saddr)))​



​{​



​m_chat += ​​ ​​"Connect Failed!\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



​m_chat += ​​ ​​"Server: "​​ ​​;​



​m_chat += inet_ntoa(saddr.sin_addr);​



​m_chat += ​​ ​​" Connected!\r\n"​​ ​​;​



​connected = ​​ ​​true​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​AfxBeginThread(ReceiveMessage,​​ ​​this​​ ​​);​​ ​​//连接建立,另起线程用于接收信息​



​}​



 

用于循环接收信息的线程

 



​UINT​​  ​​ReceiveMessage(​​ ​​LPVOID​​  ​​pParam)​



​{​



​CTestSocketServerDlg * c = (CTestSocketServerDlg *) pParam;​



​char​​  ​​buffer[1024];​



​int​​  ​​error;​​ ​​//记录recv函数返回值,即接收的字节数,也作异常代码​



​while​​ ​​(error = recv(c->toClient, buffer, 1024, 0))​



​{​



​if​​ ​​(error == 0 || error == SOCKET_ERROR)​​ ​​break​​ ​​;​



​c->PrintData(​​ ​​"Received Data"​​ ​​, (unsigned ​​ ​​char​​ ​​*)buffer, error);​



​c->aes.InvCipher((​​ ​​void​​  ​​*)buffer, error);​​ ​​//解密,恢复明文​



​c->PrintData(​​ ​​"Unencrypted Data"​​ ​​, (unsigned ​​ ​​char​​ ​​*)buffer, error);​



​c->m_chat += ​​ ​​"Client:"​​ ​​;​



​c->m_chat += buffer;​



​c->m_chat += ​​ ​​"\r\n"​​ ​​;​



​c->UpdateData(​​ ​​false​​ ​​);​



​}​



​c->m_ip = ​​ ​​"Not Connected..."​​ ​​;​



​c->UpdateData(​​ ​​false​​ ​​);​



​if​​ ​​(!c->connected)​​ ​​return​​  ​​0;​​ ​​//服务器端主动关闭,直接返回​



​closesocket(c->toClient);​



​c->connected = ​​ ​​false​​ ​​;​



​c->m_chat += ​​ ​​"Client Disconnected...\r\n"​​ ​​;​



​c->UpdateData(​​ ​​false​​ ​​);​



​AfxBeginThread(Wait4Client, c);​



​return​​  ​​0;​



​}​



 

为“Send”按钮注册单击事件,处理数据的加密发送

 



​void​​  ​​CTestSocketServerDlg::OnBtnSend()​



​{​



​if​​ ​​(!connected)​​ ​​return​​ ​​;​



​UpdateData(​​ ​​true​​ ​​);​



​if​​ ​​(m_message == ​​ ​​""​​ ​​)​​ ​​return​​ ​​;​



 



​int​​  ​​len = m_message.GetLength()+1 >= 1024 ? 1024 : m_message.GetLength()+1;​



​len = len%16 ? len+16-len%16 : len;​



​char​​  ​​buffer[1024];​



​strcpy​​ ​​(buffer,m_message.GetBuffer(0));​​ ​​//将message拷贝至buffer数组中​



​m_message.ReleaseBuffer();​



​PrintData(​​ ​​"Input Data"​​ ​​, (unsigned ​​ ​​char​​ ​​*)buffer, len);​



​aes.Cipher((​​ ​​void​​  ​​*)buffer);​​ ​​//对数据进行加密​



​if​​ ​​(send(toClient, buffer, len, 0) == SOCKET_ERROR)​​ ​​//发送密文​



​{​



​m_chat += ​​ ​​"Send Failed!(Socket Exception?)\r\n"​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​return​​ ​​;​



​}​



​PrintData(​​ ​​"Encrypted Data"​​ ​​, (unsigned ​​ ​​char​​ ​​*)buffer, len);​



​m_chat += ​​ ​​"Server:"​​  ​​+ m_message + ​​ ​​"\r\n"​​ ​​;​



​m_message =​​ ​​""​​ ​​;​



​UpdateData(​​ ​​false​​ ​​);​



​}​



 

发送和接收的时候都用到了一个函数PrintData,用于将明文或密文以16进制输出以便作演示

 



​void​​  ​​CTestSocketServerDlg::PrintData(​​ ​​char​​ ​​* title, unsigned ​​ ​​char​​ ​​* buffer, ​​ ​​int​​  ​​length)​



​{​



​int​​  ​​i;​



​CString temp(​​ ​​""​​ ​​);​



​m_chat += ​​ ​​"("​​ ​​;​



​m_chat += title;​



​m_chat += ​​ ​​":"​​ ​​;​



​for​​ ​​(i=0; i<length; i++)​



​{​



​temp.Format(​​ ​​"%s%X "​​ ​​,*(buffer+i)>15?​​ ​​""​​ ​​:​​ ​​"0"​​ ​​,*(buffer+i));​



​m_chat += temp;​



​}​



​m_chat += ​​ ​​")\r\n"​​ ​​;​



​}​



 

举报

相关推荐

0 条评论