中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

學(xué)做餅干網(wǎng)站發(fā)稿網(wǎng)

學(xué)做餅干網(wǎng)站,發(fā)稿網(wǎng),張家口市一建公司官網(wǎng),wordpress跳轉(zhuǎn)鏈接文章目錄 前言項目需求介紹一、服務(wù)端1.對Udp套接字進行一個封裝2. UdpServer的編寫3. Task.h4.protocol.h的編寫5.線程池的編寫6.main.cc 二、客戶端1. Socket.h2.protocol.h3.UdpClient4.menu.h5.main.cpp 三、運行圖 前言 本次項目可以作為之前內(nèi)容的一個擴展,學(xué)…

文章目錄

  • 前言
  • 項目需求介紹
  • 一、服務(wù)端
    • 1.對Udp套接字進行一個封裝
    • 2. UdpServer的編寫
    • 3. Task.h
    • 4.protocol.h的編寫
    • 5.線程池的編寫
    • 6.main.cc
  • 二、客戶端
    • 1. Socket.h
    • 2.protocol.h
    • 3.UdpClient
    • 4.menu.h
    • 5.main.cpp
  • 三、運行圖


前言

本次項目可以作為之前內(nèi)容的一個擴展,學(xué)會在Windows端進行網(wǎng)絡(luò)通信。
該項目需要用到的知識手段較多,在編寫該項目的同時也可以對之前C++方面的知識進行一個不錯的回顧。

項目需求介紹

在這里插入圖片描述

本次項目用到 Windows網(wǎng)絡(luò)套接字編程,多線程,線程池,線程安全,互斥鎖,IO流,文件管理,數(shù)據(jù)結(jié)構(gòu)設(shè)計,序列化,反序列化,自定義協(xié)議,STL等相關(guān)知識和技術(shù)。


提示:以下是本篇文章正文內(nèi)容,下面案例可供參考

一、服務(wù)端

1.對Udp套接字進行一個封裝

//Socket.h
#pragma once
#include<iostream>
#include<string>
#include<WinSock2.h>
#include<Windows.h>
#include<thread>
#include<functional>
#pragma comment(lib, "ws2_32.lib") // 鏈接庫文件#pragma warning(disable:4996)      //防止VS發(fā)出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){//套接字創(chuàng)建失敗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 所提供的網(wǎng)絡(luò)套接字接口函數(shù)有一點點的不同,但是實際的使用差別并不是很大! 所以這里就不細講了。

2. UdpServer的編寫

