郑州市汉狮做网站,frontpage做的社交网站,免费咨询怀孕,网站使用前流程文章目录 前言项目需求介绍一、服务端1.对Udp套接字进行一个封装2. UdpServer的编写3. Task.h4.protocol.h的编写5.线程池的编写6.main.cc 二、客户端1. Socket.h2.protocol.h3.UdpClient4.menu.h5.main.cpp 三、运行图 前言
本次项目可以作为之前内容的一个扩展#xff0c;学… 文章目录 前言项目需求介绍一、服务端1.对Udp套接字进行一个封装2. UdpServer的编写3. Task.h4.protocol.h的编写5.线程池的编写6.main.cc 二、客户端1. Socket.h2.protocol.h3.UdpClient4.menu.h5.main.cpp 三、运行图 前言
本次项目可以作为之前内容的一个扩展学会在Windows端进行网络通信。 该项目需要用到的知识手段较多在编写该项目的同时也可以对之前C方面的知识进行一个不错的回顾。
项目需求介绍 本次项目用到 Windows网络套接字编程多线程线程池线程安全互斥锁IO流文件管理数据结构设计序列化反序列化自定义协议STL等相关知识和技术。 提示以下是本篇文章正文内容下面案例可供参考
一、服务端
1.对Udp套接字进行一个封装
//Socket.h
#pragma once
#includeiostream
#includestring
#includeWinSock2.h
#includeWindows.h
#includethread
#includefunctional
#pragma comment(lib, ws2_32.lib) // 链接库文件#pragma warning(disable:4996) //防止VS发出4996号警告enum Erro
{Sock_Error 1,Bind_Error,Listen_Error
};
class Socket
{
public:Socket(){}void Init(){_listensock socket(AF_INET, SOCK_DGRAM, 0);if (_listensock SOCKET_ERROR){//套接字创建失败std::cout Socket Create Error... std::endl;exit(Sock_Error);}}void Bind(const std::string ip, const int port){memset(_sockaddr, 0, sizeof _sockaddr);_sockaddr.sin_family AF_INET;_sockaddr.sin_addr.s_addr inet_addr(ip.c_str());_sockaddr.sin_port htons(port);int n bind(_listensock, (const struct sockaddr*)_sockaddr, sizeof _sockaddr);if (n 0){std::cout Bind Error... std::endl;//std::cout errno strerror(errno) std::endl;exit(Bind_Error);}}~Socket(){closesocket(_listensock);}public:SOCKET _listensock; struct sockaddr_in _sockaddr;
};
Windows OS 和 Linux OS 所提供的网络套接字接口函数有一点点的不同但是实际的使用差别并不是很大 所以这里就不细讲了。
2. UdpServer的编写
// UdpServer.h
class UdpServer
{
public:UdpServer(){_tp ThreadPoolTask::GetInstance();_user_map new std::unordered_mapstd::string, std::string;_online_map new std::unordered_mapstd::string, struct sockaddr_in;_buffer new std::unordered_mapstd::string, std::string;_mutex new std::mutex;}void Init(){//初始化网络环境WSADATA wsd;WSAStartup(MAKEWORD(2, 2), wsd);_host.Init();_host.Bind(server_ip, server_port);ReadUser();}void ReadUser(){std::ifstream in(User.txt);if (!in.is_open()){//文件打开失败std::cout Open FIle Error... std::endl;}std::string line;while (std::getline(in, line)){//username passwd\nsize_t pos line.find( );std::string username line.substr(0, pos);std::string passwd line.substr(pos 1);_user_map-insert(std::pairstd::string,std::string(username,passwd));}std::cout -------------------------------------------------------------- std::endl;std::cout 所有已注册账号密码 std::endl;for (auto e : *_user_map){std::cout e.first e.second std::endl;}std::cout -------------------------------------------------------------- std::endl;}void Start(){_tp-Start();struct sockaddr_in client;char inbuffer[1024];std::string recvmes;while (true){int len sizeof client; // unsigned intmemset(client, 0, sizeof client);// 服务器接受数据//std::cout 开始等待数据 std::endl;int n recvfrom(_host._listensock, inbuffer, sizeof inbuffer - 1, 0, (sockaddr*)client, len);if (n 0){inbuffer[n] \0;recvmes inbuffer;while (!recvmes.empty()){Request rq;if (!rq.deserialize(recvmes)){//报文错误丢弃所有报文std::cout 报文错误 std::endl;break;}Task task(rq, client,_user_map,_mutex,_host._listensock,_online_map,_buffer);_tp-Push(task);}}else{std::cout Read Error... std::endl;exit(1);}}}~UdpServer(){WSACleanup(); //清理网络环境}
private:Socket _host;ThreadPoolTask* _tp;std::unordered_mapstd::string, std::string* _user_map;std::unordered_mapstd::string, struct sockaddr_in* _online_map;std::unordered_mapstd::string, std::string* _buffer;std::mutex* _mutex;
};我们也对UdpServer也进行了一个封装这里如果想要的话还可以继续接着完善加入守护线程和单例模式大家感兴趣可以下来试一试。
主线程老老实实打印数据其他线程就去处理客户端发来的命令。
我们可以看到类里面有许多成员变量 这里对这些变量都进行解释一下。 Socket _host; 毫无疑问服务端必须要申请一个套接字 ThreadPool* _tp; 这个是线程池的实例指针 std::unordered_mapstd::string, std::string* _user_map; 这个是服务器维护所有注册用户的哈希map std::unordered_mapstd::string, struct sockaddr_in* _online_map; 这个是服务器维护所有在线用户的哈希map std::unordered_mapstd::string, std::string* _buffer; 这个是存放所有注册用户离线消息的缓冲区 std::mutex* _mutex; 这个是为了保证线程安全提供的互斥锁 既然服务器想要维护所有用户信息那么文件管理的IO就必不可少所以这里也提供了 ReadUser函数 void ReadUser(){std::ifstream in(User.txt);if (!in.is_open()){//文件打开失败std::cout Open FIle Error... std::endl;}std::string line;while (std::getline(in, line)){//username passwd\nsize_t pos line.find( );std::string username line.substr(0, pos);std::string passwd line.substr(pos 1);_user_map-insert(std::pairstd::string,std::string(username,passwd));}std::cout -------------------------------------------------------------- std::endl;std::cout 所有已注册账号密码 std::endl;for (auto e : *_user_map){std::cout e.first e.second std::endl;}std::cout -------------------------------------------------------------- std::endl;}作为初始化_user_map的函数。
3. Task.h
//Task.h
#pragma once#includeiostream
#includestring
#includeunordered_map
#includemutex
#includefstream#includeprotocol.h
#includeSocket.hstd::string offline_message
-------------------------------------------------------------------------------\n\离线消息\n;enum Code {Login_Err -1,Normal 0,Online_User,All_User
};
class Task
{
public:Task(){}Task(const Request rq, struct sockaddr_in client, std::unordered_mapstd::string, std::string* user_map ,std::mutex* mutex, SOCKET host, std::unordered_mapstd::string, struct sockaddr_in* online_map,std::unordered_mapstd::string, std::string* buffer):_rq(rq), _client(client),_user_map(user_map),_mutex(mutex) ,_host(host),_online_map(online_map),_buffer(buffer) {}void SendOneMessage(int code, const std::string info, const struct sockaddr_in client){Respond rs(info, code);std::string mes;rs.serialize(mes);sendto(_host, mes.c_str(), mes.size(), 0, (const struct sockaddr*)(client), sizeof client);}void SendEveryoneMessage(const std::string info){Respond rs(info);std::string mes;rs.serialize(mes);for (auto user : *_online_map){sendto(_host, mes.c_str(), mes.size(), 0, (const struct sockaddr*)(user.second), sizeof(user.second));}}bool CheckUser(const std::string name, const std::string mes){auto a_it _user_map-find(name);if (a_it _user_map-end()){//不存在该用户SendOneMessage(Login_Err, std::string(该用户未注册请输入/quit退出聊天框), _client);return false;}auto o_it _online_map-find(name);if (o_it _online_map-end()){//该用户不在线SendOneMessage(0, std::string(该用户未上线您可以继续发送离线消息对方在上线后可查看), _client);Respond rs(mes,0);std::string tmp;rs.serialize(tmp);_mutex-lock();(*_buffer)[name] tmp;_mutex-unlock();return false;}return true;}void SendSpecifiedUser(const std::string client_name, const std::string name, const std::string info){std::string mes ;mes client_name;mes : ;mes info;if (!CheckUser(name,mes)){return;}struct sockaddr_in spc_user (*_online_map)[name];SendOneMessage(0, mes, spc_user);}void GetOnlineUser(std::string* out){std::string tmp;for (auto e : *_online_map){tmp e.first;tmp \n;}*out tmp;}void GetAllUser(std::string* out){std::string tmp;for (auto e : *_user_map){tmp e.first;tmp \n;}*out tmp;}void WriteUser(const std::string name, const std::string passwd){std::ofstream file(User.txt, std::ios::app);if (file){file name passwd std::endl;file.close();}else{std::cout 写入失败 std::endl;}}bool UserRegisterCheck(std::string name, std::string passwd){auto it _user_map-find(name);if (it _user_map-end()){//没找到,可以进行注册_mutex-lock();(*_user_map)[name] passwd;_mutex-unlock();WriteUser(name, passwd);std::string mes Sign Up Succeed!;SendOneMessage(0, mes,_client);mes 用户;mes name;mes 已上线,快来找他聊天吧;SendEveryoneMessage(mes);_mutex-lock();(*_online_map)[name] _client;_mutex-unlock();return true;}else{std::string mes Sign Up Failed, The Same UserName Already Exists;SendOneMessage(Login_Err, mes, _client);return false;}}bool UserLoginCheck(std::string name, std::string passwd){std::string mes;auto it _user_map-find(name);if (it _user_map-end()){//没找到直接Passmes Sign In Failed, Your Account Is Wrong;SendOneMessage(Login_Err, mes, _client);return false;}if ((*_user_map)[name] ! passwd){//密码错误mes Sign In Failed, Your Password Is Wrong;SendOneMessage(Login_Err, mes, _client);return false;}if (_online_map-find(name) ! _online_map-end()){//当前用户已经在线了mes The User has Signed In;SendOneMessage(Login_Err, mes, _client);return false;}mes Sign In Succeed! Weclome Back ;mes name;mes !;SendOneMessage(0, mes, _client);mes 用户;mes name;mes 已上线,快来找他聊天吧;SendEveryoneMessage(mes);_mutex-lock();(*_online_map)[name] _client;_mutex-unlock();//发送离线消息if (_buffer-find(name) ! _buffer-end()){//离线buffer有它的信息SendOneMessage(Normal, offline_message, _client);sendto(_host, (*_buffer)[name].c_str(), (*_buffer)[name].size(), 0, (const struct sockaddr*)(_client), sizeof _client);_mutex-lock();_buffer-erase(name);_mutex-unlock();}return true;}void LogOut(const std::string name){auto o_it _online_map-find(name);if (o_it _online_map-end()){//该用户不在线return;}_mutex-lock();_online_map-erase(name);_mutex-unlock();std::string mes;mes 用户;mes name;mes 已下线;SendEveryoneMessage(mes);}void run(){//根据类型处理信息if (_rq._type /signup){//注册流程UserRegisterCheck(_rq._info1, _rq._info2);}else if (_rq._type /signin){//登录流程UserLoginCheck(_rq._info1, _rq._info2);}else if (_rq._type /getonline){//给客户端发在线用户表std::string online;GetOnlineUser(online);SendOneMessage(Online_User, online, _client);}else if (_rq._type /getall){std::string all;GetAllUser(all);SendOneMessage(All_User, all, _client);}else if (_rq._type /exit){//下线LogOut(_rq._info1);}else if (_rq._type.find(/info) ! std::string::npos){std::string client_name _rq._type.substr(5);SendSpecifiedUser(client_name,_rq._info1, _rq._info2);}}void operator()(){run();}~Task(){}private:Request _rq;struct sockaddr_in _client;std::unordered_mapstd::string, std::string* _user_map;std::unordered_mapstd::string, struct sockaddr_in* _online_map;std::unordered_mapstd::string, std::string* _buffer;std::mutex* _mutex;SOCKET _host;};Task.h其实才是重中之重所有服务器的需求我都写在了这里面可以根据函数名和成员变量来分析每个函数都是实现了一个怎样的功能。 该项目服务器所有功能的实现我都进行了接近完美的封装。
4.protocol.h的编写
#pragma once#include iostream
#include stringconst char blank_space_sep ;
const char protocol_sep ;class Request
{
public:Request() {} // 提供一个无参构造Request(const std::string type, const std::string info1 , const std::string info2 ): _type(type), _info1(info1), _info2(info2) {}Request(const char* type, const char* info1, const char* info2): _type(type), _info1(info1), _info2(info2) {}bool serialize(std::string* out_str){// 协议规定 字符串格式应序列化为len\n_type _info1 _info2\nstd::string main_body _type;main_body blank_space_sep;main_body _info1;main_body blank_space_sep;main_body _info2;*out_str std::to_string(main_body.size());*out_str protocol_sep;*out_str main_body;*out_str protocol_sep;return true;}bool deserialize(std::string in_str){// 协议规定 in_str的格式应为len_type _info1 _info2size_t pos in_str.find(protocol_sep);if (pos std::string::npos){// 说明没找到return false;}std::string sl in_str.substr(0, pos);int len std::stoi(sl); // 如果这里的sl不是一串数字stoi就会抛异常 BUG? 严格限制客户端行为size_t total_len sl.size() 1 len 1;if (in_str.size() total_len){return false;}if (in_str[total_len - 1] ! protocol_sep){return false;}std::string main_body in_str.substr(pos 1, len);// main_body_type _info1 _info2size_t left main_body.find(blank_space_sep);if (left std::string::npos){// 说明没找到 return false;}size_t right main_body.rfind(blank_space_sep);if (left right){// 说明只有一个 return false;}_type main_body.substr(0, left); _info2 main_body.substr(right 1);_info1 main_body.substr(left 1, right - left - 1);in_str.erase(0, total_len);return true;}void print(){std::cout _type _info1 _info2 std::endl;}~Request() {}public:std::string _type;std::string _info1;std::string _info2;
};class Respond
{
public:Respond() {} // 提供一个无参构造Respond(std::string info, int code 0): _info(info), _code(code) {}bool serialize(std::string* out_str){// 协议规定 字符串格式应序列化为len_code _infostd::string main_body std::to_string(_code);main_body blank_space_sep;main_body _info;*out_str std::to_string(main_body.size());*out_str protocol_sep;*out_str main_body;*out_str protocol_sep;return true;}bool deserialize(std::string in_str){// 协议规定 in_str的格式应为len_code _infosize_t pos in_str.find(protocol_sep);if (pos std::string::npos){// 说明没找到return false;}std::string sl in_str.substr(0, pos);int len std::stoi(sl); // 如果这里的sl不是一串数字stoi就会抛异常 BUG? 严格限制客户端行为size_t total_len sl.size() 1 len 1;if (in_str.size() total_len){return false;}if (in_str[total_len - 1] ! protocol_sep){return false;}std::string main_body in_str.substr(pos 1, len);// main_body_code _infosize_t blank main_body.find(blank_space_sep);if (blank std::string::npos){// 说明没找到 return false;}_code std::stoi(main_body.substr(0, blank));_info main_body.substr(blank 1);in_str.erase(0, total_len);return true;}void print(){std::cout _code _info std::endl;}~Respond() {}public:int _code; // 表示结果可信度 0表示可信std::string _info;
};序列化和反序列化我们之前也有写过。 因为我们这里服务器跟客户端的需求过多所以就需要对提交来的数据进行分析。 class Request { _type 作为客户端想要执行的命令 _info1 作为客户端发来的信息1 _info 2 作为客户端发来的信息2 }
class Respond{ _code 作为服务端向客户端数据的标识符解释 _info 作为服务端向客户端发送的信息 }
5.线程池的编写
#pragma once#includeiostream
#includethread
#includestring
#includemutex
#includevector
#includequeue
#includecondition_variablestatic const int defalutnum 10;template class T
class ThreadPool
{
public:void Wakeup(){_cond.notify_one();}void ThreadSleep(std::unique_lockstd::mutex lock){_cond.wait(lock);}bool IsQueueEmpty(){return _tasks.empty();}public:static void HandlerTask(ThreadPoolT* tp){while (true){T t;{std::unique_lockstd::mutexlock(_mutex);while (tp-IsQueueEmpty()){tp-ThreadSleep(lock);}t tp-Pop();}t();}}void Start(){size_t num _threads.size();for (int i 0; i num; i){_threads[i] std::thread(HandlerTask, this);}}T Pop(){T t _tasks.front();_tasks.pop();return t;}void Push(const T t){std::unique_lockstd::mutexlock(_mutex);_tasks.push(t);Wakeup();}static ThreadPoolT* GetInstance(){if (nullptr _tp) {std::unique_lockstd::mutexlock(_lock);if (nullptr _tp){_tp new ThreadPoolT();}}return _tp;}private:ThreadPool(int num defalutnum) : _threads(num){}~ThreadPool() {}ThreadPool(const ThreadPoolT) delete;const ThreadPoolT operator(const ThreadPoolT) delete;
private:std::vectorstd::thread _threads;std::queueT _tasks;static std::mutex _mutex;std::condition_variable _cond;static ThreadPoolT* _tp;static std::mutex _lock;
};template class T
ThreadPoolT* ThreadPoolT::_tp nullptr;template class T
std::mutex ThreadPoolT::_lock;template class T
std::mutex ThreadPoolT::_mutex;
这里的线程池我直接采用了C11提供的Thread类并进行了完美的封装。
6.main.cc
这个我是不太想贴出来的有点侮辱大家的智商不过考虑到广大大学生…
#includeUdpServer.hint main()
{UdpServer us;us.Init();us.Start();return 0;
}二、客户端
1. Socket.h
与服务端Socket.h代码一致
2.protocol.h
与服务端protocol.h代码一致
3.UdpClient
#pragma once#includestring
#includethread#includeprotocol.h
#includeSocket.h
#includemenu.h
#pragma comment(lib, ws2_32.lib) // 链接库文件
#pragma warning(disable:4996) //防止VS发出4996号警告const int server_port 8080;
const std::string server_ip 127.0.0.1; //提前写好服务器IPstd::string yourname ;enum Code {Login_Err -1,Normal 0,Online_User,All_User
};struct Thread_Data
{SOCKET socket_fd;struct sockaddr_in server;
};void GetOnlineUser(const Thread_Data data)
{Request rq(/getonline);std::string info;rq.serialize(info);sendto(data.socket_fd, info.c_str(), (int)info.size(), 0, (const struct sockaddr*)data.server, sizeof(data.server));
}void GetAllUser(const Thread_Data data)
{Request rq(/getall);std::string info;rq.serialize(info);sendto(data.socket_fd, info.c_str(), (int)info.size(), 0, (const struct sockaddr*)data.server, sizeof(data.server));
}void Exit(const Thread_Data data)
{Request rq(/exit, yourname);std::string info;rq.serialize(info);sendto(data.socket_fd, info.c_str(), (int)info.size(), 0, (const struct sockaddr*)data.server, sizeof(data.server));exit(0);
}void recv_mes(const Thread_Data data)
{char buffer[1024];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in tmp;int tmp_len sizeof(tmp);int n recvfrom(data.socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)tmp, tmp_len);if (n 0){buffer[n] 0;message buffer;while (!message.empty()){Respond rs;if (!rs.deserialize(message)){//报文错误丢弃所有报文std::cout 报文错误 std::endl;break;}if (rs._code Online_User){PrintOnline(rs._info);continue;}else if (rs._code All_User){PrintAll(rs._info);continue;}std::cout rs._info std::endl;}}else{std::cout Recv Error... std::endl;exit(1);}}
}void LoginPage(const Thread_Data data)
{std::string name;while (true){std::cout sign_page std::endl;std::string login;std::cin login;std::string passwd;if (login 1){//登录std::cout 请输入你的账号;std::cin name;std::cout 请输入你的密码;std::cin passwd;Request rq(/signin, name.c_str(), passwd.c_str());std::string mes;rq.serialize(mes);sendto(data.socket_fd, mes.c_str(), (int)mes.size(), 0, (const struct sockaddr*)data.server, sizeof(data.server));break;}else if (login 2){//注册while (true){std::cout 请输入你要注册的账号;std::cin name;std::cout 请输入你要注册的密码;std::cin passwd;std::string confirm;std::cout 请重新输入你的密码;std::cin confirm;if (confirm ! passwd){std::cout 两次输入的密码不正确请重新注册 std::endl;continue;}break;}Request rq(/signup, name.c_str(), passwd.c_str());std::string mes;rq.serialize(mes);sendto(data.socket_fd, mes.c_str(), (int)mes.size(), 0, (const struct sockaddr*)data.server, sizeof(data.server));break;}else{//用户输入错误std::cout 输入错误请重新输入 std::endl;continue;}}yourname name;
}void HandRequest(const Thread_Data data)
{std::string message;std::cout ico std::endl;while (true){LoginPage(data);//回应请求char buffer[1024];memset(buffer, 0, sizeof(buffer));struct sockaddr_in tmp;int tmp_len sizeof tmp;int n recvfrom(data.socket_fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*)tmp, tmp_len);if (n 0){buffer[n] 0;message buffer;Respond rs;rs.deserialize(message);std::cout rs._info std::endl;if (rs._code Login_Err){std::cout 请重新登录/注册 std::endl;continue;}break;}else{std::cout Hand Recv Error... std::endl;exit(1);}}}void ConnectUser(const Thread_Data data, const std::string name)
{std::string mes;std::cout ------------------------------------------------------------------------------------------ std::endl;std::cout 聊天框 std::endl;std::cout 小帮手 想要退出聊天框请输入/quit std::endl;while (true){std::cout Send A Message;std::cin mes;if (mes /help){HelpMenu();continue;}else if (mes /quit){break;}else if (mes /online){GetOnlineUser(data);continue;}else if (mes /all){GetAllUser(data);continue;}else if (mes /clear){system(cls);}else if (mes /exit){Exit(data);}std::string cmd /info;cmd yourname;Request rq(cmd.c_str(), name, mes.c_str());std::string info;rq.serialize(info);sendto(data.socket_fd, info.c_str(), (int)info.size(), 0, (const struct sockaddr*)data.server, sizeof(data.server));std::cout 成功发送消息 std::endl;}std::cout ------------------------------------------------------------------------------------------ std::endl;
}void send_mes(const Thread_Data data)
{HelpMenu();std::string command;std::string name;while (true){std::cin command;//根据if(command /online){ GetOnlineUser(data);continue;}else if (command /all){GetAllUser(data);continue;}else if (command /help){HelpMenu();continue;}else if (command /go){GetOnlineUser(data);std::cout 你想要和谁聊天 std::endl;std::cin name;if (name yourname){std::cout 不可与自己聊天退出 std::endl;continue;}ConnectUser(data, name);std::cout 你已离开与 name 的聊天 可以输入/online 查看当前在线用户 std::endl;}else if (command /clear){system(cls);}else if (command /exit){Exit(data);}else{std::cout 未知命令输入/help来查看所有命令 std::endl;}}
}class UdpClient {
public:UdpClient() {}void Init(){WSADATA wsd;WSAStartup(MAKEWORD(2, 2), wsd);_host.Init();}void Start(){struct sockaddr_in server;memset(server, 0, sizeof server);server.sin_family AF_INET;server.sin_addr.s_addr inet_addr(server_ip.c_str());server.sin_port htons(server_port);std::thread threads[2];Thread_Data data;data.server server;data.socket_fd _host._listensock;//等待第一次握手请求HandRequest(data);threads[1] std::thread(send_mes, std::ref(data));threads[0] std::thread(recv_mes, std::ref(data));threads[0].join();threads[1].join();}~UdpClient() {WSACleanup(); //清理网络环境}
private:Socket _host;
};
客户端的设计我是分为两个部分第一个部分我称为客户端的握手请求 让客户端进行登录或注册。 然后再进入第二个部分从这里开始读取数据和写数据就使用多线程让两个模块进行分离。
写数据的进程 可以让用户自由选择交互命令读数据的进程分析服务器发来的数据并进行响应。
4.menu.h
#pragma once#includestring
#includeiostream
#includevector
//#pragma execution_character_set(utf-8)const std::string ico ______ _ _ _ _____ _ _ \n\| ____| | | (_) ( ) / ____| | | | \n\| |__ ___ _ __ __ _ | |_ _ _ __ _____ |/ ___ | | | |__ __ _| |_ \n\| __/ _ \\ _ \\ / _ | _ | | | | | _ \\|_ / | / __| | | | _ \\ / _ | __|\n\| | | __/ | | | (_| | | |__| | |_| | | | |/ /| | \\__ \\ | |____| | | | (_| | |_ \n\|_| \\___|_| |_|\\__, | \\____/ \\__,_|_| |_/___|_| |___/ \\_____|_| |_|\\__,_|\\__|\n\__/ | \n\|___/ \n;const std::string sign_page ------------------------------------------------------------------------------------------\n\
| 1.sign in(登录) 2. sign up(注册) |\n\
| 请输入序号 |\n\
| |\n\------------------------------------------------------------------------------------------;const std::vectorstd::string OKword { };void PrintOnline(const std::string online)
{std::cout ------------------------------------------------------------------------------------------ std::endl;std::cout 当前在线用户 std::endl;std::cout online std::endl;std::cout ------------------------------------------------------------------------------------------ std::endl;
}void PrintAll(const std::string all)
{{std::cout ------------------------------------------------------------------------------------------ std::endl;std::cout 所有注册用户 std::endl;std::cout all std::endl;std::cout ------------------------------------------------------------------------------------------ std::endl;}}void HelpMenu()
{printf( --------------------------------------------------------------------------------------------------------------------\n\
| 小帮手 |\n\
| 输入/online 可以查看当前在线用户 |\n\
| 输入/all 可以查看所有注册用户 |\n\
| 输入/help 可以再次召唤小帮手 |\n\
| 输入/go 进入一个指定用户的聊天窗口在聊天窗口内不可使用/go命令期间可以收到其他用户发来的消息 |\n\
| 输入/quit 可以离开与当前用户的聊天窗口 |\n\
| 输入/clear 可以清理界面 |\n\
| 输入/exit 关闭客户端下线 |\n\
| |\n\
| WARNING: 由于该程序采用UDP协议为无连接传输协议请登录后务必使用/exit退出程序不要直接关闭客户端否则后果自负 |\n\
| WARNING: 由于该程序采用UDP协议为无连接传输协议请登录后务必使用/exit退出程序不要直接关闭客户端否则后果自负 |\n\
| WARNING: 由于该程序采用UDP协议为无连接传输协议请登录后务必使用/exit退出程序不要直接关闭客户端否则后果自负 |\n\--------------------------------------------------------------------------------------------------------------------\n);
}
这个头文件就主要是在命令行界面一定程度上做一些仿图形界面方便美观。
5.main.cpp
#includeUdpClient.hint main()
{UdpClient uc;uc.Init();uc.Start();return 0;
}三、运行图
这里我就不放出服务器运行的图片了因为服务器运行的时候我没有写什么输出屏幕语句所以啥也没有。