TCP:多人聊天窗口(1)
【摘要】 简介
也算是一个小任务吧!基本要求,Linux下Tcp服务端,Windows,MFCTcp客户端。
环境:
Linux:Centos6.7
Windows;vs2008MFC
12
思路
客户端:登录界面,主界面,聊天窗口。 登录界面:输入用户ID,用户IP。客户端登录服务端成功,进入主界面。客户端登录失败,等待登录成功。 主界面:所有用户ID,组ID...
简介
也算是一个小任务吧!基本要求,Linux下Tcp服务端,Windows,MFCTcp客户端。
环境:
Linux:Centos6.7
Windows;vs2008MFC
- 1
- 2
思路
客户端:登录界面,主界面,聊天窗口。
登录界面:输入用户ID,用户IP。客户端登录服务端成功,进入主界面。客户端登录失败,等待登录成功。
主界面:所有用户ID,组ID,双击打开聊天窗口,单一ID只能打开一个窗口。
聊天窗口:显示聊天内容,聊天内容输出窗口
服务端:消息中转,控制群组,用户
代码
- 协议:
消息类型:登录消息,删除账号消息,个人消息(点对点聊天消息包),群组消息(群组聊天消息包),创建群组,删除群组
报文结构:报文头(消息类型,发送ID,收方ID,报文长度),消息内容
UDP心跳包,判断在线不在线。(后来屏蔽)
#pragma pack(1)
#define MSG_MAXLEN 260
#define MSGHEADER_LEN 8
enum MsgType{
Res =0xFF20,
Del,
AloneMsg, //个人消息
ClubMsg, //群组消息
CreatCmd, //创建群组
DelCmd, //删除群组
};
struct MsgHeader{
unsigned short usMsgId; //消息类型 0x0020: 0: 1:群组消息 2:创建群组 3:删除群组
unsigned short usSendID; //发方ID
unsigned short usRecvID; //收方ID
unsigned short usMsgLen; //消息长度
};
struct ResDel{ //注册/注销消息
MsgHeader m_MsgHeader;
unsigned short usID; //人员ID(按照注册顺序分配,区间段为10001-10002)
char strIp[16]; //ip信息
};
struct Msg_pack{ //聊天消息包
MsgHeader m_Header; //报文头
char strMsg[255]; //消息字段
};
/*UDP心跳包-1S1发*/
struct UDPMsg{
unsigned short usID; //人员ID
unsigned int iNo;//Msg编号
};
/*创建群组*/
struct CreatClub{
MsgHeader m_MsgHeader;
unsigned short usClubID; //群组ID
unsigned short usClubCreatID; //群组创建人ID
unsigned short usMerberSum; //群组创建人数
char * strMerberIDInfo; //群组人员ID信息
};
/*删除群组*/
struct DelClub {
MsgHeader m_MsgHeader;
unsigned short usClubID; unsigned short usClubCreatID;
};
/*所有在线用户状态包*/
struct UsrOnlineState{
MsgHeader m_MsgHeader;
unsigned short usSum;//用户个数
};
struct UsrOnline{
unsigned short usID;//用户ID
bool bState;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 服务端
这版的缺陷,时间关系,加前期Tcp实现问题,没时间继续优化,用户组,群组写死了。如果有时间下一版改进,都改为动态聊天。初步计划,读写本地配置文件,用来实现服务端对用户的管理。
/*ListInit*/
unsigned short str_Number[10] = {10001,10002,10003,10004,10005,10006,10007,10008,10009,10010};
unsigned short str_Club[3][10] = {
{10001,10002,10003},
{10001,10004,10005},
{10006,10002,10008,10009},
};
void InfoInit()
{
std::vector<unsigned short> club_1(str_Club[0],str_Club[0]+3);
std::vector<unsigned short> club_2(str_Club[1],str_Club[1]+3);
std::vector<unsigned short> club_3(str_Club[2],str_Club[2]+3);
map_club.insert(std::make_pair(20001,club_1));
map_club.insert(std::make_pair(20002,club_2));
map_club.insert(std::make_pair(20003,club_3));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
Tcp类:
包含,socket创建,端口绑定,监听端口设定,客户端连接函数,消息接收,消息发送。
class TcpNetwork{
public:
int CreatSocket();
bool BindSocket();
bool listenport();
int Clientconnect();
int TcpInit();
void RecvMsg();
void QuitTcp();
void SendMsg(Msg_pack m_Msg_pack,int sockID);
private:
struct timeval tv; struct sockaddr_in ServerTalk; static char TcpRecvBuf[TcpBufMax];
int sock_Server;
int Clent_sock;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
/*TcpNetWork Funtion*/
int TcpNetwork::CreatSocket()
{
sock_Server = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
ServerTalk.sin_family = AF_INET;
ServerTalk.sin_port = htons(TcpRecvPort);
ServerTalk.sin_addr.s_addr = htonl(INADDR_ANY);
return sock_Server;
}
bool TcpNetwork::BindSocket()
{
if(bind(sock_Server,(struct sockaddr*)&ServerTalk,sizeof(sockaddr))==-1)
{
return false;
}else{
printf("Tcp:绑定成功\n");
return true;
}
}
bool TcpNetwork::listenport(){
int iListen = listen(sock_Server,QUEUE);
if(iListen == -1){
printf("listen\n");
return false;
}else{
printf("Tcp:Listen Scucessfully\n");
return true;
}
}
int TcpNetwork::Clientconnect()
{
struct sockaddr_in ClientTalk;
char ClinentIp[INET_ADDRSTRLEN];
socklen_t length = sizeof(ClientTalk);
Clent_sock = accept(sock_Server,(struct sockaddr*)&ClientTalk,&length);
printf("Clent_sock:%d\n",Clent_sock);
if(Clent_sock<0) {
printf(" Tcp:Connect fail\n");
return -1;
}else {
printf("Tcp:Connect OK!\n"); return Clent_sock;
}
}
int TcpNetwork::TcpInit()
{
int sock_tcp =CreatSocket();
if(BindSocket()==false) {
printf("Tcp:端口绑定失败\n");
}
else{
if(listenport() == false) { printf("Tcp:监听口设置失败\n");;
}
}
return sock_tcp;
}
char TcpNetwork::TcpRecvBuf[TcpBufMax] = {0};
void TcpNetwork::RecvMsg()
{ char Recvbuf[280] = {0};
int ret = recv(Clent_sock,Recvbuf,280,0);
if(strlen(Recvbuf)>0){
printf("Recv:Clent_sock:%d\n",Clent_sock);
MsgProess(Recvbuf,Clent_sock); }else {
}
}
void TcpNetwork::QuitTcp()
{
close(Clent_sock);
close(sock_Server);
}
void TcpNetwork::SendMsg(Msg_pack m_Msg_pack,int sockID)
{
char Buf_Msg[280] = {0};
memcpy(Buf_Msg,&m_Msg_pack,sizeof(Msg_pack));
send(sockID,Buf_Msg,sizeof(Msg_pack),0);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
UDP类
包含:UDPSocket的创建,绑定,收发,还有一个心跳包的打印函数,用来测试。
class UdpNetwork{
public:
void CreatSocket();
bool BindSocket();
void UdpInit();
void UdpRecv();
void PrintfClientInfo();
UDPMsg m_UDPMsg; private:
int sockfd;
sockaddr_in ServerSocket;
sockaddr_in ClientSocket;
static char UdpRecvBuf[UdpBufMax];
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
/*UdpSocket Funtion*/
void UdpNetwork::CreatSocket(){
bzero(&ServerSocket,sizeof(ServerSocket));
ServerSocket.sin_family = AF_INET;
ServerSocket.sin_port = htons(UdpRecvPort);
ServerSocket.sin_addr.s_addr = htonl(INADDR_ANY); sockfd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sockfd == -1){
printf("Udp:Creat Socket Fail!\n");
}else printf("Udp:Creat Udp Socket OK!\n");
}
bool UdpNetwork::BindSocket(){
int state =bind(sockfd,(struct sockaddr *)&ServerSocket,sizeof(ServerSocket)) ;
if(state == -1){
printf("Udp Bind fail!\n");
return false; }else {
printf("Udp Bind OK!\n"); return true;
}
}
void UdpNetwork::UdpInit(){
CreatSocket();
BindSocket();
}
char UdpNetwork::UdpRecvBuf[UdpBufMax] = {0};
void UdpNetwork::UdpRecv(){
printf("Udp -----\n");
socklen_t ClientLen = sizeof(struct sockaddr_in);
int RecvLen = recvfrom(sockfd,UdpRecvBuf,UdpBufMax,0,(sockaddr*)&ClientSocket,&ClientLen);
if(RecvLen<0){
printf("Udp:Recv Fail!\n");
}else printf("Udp:Recv OK!\n");
memcpy(&m_UDPMsg,&UdpRecvBuf,sizeof(UDPMsg));
printf("Udp:RecvMsg Len is:%d,usID is:%d,iNo:%d\n",RecvLen,m_UDPMsg.usID,m_UDPMsg.iNo);
PrintfClientInfo(); }
void UdpNetwork::PrintfClientInfo(){
char ClinentIp[INET_ADDRSTRLEN];
inet_ntop(AF_INET,&ClientSocket.sin_addr,ClinentIp,sizeof(ClinentIp));
printf("Udp:Client IP is :%s,port is:%d\n",ClinentIp,ntohs(ClientSocket.sin_port));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
多线程通信部分:
/*Thread Funtion*/
int TcpInit()
{
int sock_Server = m_Tcp.TcpInit();
return sock_Server;
}
void *TcpThread(void *ptr)
{
int clientSocket = *((int *)ptr);
time_t Befortime; //时间相关部分可以注释掉,用来统计时间用,有一些错误,对主题消息流程没有影响
time(&Befortime);
time_t Nowtime;
double TimeErr = 0;
std::map<unsigned short,int>::iterator it = threadBind.begin();;
while(1)
{
time(&Nowtime);
char Recvbuf[280] = {0};
int ret = recv(clientSocket,Recvbuf,280,0);
if(ret<0)
{ printf("接收失败"); for(it;it != threadBind.end();it++) { if(it->second == clientSocket) { threadBind.erase(it); } }
} if(strlen(Recvbuf)>0)
{ MsgProess(Recvbuf,clientSocket); }
TimeErr=difftime(Nowtime,Befortime);
usleep(20000);//线程休眠20ms
}
}
void UdpInit()
{
m_Udp.UdpInit();
}
void *UdpThread(void *ptr)
{
while(1)
m_Udp.UdpRecv();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
报文解析部分:
做了两个容器用来控制用户,和Socket管理
std::map<unsigned short,int>threadBind;
std::map<unsigned short,std::vector<unsigned short> >map_club;
int GetSockID(unsigned short usID);
std::vector<unsigned short> GetClubVector(unsigned short usClubID);
- 1
- 2
- 3
- 4
- 5
std::vector<unsigned short> GetClubVector(unsigned short usClubID)
{
return map_club[usClubID];
}
int GetSockID(unsigned short usID)
{
std::map<unsigned short,int>::iterator it = threadBind.find(usID);
if(it != threadBind.end()){
return it->second;
}else{
return 0;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
报文处理
void MsgProess(char * RecvMsg,int sockID);//初步分析,报文头的解析
void MsgPro(unsigned short msgId,char * Msg,int Msg_len,int sockID);//详细解析,并做相应的处理
- 1
- 2
TcpNetwork m_Tcp;
UdpNetwork m_Udp;
void MsgProess(char * RecvMsg,int sockID)
{
MsgHeader m_Header;
memcpy(&m_Header,RecvMsg,sizeof(MsgHeader));
printf("m_Header:%d,%d,%d,%d\n",m_Header.usMsgId,m_Header.usSendID,m_Header.usRecvID,m_Header.usMsgLen);
MsgPro(m_Header.usMsgId,RecvMsg,m_Header.usMsgLen,sockID);
}
void MsgPro(unsigned short msgId,char * Msg,int Msg_len,int sockID)
{
Msg_pack m_Msg_pack ={0};
ResDel m_ResDel = {0};
std::pair<unsigned short,int> it;
std::vector<unsigned short> vec_clubNumber;
char Buf_Msg[280] = {0};
switch(msgId)
{
case Res: memcpy(&m_ResDel,Msg,sizeof(ResDel)); it = std::make_pair(m_ResDel.m_MsgHeader.usSendID,sockID); printf("Res:%d,%d\n",it.first,it.second); threadBind.insert(it); break;
case Del: memcpy(&m_ResDel,Msg,sizeof(ResDel)); break;
case AloneMsg: memcpy(&m_Msg_pack,Msg,sizeof(Msg_pack)); if(m_Msg_pack.m_Header.usSendID == m_Msg_pack.m_Header.usRecvID) break; if(GetSockID(m_Msg_pack.m_Header.usRecvID) != 0) { printf("AloneMsg SocketID::%d,%d\n",sockID,GetSockID(m_Msg_pack.m_Header.usRecvID)); int i = send(GetSockID(m_Msg_pack.m_Header.usRecvID),Msg,sizeof(Msg_pack),0); if(i<0) { printf("Err: TcpSendMsg Fail!\n"); } } break;
case ClubMsg: memcpy(&m_Msg_pack,Msg,sizeof(Msg_pack)); vec_clubNumber = GetClubVector(m_Msg_pack.m_Header.usRecvID); for(int i= 0;i<vec_clubNumber.size();i++){ printf("Club::%d,%d,%d\n",vec_clubNumber[i],GetSockID(vec_clubNumber[i]),m_Msg_pack.m_Header.usSendID); if(m_Msg_pack.m_Header.usSendID == vec_clubNumber[i]) continue; if(GetSockID(vec_clubNumber[i]) != 0){ int b= send(GetSockID(vec_clubNumber[i]),Msg,sizeof(Msg_pack),0); if(b<0) { printf("Err: Tcp ClubMsg SendFail!\n"); } } }
break;
case CreatCmd: break;
case DelCmd: break;
default: break; }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
入口函数
/*Main Funtion*/
int main(){
InfoInit();
pthread_t Udp_id;
UdpInit();
int sock_Server = TcpInit();//Tcp初始化
int i = 0;
while(1){
int ibuf =m_Tcp.Clientconnect();//客户端连接判断
if(ibuf == -1)
{ printf("Tcp:ibuf %d\n",ibuf); continue;
}else{ printf("This is %d thread\n",i); pthread_t Tcp_id; int ret_Tcp = pthread_create(&Tcp_id,NULL,TcpThread,&ibuf); pthread_detach(Tcp_id);//线程分离 i+=1;
}
usleep(10000);
} /*int ret_Udp = pthread_create(&Udp_id,NULL,UdpThread,NULL);
if(ret_Udp != 0){
printf("ret_Udp Create Fail;\n");
}*/
close(sock_Server);
//pthread_join(Udp_id,NULL);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 客户端
下一篇介绍,有点多,今天写不动了。 - 源码
源码
文章来源: blog.csdn.net,作者:何其不顾四月天,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/u011218356/article/details/91672934
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)