首先我们在之前TCP的基础上 进行了优化 解决了粘包问题
这里的解决方式是:在包头给出包的大小
接收端接收到包时 先解析出包的大小 再根据大小分配空间
解决粘包代码
SOCKET sockWaiter= my_map[GetCurrentThreadId()];
// 服务员等客人说话 --recv();
char *pszbuf=NULL;
//当前位置标识
int offset=0;
//包长度
int nPackSize;
int nRecvNum;
while(my_FlagQuit)
{
//jieshoubaodaxiao
nRecvNum=recv(sockWaiter,(char*)&nPackSize,sizeof(int ),0);
if(nRecvNum<=0)
{
//判断服务器是否下线
if(WSAGetLastError()==10054)
{
//删除映射
auto ite=my_map.begin();
while(ite!=my_map.end())
{
if(ite->second==sockWaiter)
{
my_map.erase(ite);
closesocket(sockWaiter);
break;
}
//ite++;
}
break;
}
continue;
}
//获得接受内容
pszbuf=new char[nPackSize];
while(nPackSize)//看当前剩余的还有多少没接受完
{
nRecvNum=recv(sockWaiter,pszbuf+offset,nPackSize,0);
if(nRecvNum>0)
{
nPackSize-=nRecvNum;
offset+=nRecvNum;
}
}
cout<<"client say:"<<pszbuf<<endl;
delete []pszbuf;
pszbuf=NULL;
}
完整代码如下:
#ifndef CTCPMAP_H
#define CTCPMAP_H
#include<windows.h>
#include<list>
#include<winsock2.h>
#include<iostream>
#include<map>
using namespace std;
class CTCPmap
{
public:
//构造
CTCPmap();
//析构
~CTCPmap();
//创初始化网络
bool InitnewWork();
//销毁网络
void DeleteWork();
//线程函数
static DWORD WINAPI ThreadAccept(LPVOID lpvoid);
static DWORD WINAPI ThreadRecv(LPVOID lpvoid);
//发送数据
bool sendDate(SOCKET sockWaiter,char*szbuf,int nLen);
//接收数据
void recvDate();
private:
SOCKET m_soclisten;
std::list<HANDLE> my_Threadpool;
bool my_FlagQuit;
std::map<DWORD,SOCKET> my_map;
};
#include "ctcpmap.h"
CTCPmap::CTCPmap()
{
m_soclisten=0;
my_FlagQuit=true;
}
CTCPmap::~CTCPmap()
{
}
bool CTCPmap::InitnewWork()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
DeleteWork();
return false;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
return false;
}
//创建套接字
m_soclisten=socket(AF_INET,SOCK_STREAM,0);
if(m_soclisten==INVALID_SOCKET)
{
DeleteWork();
return false;
}
//绑定
sockaddr_in addrserver;
//绑定IP
addrserver.sin_addr.S_un.S_addr=0;
addrserver.sin_port=htons(8899);
addrserver.sin_family=AF_INET;
if(SOCKET_ERROR==bind(m_soclisten,(sockaddr*)&addrserver,sizeof(addrserver)))
{
DeleteWork();
return false;
}
//监听
if(SOCKET_ERROR==listen(m_soclisten,10))
{
DeleteWork();
return false;
}
//创建线程
HANDLE hand=CreateThread(0,0,&ThreadAccept,this,0,0);
if(hand)
my_Threadpool.push_back(hand);
return true;
}
void CTCPmap::DeleteWork()
{
//线程销毁
my_FlagQuit=false;
auto ite=my_Threadpool.begin();
while(ite!=my_Threadpool.end())
{
if(WAIT_TIMEOUT==WaitForSingleObject(*ite,100))
TerminateThread(*ite,-1);
CloseHandle(*ite);
*ite=NULL;
ite++;
}
my_Threadpool.clear();
//关闭句柄
if(m_soclisten)
{
closesocket(m_soclisten);
m_soclisten=0;
}
//卸载服务器
WSACleanup();
}
//接收客户端的线程函数
DWORD WINAPI CTCPmap::ThreadAccept(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
sockaddr_in addrclient;
int nsize=sizeof(addrclient);
DWORD ThreadId;
while(pthis->my_FlagQuit)
{
//店长接受客人 分配给服务员 --接受链接--accept();
//服务者与他的客人对应起来
SOCKET sockWaiter=accept(pthis->m_soclisten,(sockaddr*)&addrclient,&nsize);
cout<<"client ip:"<<inet_ntoa(addrclient.sin_addr)<<"accept"<<endl;
//对应失败 跳出 继续循环
if(sockWaiter==INVALID_SOCKET)continue;
//对应成功 线程池添加了一个服务者线程
HANDLE hand=CreateThread(0,0,&ThreadRecv,pthis,0,&ThreadId);
//这里ThreadId来当做编号 映射当前对应的服务者 将当前服务者计入到map[ThreadId]中 以后找的时候找ThreadId就能找到对应的服务者
if(hand)
pthis->my_map[ThreadId]=sockWaiter;
pthis->my_Threadpool.push_back(hand);
}
return 0;
}
DWORD WINAPI CTCPmap::ThreadRecv(LPVOID lpvoid)
{
CTCPmap *pthis=(CTCPmap*)lpvoid;
while(pthis->my_FlagQuit)
{
// 接受
pthis->recvDate();
}
return 0;
}
void CTCPmap::recvDate()//接收函数
{
SOCKET sockWaiter= my_map[GetCurrentThreadId()];
// 服务员等客人说话 --recv();
char *pszbuf=NULL;
//当前位置标识
int offset=0;
//包长度
int nPackSize;
int nRecvNum;
while(my_FlagQuit)
{
//jieshoubaodaxiao
nRecvNum=recv(sockWaiter,(char*)&nPackSize,sizeof(int ),0);
if(nRecvNum<=0)
{
//判断服务器是否下线
if(WSAGetLastError()==10054)
{
//删除映射
auto ite=my_map.begin();
while(ite!=my_map.end())
{
if(ite->second==sockWaiter)
{
my_map.erase(ite);
closesocket(sockWaiter);
break;
}
//ite++;
}
break;
}
continue;
}
//获得接受内容
pszbuf=new char[nPackSize];
while(nPackSize)//看当前剩余的还有多少没接受完
{
nRecvNum=recv(sockWaiter,pszbuf+offset,nPackSize,0);
if(nRecvNum>0)
{
nPackSize-=nRecvNum;
offset+=nRecvNum;
}
}
cout<<"client say:"<<pszbuf<<endl;
delete []pszbuf;
pszbuf=NULL;
}
}
bool CTCPmap:: sendDate(SOCKET sockWaiter,char*szbuf,int nLen)
{
if(sockWaiter==INVALID_SOCKET||!szbuf<=0||nLen<=0)
return false;
if(send(sockWaiter,(char*)&nLen,sizeof(int ),0)<=0)
return false;
if(send(sockWaiter,szbuf,nLen,0)<=0)
return false;
int nRecvNum;
int nRecvNuumSIze=sizeof(szbuf);
while(my_FlagQuit)
{
cin>>szbuf;
send(sockWaiter,(char*)&nRecvNuumSIze,sizeof(int),0);
send(sockWaiter,szbuf,sizeof(szbuf),0);
//recv()
nRecvNum= recv(sockWaiter,szbuf,sizeof(szbuf),0);
if(sockWaiter>0)
{
cout<<"fuwuqi say:"<<szbuf<<endl;
}
}
return true;
}
#include <QCoreApplication>
#include<winsock2.h>
#include<iostream>
#include"ctcpmap.h"
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
/*
*1.选择种类 --WSAStartup();
*2.雇店长 --创建套接字 socket();
*3.找地 --绑定 --bind();
*4.店长宣传 --监听 --listen();
*5.店长接受客人 分配给服务员 --接受链接--accept();
*6.客人与服务员 服务员等客人说话 --recv();
*7.回复 --send();
*8.下班--closesocket();
*9.关门--WASCleanup();
*
*/
CTCPmap ctcp;
if(ctcp.InitnewWork())
cout<<"initnework sucess"<<endl;
//回复 --send();
//下班--closesocket();
//关门--WSACleanup();
return a.exec();
}
客户端:
#include <QCoreApplication>
#include<winsock2.h>
#include<iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//加载库
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
WSACleanup();
return 1;
}
else
printf("The Winsock 2.2 dll was found okay\n");
//创建套接字
SOCKET sockclient=socket(AF_INET,SOCK_STREAM,0);
if(sockclient==INVALID_SOCKET)
{
WSACleanup();
return 1;
}
//connect()
//绑定IP
sockaddr_in addrserver;
addrserver.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrserver.sin_port=htons(8899);
addrserver.sin_family=AF_INET;
if(SOCKET_ERROR==connect(sockclient,(sockaddr*)&addrserver,sizeof(addrserver)))
{
closesocket(sockclient);
WSACleanup();
return 1;
}
//send()
int nRecvNum;
char szbuf[1024];
//解决粘包问题 在包头加包长度
int nRecvNuumSIze=sizeof(szbuf);
while(1)
{
cin>>szbuf;
send(sockclient,(char*)&nRecvNuumSIze,sizeof(int),0);
send(sockclient,szbuf,sizeof(szbuf),0);
//recv()
nRecvNum= recv(sockclient,szbuf,sizeof(szbuf),0);
if(nRecvNum>0)
{
cout<<"fuwuqi say:"<<szbuf<<endl;
}
}
//closesocket()
closesocket(sockclient);
//WSACleanup()
WSACleanup();
return a.exec();
}