// UdpServer.h
class UdpServer
{
public:UdpServer(){_tp = ThreadPool<Task>::GetInstance();_user_map = new std::unordered_map<std::string, std::string>;_online_map = new std::unordered_map<std::string, struct sockaddr_in>;_buffer = new std::unordered_map<std::string, std::string>;_mutex = new std::mutex;}void Init(){//初始化網(wǎng)絡(luò)環(huán)境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\n"size_t pos = line.find(' ');std::string username = line.substr(0, pos);std::string passwd = line.substr(pos + 1);_user_map->insert(std::pair<std::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);// 服務(wù)器接受數(shù)據(jù)//std::cout << "開始等待數(shù)據(jù)" << 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();   //清理網(wǎng)絡(luò)環(huán)境}
private:Socket _host;ThreadPool<Task>* _tp;std::unordered_map<std::string, std::string>* _user_map;std::unordered_map<std::string, struct sockaddr_in>* _online_map;std::unordered_map<std::string, std::string>* _buffer;std::mutex* _mutex;
};

我們也對UdpServer也進行了一個封裝,這里如果想要的話還可以繼續(xù)接著完善,加入守護線程單例模式,大家感興趣可以下來試一試。

主線程老老實實打印數(shù)據(jù),其他線程就去處理客戶端發(fā)來的命令。

我們可以看到類里面有許多成員變量, 這里對這些變量都進行解釋一下。

Socket _host; 毫無疑問,服務(wù)端必須要申請一個套接字
ThreadPool* _tp; 這個是線程池的實例指針
std::unordered_map<std::string, std::string>* _user_map; 這個是服務(wù)器維護所有注冊用戶的哈希map
std::unordered_map<std::string, struct sockaddr_in>* _online_map; 這個是服務(wù)器維護所有在線用戶的哈希map
std::unordered_map<std::string, std::string>* _buffer; 這個是存放所有注冊用戶離線消息的緩沖區(qū)
std::mutex* _mutex; 這個是為了保證線程安全提供的互斥鎖

既然服務(wù)器想要維護所有用戶信息,那么文件管理的IO就必不可少,所以這里也提供了 ReadUser函數(shù)

    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\n"size_t pos = line.find(' ');std::string username = line.substr(0, pos);std::string passwd = line.substr(pos + 1);_user_map->insert(std::pair<std::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的函數(shù)。

3. Task.h

//Task.h
#pragma once#include<iostream>
#include<string>
#include<unordered_map>
#include<mutex>
#include<fstream>#include"protocol.h"
#include"Socket.h"std::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_map<std::string, std::string>* user_map ,std::mutex* mutex, SOCKET host, std::unordered_map<std::string, struct sockaddr_in>* online_map,std::unordered_map<std::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("該用戶未上線,您可以繼續(xù)發(fā)送離線消息,對方在上線后可查看"), _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()){//當(dāng)前用戶已經(jīng)在線了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();//發(fā)送離線消息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(){//根據(jù)類型處理信息if (_rq._type == "/signup"){//注冊流程UserRegisterCheck(_rq._info1, _rq._info2);}else if (_rq._type == "/signin"){//登錄流程UserLoginCheck(_rq._info1, _rq._info2);}else if (_rq._type == "/getonline"){//給客戶端發(fā)在線用戶表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_map<std::string, std::string>* _user_map;std::unordered_map<std::string, struct sockaddr_in>* _online_map;std::unordered_map<std::string, std::string>* _buffer;std::mutex* _mutex;SOCKET _host;};

Task.h其實才是重中之重,所有服務(wù)器的需求我都寫在了這里面,可以根據(jù)函數(shù)名和成員變量來分析每個函數(shù)都是實現(xiàn)了一個怎樣的功能。 該項目服務(wù)器所有功能的實現(xiàn),我都進行了接近完美的封裝。

4.protocol.h的編寫

#pragma once#include <iostream>
#include <string>const char blank_space_sep = ' ';
const char protocol_sep = '&';class Request
{
public:Request() {} // 提供一個無參構(gòu)造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){// 協(xié)議規(guī)定 字符串格式應(yīng)序列化為"len\n_type _info1 _info2\n"std::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){// 協(xié)議規(guī)定 in_str的格式應(yīng)為"len&_type _info1 _info2&"size_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不是一串?dāng)?shù)字,stoi就會拋異常! BUG?  嚴(yán)格限制客戶端行為!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 _info2"size_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() {} // 提供一個無參構(gòu)造Respond(std::string info, int code = 0): _info(info), _code(code) {}bool serialize(std::string* out_str){// 協(xié)議規(guī)定 字符串格式應(yīng)序列化為"len&_code _info"std::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){// 協(xié)議規(guī)定 in_str的格式應(yīng)為"len&_code _info"size_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不是一串?dāng)?shù)字,stoi就會拋異常! BUG?  嚴(yán)格限制客戶端行為!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 _info"size_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; // 表示結(jié)果可信度  0表示可信std::string _info;
};

序列化和反序列化,我們之前也有寫過。 因為我們這里服務(wù)器跟客戶端的需求過多,所以就需要對提交來的數(shù)據(jù)進行分析。
class Request
{
_type 作為客戶端想要執(zhí)行的命令
_info1 作為客戶端發(fā)來的信息1
_info 2 作為客戶端發(fā)來的信息2
}

class Respond{
_code 作為服務(wù)端向客戶端數(shù)據(jù)的標(biāo)識符(解釋)
_info 作為服務(wù)端向客戶端發(fā)送的信息
}

5.線程池的編寫

#pragma once#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<vector>
#include<queue>
#include<condition_variable>static const int defalutnum = 10;template <class T>
class ThreadPool
{
public:void Wakeup(){_cond.notify_one();}void ThreadSleep(std::unique_lock<std::mutex>& lock){_cond.wait(lock);}bool IsQueueEmpty(){return _tasks.empty();}public:static void HandlerTask(ThreadPool<T>* tp){while (true){T t;{std::unique_lock<std::mutex>lock(_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_lock<std::mutex>lock(_mutex);_tasks.push(t);Wakeup();}static ThreadPool<T>* GetInstance(){if (nullptr == _tp) {std::unique_lock<std::mutex>lock(_lock);if (nullptr == _tp){_tp = new ThreadPool<T>();}}return _tp;}private:ThreadPool(int num = defalutnum) : _threads(num){}~ThreadPool() {}ThreadPool(const ThreadPool<T>&) = delete;const ThreadPool<T>& operator=(const ThreadPool<T>&) = delete;
private:std::vector<std::thread> _threads;std::queue<T> _tasks;static std::mutex _mutex;std::condition_variable _cond;static ThreadPool<T>* _tp;static std::mutex _lock;
};template <class T>
ThreadPool<T>* ThreadPool<T>::_tp = nullptr;template <class T>
std::mutex ThreadPool<T>::_lock;template <class T>
std::mutex ThreadPool<T>::_mutex;

這里的線程池,我直接采用了C++11提供的Thread類,并進行了完美的封裝。

6.main.cc

這個我是不太想貼出來的,有點侮辱大家的智商,不過考慮到廣大大學(xué)生…

#include"UdpServer.h"int main()
{UdpServer us;us.Init();us.Start();return 0;
}

二、客戶端

1. Socket.h

與服務(wù)端Socket.h代碼一致

2.protocol.h

與服務(wù)端protocol.h代碼一致

3.UdpClient

#pragma once#include<string>
#include<thread>#include"protocol.h"
#include"Socket.h"
#include"menu.h"
#pragma comment(lib, "ws2_32.lib") // 鏈接庫文件
#pragma warning(disable:4996)      //防止VS發(fā)出4996號警告const int server_port = 8080;
const std::string server_ip = "127.0.0.1"; //提前寫好服務(wù)器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);//回應(yīng)請求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 << "成功發(fā)送消息" << 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;//根據(jù)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 ,查看當(dāng)前在線用戶" << 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();   //清理網(wǎng)絡(luò)環(huán)境}
private:Socket _host;
};

客戶端的設(shè)計,我是分為兩個部分,第一個部分我稱為客戶端的握手請求,
讓客戶端進行登錄或注冊。 然后再進入第二個部分,從這里開始讀取數(shù)據(jù)和寫數(shù)據(jù)就使用多線程讓兩個模塊進行分離。

寫數(shù)據(jù)的進程 可以讓用戶自由選擇交互命令,讀數(shù)據(jù)的進程分析服務(wù)器發(fā)來的數(shù)據(jù),并進行響應(yīng)。

4.menu.h

#pragma once#include<string>
#include<iostream>
#include<vector>
//#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::vector<std::string> OKword = { "" };void PrintOnline(const std::string& online)
{std::cout << "------------------------------------------------------------------------------------------" << std::endl;std::cout << "                                        當(dāng)前在線用戶" << 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  可以查看當(dāng)前在線用戶                                                                                  |\n\
| 輸入/all     可以查看所有注冊用戶                                                                                  |\n\
| 輸入/help    可以再次召喚小幫手                                                                                    |\n\
| 輸入/go      進入一個指定用戶的聊天窗口,在聊天窗口內(nèi)不可使用/go命令,期間可以收到其他用戶發(fā)來的消息               |\n\
| 輸入/quit    可以離開與當(dāng)前用戶的聊天窗口                                                                          |\n\
| 輸入/clear   可以清理界面                                                                                          |\n\
| 輸入/exit    關(guān)閉客戶端,下線                                                                                      |\n\
|                                                                                                                    |\n\
| WARNING: 由于該程序采用UDP協(xié)議,為無連接傳輸協(xié)議,請登錄后務(wù)必使用/exit退出程序,不要直接關(guān)閉客戶端,否則后果自負(fù)  |\n\
| WARNING: 由于該程序采用UDP協(xié)議,為無連接傳輸協(xié)議,請登錄后務(wù)必使用/exit退出程序,不要直接關(guān)閉客戶端,否則后果自負(fù)  |\n\
| WARNING: 由于該程序采用UDP協(xié)議,為無連接傳輸協(xié)議,請登錄后務(wù)必使用/exit退出程序,不要直接關(guān)閉客戶端,否則后果自負(fù)  |\n\--------------------------------------------------------------------------------------------------------------------\n");
}

這個頭文件就主要是在命令行界面一定程度上做一些仿圖形界面,方便美觀。

5.main.cpp

#include"UdpClient.h"int main()
{UdpClient uc;uc.Init();uc.Start();return 0;
}

三、運行圖

這里我就不放出服務(wù)器運行的圖片了,因為服務(wù)器運行的時候,我沒有寫什么輸出屏幕語句,所以啥也沒有。

在這里插入圖片描述

http://www.risenshineclean.com/news/31472.html

相關(guān)文章:

  • 行業(yè)門戶網(wǎng)站模板中國剛剛發(fā)生8件大事
  • 如何開發(fā)app小程序win優(yōu)化大師
  • 北京市文化局政務(wù)網(wǎng)站建設(shè)項目獨立站seo怎么做
  • 深圳網(wǎng)站制作鄭州怎么優(yōu)化網(wǎng)站排名靠前
  • 南通seo公司網(wǎng)站免費推廣產(chǎn)品平臺有哪些
  • 上海奉賢 網(wǎng)站建設(shè)百度指數(shù)查詢移動版
  • 廉江網(wǎng)站建設(shè)公眾號推廣合作平臺
  • html網(wǎng)頁設(shè)計基礎(chǔ)seo優(yōu)化主要做什么
  • 網(wǎng)站開發(fā)者模式企業(yè)官網(wǎng)建站
  • 學(xué)網(wǎng)站建設(shè)需要什么軟件百度外包公司有哪些
  • 企業(yè)網(wǎng)站建設(shè)參考資料競價推廣賬戶競價托管
  • 珠海服務(wù)好的網(wǎng)站建設(shè)武漢seo
  • 江陰做網(wǎng)站百度秒收錄軟件工具
  • 微商網(wǎng)站開發(fā)寧波網(wǎng)站推廣營銷
  • 恒華大廈做網(wǎng)站公司山東做網(wǎng)站
  • 專門教做甜品的網(wǎng)站愛站網(wǎng)關(guān)鍵詞
  • wordpress用qq注冊谷歌seo什么意思
  • 如何做目錄wordpress北京網(wǎng)站優(yōu)化外包
  • 上海 高端 網(wǎng)站建設(shè)我的百度賬號登錄
  • 創(chuàng)新的鹽城網(wǎng)站建設(shè)百度收錄查詢代碼
  • 一步一步教你做網(wǎng)站哪家公司建設(shè)網(wǎng)站好
  • 做網(wǎng)站續(xù)費seo概念
  • 租電信服務(wù)器開網(wǎng)站磁力鏈bt磁力天堂
  • wordpress主題 dux1.2原版整站優(yōu)化排名
  • dk域名網(wǎng)站全搜網(wǎng)
  • 白菜網(wǎng)站建設(shè)如何優(yōu)化網(wǎng)站首頁
  • 公司網(wǎng)站建app互聯(lián)網(wǎng)營銷師怎么報名
  • 建設(shè)部一建注冊公示網(wǎng)站培訓(xùn)心得體會范文大全1000字
  • 怎么建設(shè)好一個外貿(mào)購物網(wǎng)站哪有惡意點擊軟件買的
  • 寧夏住房和城鄉(xiāng)建設(shè)廳網(wǎng)站執(zhí)業(yè)資格工具大全