纯c++写的聊天小程序
涉及的技术
- c++ socket
- c++ stl
- c++ thread
设计的原理
以一个结构体的形式存储客户端,用vector存取存在的客户端,开启多线程处理逻辑
效果
源代码
server
#include<thread>
#include <WinSock2.h>
#include<iostream>
#include<vector>
#include <csignal>
#include<exception>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int myStrLen(char* str)
{
int i = 0;
while (*str != '\0')
{
i++;
str++;
}
return i;
}
char* myStrcat(char *str1, char* str2)
{
char *temp = str1;
while (*temp != '\0')temp++;
while ((*temp++ = *str2++) != '\0');
return str1;
}
char *myStrcp(char *str1, char* str2)
{
char *temp = str1;
while ((*temp++ = *str2++) != '\0');
return str1;
}
bool myStrSame(char *str1, char *str2)
{
if (myStrLen(str1) != myStrLen(str2))return false;
for (int i = 0; i<myStrLen(str1); i++)
if (*str1++ != *str2++)return false;
return true;
}
char * myStrSplit(char *str1, char *str2, char *str3, char sp)
//参数:姓名,需要分解信息,得到信息,分隔符
{
char *temp = str1;
int i = 0;
while ((*str3++ = *str2++) != sp)i++;
while ((*temp++ = *str2++) != '\0');
*(--str3) = '\0';
return str3;
}
//start set global variable
SOCKET ServerSocket = INVALID_SOCKET;//服务端套接字
SOCKET ClientSocket = INVALID_SOCKET;//客户端套接字
SOCKADDR_IN ServerAddr = {
0 };//服务端地址
SOCKADDR_IN ClientAddr = {
0 };//客户端地址
USHORT uPort = 10001;//服务器监听端口
int iClientAddrLen = sizeof(ClientAddr);
struct Client
{
SOCKET client_socket;
char clint_name[255];
};
Client temp_client;
vector<Client> clients;
char buffer[4096] = {
0 };
int iRecvLen = 0;
int iSendLen = 0;
char msg_type = '#';
//end set global variable
//msg type
//# name
//$ show online user
//info@xx send info to xx
//& is exit
bool showClients()
//查看在线的用户
{
if (clients.size() == 0)
{
cout << "online users is null\n";
return false;
}
cout << "online uses are:\n";
for (auto it = clients.begin(); it < clients.end(); it++)
cout << it->clint_name << endl;
return true;
}
void deleteClient(char *name)
//离开的客户端进行处理
{
for (auto it = clients.begin(); it < clients.end(); it++)
{
if (myStrSame(name,it->clint_name))
{
clients.erase(it);
cout << "delete client "<<name<<" success\n";
return;
}
}
//cout << "delete client fail\n";
}
int startServer()
//打开服务器
{
//存放套接字信息的结构
WSADATA wsaData = {
0 };
//初始化套接字
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
{
printf("WSAStartup failed with error code: %d\n", WSAGetLastError());
return -1;
}
//判断版本
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("wVersion was not 2.2\n");
return -1;
}
//创建套接字
ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ServerSocket == INVALID_SOCKET)
{
printf("socket failed with error code: %d\n", WSAGetLastError());
return -1;
}
//设置服务器地址
ServerAddr.sin_family = AF_INET;//连接方式
ServerAddr.sin_port = htons(uPort);//服务器监听端口
ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//任何客户端都能连接这个服务器
//绑定服务器
if (SOCKET_ERROR == ::bind(ServerSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)))
{
printf("bind failed with error code: %d\n", WSAGetLastError());
closesocket(ServerSocket);
return -1;
}
if (SOCKET_ERROR == listen(ServerSocket, 20))
{
printf("listen failed with error code: %d\n", WSAGetLastError());
closesocket(ServerSocket);
WSACleanup();
return -1;
}
cout << "starting server...\n";
return 0;
}
bool isExistClient(char *name)
//判断客户端是否存在在clients中
{
for (auto it = clients.begin(); it < clients.end(); it++)
{
if (myStrSame(name, it->clint_name))return true;
}
return false;
}
void sendMessage(Client * client,char * message,char *name)
//发送消息,目的地的name,以及信息message,转发的消息格式:message+@+sender name
{
char buf[2048] = {
0 };
buf[0] = '\0';
myStrcat(buf, message);
myStrcat(buf, "@");
myStrcat(buf, client->clint_name);
int ret = 0;
//寻找目的客户端,开始发送信息
for (auto it = clients.begin(); it < clients.end(); it++)
{
if (myStrSame(name, it->clint_name))
{
ret = send(it->client_socket, buf, myStrLen(buf), 0);
break;
}
}
if (SOCKET_ERROR == ret)
{
printf("send failed with error code: %d\n", WSAGetLastError());
deleteClient(client->clint_name);
WSACleanup();
}
else
{
cout << client->clint_name << " to " << name << " send :" << buf << endl;
buf[0] = '\0';
}
}
void recvMessage(Client *clinet)
//读取客户端的消息
{
char buf[1024] = {
0 };
int ret = 0;
memset(buf, 0, sizeof(buf));
ret = recv(clinet->client_socket, buf, sizeof(buf), 0);
if (SOCKET_ERROR == ret)
{
deleteClient(clinet->clint_name);
//printf("recv failed with error code: %d\n", WSAGetLastError());
return;
}
else
{
char judge_type = buf[myStrLen(buf) - 1];
buffer[myStrLen(buf) - 1] = '\0';
switch (judge_type)
{
case '#'