1.简介
C++代码,TCP协议实现局域网聊天系统(长链接),有日志的打印,实现登录注册以及单发信息和群发信息功能,工作业余时间所写,附上github地址里面有Readme说明文件
说明:登录注册时客户端将用户输入的信息发送给服务器让服务器进行处理,客户端通信时为客户端先将信息发送给服务器,然后让服务器转发
由于代码文件过多代码过长,下面仅展示服务器和客户端代码,整个工程代码请在以上Github地址自取
2.服务器Select部分相关代码
bool Server::SelectClient()
{
int result = 0;
//先清空集合,再将套接字加入集合
FD_ZERO(&mSet);
FD_SET(mSocketfd, &mSet);
mMaxfd = mSocketfd;
while(!mIsStop)
{
mReadset = mSet;
LOG(TAG, "select ...");
result = select(mMaxfd + 1, &mReadset, NULL, NULL, NULL);
if(result < 0)
{
LOGE(TAG, "select failed ...");
return false;
}
//如果该套接字在此集合内则进行accept操作,并创建客户端链表
if(FD_ISSET(mSocketfd, &mReadset))
{
mAddrlen = sizeof(mCaddr);
LOG(TAG, "accept ...");
mAcceptfd = accept(mSocketfd, (sockaddr *)&mSaddr, (socklen_t *)&mAddrlen);
if(mAcceptfd < 0)
{
LOGE(TAG, "accept failed ...");
return false;
}
LOG(TAG, "client ip = ", inet_ntoa(mSaddr.sin_addr));
LOG(TAG, "client port = ", ntohs(mSaddr.sin_port));
//将accept返回的描述符加入集合
FD_SET(mAcceptfd, &mSet);
if(mMaxfd < mAcceptfd)
{
mMaxfd = mAcceptfd;
}
//以头插法创建客户端链表
mNode = (ClientNode *)malloc(sizeof(ClientNode));
if(!mNode)
{
LOGE(TAG, "malloc failed ...");
return false;
}
mNode->fd = mAcceptfd;
mNode->caddr = mCaddr;
mNode->next = mHead;
mHead = mNode;
}
//遍历客户端链表
for(mNode = mHead ; mNode != NULL; mNode = mNode->next)
{
mTempfd = mNode->fd;
if(!FD_ISSET(mTempfd, &mReadset))
{
continue;
}
//开始读取客户端信息,拆包判断
Package pack;
result = read(mTempfd, &pack, sizeof(Package));
if(result < 0)//此时移除下线节点
{
LOGE(TAG, "read error or TCP broken ...");
RemoveUnlineUserNode(mTempfd);
close(mTempfd);
FD_CLR(mTempfd, &mSet);
break;
}
else if(result == 0)//此时移除下线节点
{
LOGE(TAG, "TCP broken ...");
RemoveUnlineUserNode(mTempfd);
close(mTempfd);
FD_CLR(mTempfd, &mSet);
break;
}
int online = 0;
bool IsBreak = false;
switch(pack.type)
{
case REGISTER://当包头表示注册信息时
result = read(mTempfd, &mRegistinfo, sizeof(RegisteInfo));
if(result < 0)
{
LOGE(TAG, "read error ...");
return false;
}
LOG(TAG, "read your name = ", mRegistinfo.name);
LOG(TAG, "read telphone = ", mRegistinfo.telphone);
LOG(TAG, "read password = ", mRegistinfo.password);
LOG(TAG, "and your id is : ", mTempfd);
mRegistinfo.id = mTempfd;
if(mStorage)
{
bool ret = mStorage->WriteToMysql(mRegistinfo);
if( !ret )
{
WriteData(RFAILED);
}
else
{
WriteData(RSUCCESSED);
}
}
else
{
LOGE(TAG, "don't save to mysql because of connect mysql error ...");
}
break;
case LOGIN://当包头表示登录信息时
result = read(mTempfd, &mLogininfo, sizeof(LoginInfo));
if(result < 0)
{
LOGE(TAG, "read error ...");
return false;
}
LOG(TAG, "Login username = ", mLogininfo.name);
LOG(TAG, "Login password = ", mLogininfo.password);
if(mStorage)
{
bool ret = mStorage->ReadFromMysql(mLogininfo.name, mLogininfo.password);
if(ret)//登录成功
{
WriteData(LSUCCESSED);
//登录成功则将该用户加入链表
CreateUserNode(mTempfd, mLogininfo.name);
}
else//登录失败
{
WriteData(LFAILED);
}
}
else
{
LOGE(TAG, "don't read mysql because of connect mysql error ...");
}
break;
case GETONLINEUSER://当包头表示获取在线用户链表时
result = read(mTempfd, &online, sizeof(int));
if(result < 0)
{
LOGE(TAG, "read get online user commond error don't send online usernode...");
return false;
}
else
{
LOG(TAG, "read commond = ", online);
//发送在线用户链表
WriteData();
}
break;
case MESSAGE://先接收信息在转发
DealMessage();
break;
case ZERO:
IsBreak = true;
break;
}
if(IsBreak)
{
break;
}
}
}
return true;
}
2.客户端相关代码
bool Client::Init()
{
int result = 0;
mIsStop = false;
signal(SIGPIPE, RecvSignal);
SetIPandPort();
LOG(TAG, "socket ...");
mSocketfd = socket(AF_INET, SOCK_STREAM, 0);
if(mSocketfd < 0)
{
LOGE(TAG, "socket failed ...");
return false;
}
mSaddr.sin_family = AF_INET;
mSaddr.sin_port = htons(mPort);
mSaddr.sin_addr.s_addr = inet_addr(mIp);
LOG(TAG, "connect ...");
result = connect(mSocketfd, (sockaddr *)&mSaddr, (socklen_t)sizeof(mSaddr));
if(result < 0)
{
LOGE(TAG, "connect failed ...");
return false;
}
return true;
}
void Client::DeInit()
{
mIsStop = true;
}
void Client::SetIPandPort()
{
char select[12];
char ip[20];
system("clear");
LOG(TAG, "默认连接的IPV4 = ", mIp);
LOG(TAG, "默认连接的Port = ", mPort);
LOG(TAG, "是否更改连接的ip及port(yes/no)?");
LOGI(select);
if(0 == strcmp(select, "yes") || 0 == strcmp(select, "YES"))
{
LOG(TAG, "请输入新IPV4:");
LOGI(ip);
//mIp = ip;
mIp = new char[20];
strcpy(mIp, ip);
LOG(TAG, "请输入新PORT: ");
LOGI(&mPort);
}
else
{
LOG(TAG, "连接默认的ip地址和端口");
sleep(1);
return ;
}
LOG(TAG, "当前连接的IPV4 = ", mIp);
LOG(TAG, "当前连接的Port = ", mPort);
sleep(1);
}
bool Client::Connect()
{
bool result = true;
result = Init();
if(!result)
{
LOGE(TAG, "init socket error can't connect ...");
return false;
}
else
{
return true;
}
return true;
}
void Client::DisConnect()
{
close(mSocketfd);
}