go伺服器與mfc做客戶端的簡單群聊Demo
阿新 • • 發佈:2018-11-12
//先前程式碼對中文處理有問題,以下程式碼做了修正,
//go語言伺服器程式碼
package main import( "fmt" "net" "strconv" "runtime" //go執行緒庫 "strings" ) var conns map[string]net.Conn //////////////////////////////////////////////////////// // //錯誤檢查 // //////////////////////////////////////////////////////// func checkError(err error,info string) (res bool) { if(err != nil){ fmt.Println(info+" " + err.Error()) //checkOnlineUser() return false } return true } //////////////////////////////////////////////////////// // //伺服器端接收資料執行緒 //引數: // 資料連線 conn // 通訊通道 messages // //////////////////////////////////////////////////////// func Handler(conn net.Conn,messages chan string){ //fmt.Println("connection is connected from ...",conn.RemoteAddr().String()) buf := make([]byte,1024) for{ lenght, err := conn.Read(buf) if(checkError(err,"Connection")==false){ delete(conns,conn.RemoteAddr().String()) conn.Close() checkOnlineUser() break } if lenght > 0{ buf[lenght]=0 } fmt.Println("Rec[",conn.RemoteAddr().String(),"] Say :" ,buf[:lenght]) say := conn.RemoteAddr().String() + " say: "; s := []byte(say) name := buf[8:lenght] id := buf[4:8] shead := buf[0:4] sh := string(shead[:]) sh = strings.Replace(sh, " ", "", -1) i2,_ := strconv.Atoi(sh) i2 = i2 + len(say) slen := strconv.Itoa(i2) sl := fmt.Sprintf("%4s",slen) head := []byte(sl) content := make([]byte,2048) for i := 0; i < len(head); i++{ content[i] = head[i] } j := 0 for i:= len(head); i < len(id) + len(head) ; i++{ content[i] = id[j] j++ } j=0 for i:=len(head)+len(id); i < len(s) + len(head) + len(id) ; i++{ content[i] = s[j] j++ } j = 0 for i:=len(head)+len(id)+len(s); i < len(s) + len(head) + len(id) + len(name); i++{ content[i] = name[j] j++ } sum := len(s) + len(head) + len(id) + len(name) reciveStr := string(content[:sum]) messages <- reciveStr } } ////////////////////// // //檢測線上使用者 // ////////////////////// func checkOnlineUser(){ var msg string var slen string //for{ for key,_ := range conns { //fmt.Println("connection is connected from ...",key) msg += key; msg += "-"; } l1:=len([]rune(msg)) if l1==0{ return } msg = string(msg[0:l1-1]) slen = strconv.Itoa(l1-1) //fmt.Println(slen) sl := fmt.Sprintf("%4s",slen) // fmt.Println(sl) stype := "1000" msg = sl + stype + msg for _,value := range conns { //fmt.Println("傳送內容",msg) runtime.Gosched(); _,err :=value.Write([]byte(msg)) if(err != nil){ fmt.Println(err.Error(),"checkOnlineUser") //delete(conns,key) }else{ fmt.Println("傳送使用者列表了") } } // } } //////////////////////////////////////////////////////// // //伺服器傳送資料的執行緒 // //引數 // 連線字典 conns // 資料通道 messages // //////////////////////////////////////////////////////// func echoHandler(conns *map[string]net.Conn,messages chan string){ for{ msg:= <- messages for _,value := range *conns { //fmt.Println("connection is connected from ...",key) _,err :=value.Write([]byte(msg)) if(err != nil){ fmt.Println(err.Error(),"123") //delete(*conns,key) } } } } //////////////////////////////////////////////////////// // //啟動伺服器 //引數 // 埠 port // //////////////////////////////////////////////////////// func StartServer(port string){ service:=":"+port //strconv.Itoa(port); tcpAddr, err := net.ResolveTCPAddr("tcp4", service) checkError(err,"ResolveTCPAddr") l,err := net.ListenTCP("tcp",tcpAddr) checkError(err,"ListenTCP") //conns=make(map[string]net.Conn) messages := make(chan string) //imsgstart = 1 //啟動伺服器廣播執行緒 go echoHandler(&conns,messages) for { fmt.Println("msg Listening ...") conn,err := l.Accept() checkError(err,"Accept") fmt.Println("Accepting ...") conns[conn.RemoteAddr().String()]=conn //fmt.Println(conn.RemoteAddr().String()) checkOnlineUser() //啟動一個新執行緒 go Handler(conn,messages) } } func main(){ conns=make(map[string]net.Conn) go StartServer("7777"); fmt.Println("服務啟動完成") for{ } }
// qunliaoDlg.cpp : 實現檔案 // #include "stdafx.h" #include "qunliao.h" #include "qunliaoDlg.h" #include <atlbase.h> #include <boost/format.hpp> #include <boost/tokenizer.hpp> #include <boost/algorithm/string.hpp> #include < boost/locale/encoding.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #define BOOST_DATE_TIME_SOURCE #ifdef _DEBUG #define new DEBUG_NEW #endif // 用於應用程式“關於”選單項的 CAboutDlg 對話方塊 class CAboutDlg : public CDialog { public: CAboutDlg(); // 對話方塊資料 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支援 // 實現 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() // CqunliaoDlg 對話方塊 CqunliaoDlg::CqunliaoDlg(CWnd* pParent /*=NULL*/) : CDialog(CqunliaoDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CqunliaoDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST3, m_ListCtrl); DDX_Control(pDX, IDC_EDIT2, m_EditSendText); DDX_Control(pDX, IDC_LIST1, m_ListMsg); } BEGIN_MESSAGE_MAP(CqunliaoDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP ON_BN_CLICKED(IDC_BUTTON1, &CqunliaoDlg::OnBnClickedButton1) ON_WM_CLOSE() ON_NOTIFY(NM_CUSTOMDRAW, IDC_LIST3, &CqunliaoDlg::OnNMCustomdrawList3) ON_BN_CLICKED(IDC_BUTTON2, &CqunliaoDlg::OnBnClickedButton2) END_MESSAGE_MAP() void CqunliaoDlg::init() { tcp::resolver resolver(io_service); tcp::resolver::query query("127.0.0.1", "7777"); tcp::resolver::iterator iterator = resolver.resolve(query); tcp::resolver resolver2(io_service_user); tcp::resolver::query query2("127.0.0.1", "8888"); tcp::resolver::iterator iterator2 = resolver2.resolve(query2); buser = true; bmsg = true; m_pclient = boost::shared_ptr<chat_client>(new chat_client(io_service, iterator)); //m_puserclient = boost::shared_ptr<chat_client_user>(new chat_client_user(io_service, iterator2)); //m_pusername = boost::shared_ptr<chat_client>(new chat_client(io_service, iterator)); m_pthread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service))); //m_pthread2 = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service_user))); m_pmsgthread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CqunliaoDlg::GetMessage,this))); //m_puserthread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CqunliaoDlg::GetUserList,this))); //m_pusernamethread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CqunliaoDlg::GetUserName,this))); //m_pthread->detach(); //m_pmsgthread->detach(); } // CqunliaoDlg 訊息處理程式 BOOL CqunliaoDlg::OnInitDialog() { CDialog::OnInitDialog(); // 將“關於...”選單項新增到系統選單中。 // IDM_ABOUTBOX 必須在系統命令範圍內。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 設定此對話方塊的圖示。當應用程式主視窗不是對話方塊時,框架將自動 // 執行此操作 SetIcon(m_hIcon, TRUE); // 設定大圖示 SetIcon(m_hIcon, FALSE); // 設定小圖示 // TODO: 在此新增額外的初始化程式碼 m_ListMsg.DeleteAllItems(); m_ListMsg.InsertColumn(0, _T("")); m_ListMsg.SetColumnWidth(0, 200);//設定列寬 m_ListCtrl.DeleteAllItems(); m_ListCtrl.InsertColumn(0, _T("")); m_ListCtrl.SetColumnWidth(0, 160);//設定列寬 init(); return TRUE; // 除非將焦點設定到控制元件,否則返回 TRUE } void CqunliaoDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // 如果向對話方塊新增最小化按鈕,則需要下面的程式碼 // 來繪製該圖示。對於使用文件/檢視模型的 MFC 應用程式, // 這將由框架自動完成。 void CqunliaoDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用於繪製的裝置上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使圖示在工作區矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 繪製圖標 dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } //當用戶拖動最小化視窗時系統呼叫此函式取得游標 //顯示。 HCURSOR CqunliaoDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } //void CqunliaoDlg::GetUserName() //{ // while(true){ // if(m_puserclient->GetChatMessage().body_length()){ // m_strUserName = m_puserclient->GetChatMessage().body(); // m_puserclient->GetChatMessage().clear(); // break; // }else{ // boost::this_thread::sleep(boost::posix_time::seconds(1)); // } // } // m_puserthread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CqunliaoDlg::GetUserList,this))); //} void CqunliaoDlg::GetUserMsg() { std::string strTime = boost::posix_time::to_iso_string( boost::posix_time::second_clock::local_time()); // 這時候strTime裡存放時間的格式是YYYYMMDDTHHMMSS,日期和時間用大寫字母T隔開了 int pos = strTime.find('T'); strTime.replace(pos,1,std::string(" ")); strTime.replace(pos + 3,0,std::string(":")); strTime.replace(pos + 6,0,std::string(":")); strTime.replace(0+4,0,std::string("-")); strTime.replace(0+4+3,0,std::string("-")); //std::string str2(); std::wstring wStr = boost::locale::conv::utf_to_utf<wchar_t>( m_pclient->GetChatMessage().body() ); CString str(wStr.c_str()); CString strtime(strTime.c_str()); int i = m_ListMsg.GetItemCount(); CString col; col.Format(_T("%d"),i); m_ListMsg.InsertItem(i,col); m_ListMsg.SetItemText(i,0,strtime); col.Format(_T("%d"),i+1); m_ListMsg.InsertItem(i+1,col); //str = m_strUserName + "說:" + str; m_ListMsg.SetItemText(i+1,0,str); m_pclient->GetChatMessage().clear(); } void CqunliaoDlg::GetUserList() { { //CString str(); std::vector<std::string> vecSegTag; // boost::is_any_of這裡相當於分割規則了 std::string str = m_pclient->GetChatMessage().body(); boost::split(vecSegTag, str ,boost::is_any_of(_T("-"))); m_ListCtrl.DeleteAllItems(); CString col, strname; //std::string tmp; for(int i = 0; i < vecSegTag.size(); i++){ col.Format(_T("%d"),i); m_ListCtrl.InsertItem(i,col); CString strname(vecSegTag[i].c_str()); m_ListCtrl.SetItemText(i,0,strname); } /*int i = m_ListCtrl.GetItemCount(); CString col; col.Format(_T("%d"),i); m_ListCtrl.InsertItem(i,col); m_ListCtrl.SetItemText(i,0,str);*/ m_pclient->GetChatMessage().clear(); } } void CqunliaoDlg::GetMessage() { while(bmsg){ try{ if(m_pclient->GetChatMessage().body_length()) { std::string msgtype = m_pclient->GetChatMessage().type(); if(msgtype=="1000")//使用者列表 { GetUserList(); }else{ GetUserMsg(); } }else{ boost::this_thread::sleep(boost::posix_time::seconds(1)); } }catch(std::exception e){ return; }catch(...){ return; } } } void CqunliaoDlg::OnBnClickedButton1() { // TODO: 在此新增控制元件通知處理程式程式碼 using namespace std; // For strlen and memcpy. USES_CONVERSION; chat_message msg; CString str; m_EditSendText.GetWindowText(str); if(str.IsEmpty()) { return; } // char *chr=T2A(str); //char *chr = "我"; std::wstring wstr = str.GetString(); string test2=boost::locale::conv::from_utf(wstr,"UTF-8"); //WideCharToMultiByte(CP_ACP,0,str.GetBuffer(),-1,chr,str.GetLength(),NULL,NULL); msg.body_length(test2.length()); memcpy(msg.body(), test2.c_str(), msg.body_length()); msg.encode_header(); msg.msg_type("1001"); m_pclient->write(msg); //delete chr; // chr = NULL; } void CqunliaoDlg::OnClose() { // TODO: 在此新增訊息處理程式程式碼和/或呼叫預設值 m_pthread->interrupt(); //m_pthread2->interrupt(); m_pclient->close(); //m_puserclient->close(); //m_pthread->join(); buser=false; bmsg=false; m_pmsgthread->join(); // m_puserthread->join(); //m_pthread->interrupt(); m_pmsgthread->interrupt(); //m_puserthread->interrupt(); CDialog::OnClose(); } void CqunliaoDlg::OnNMCustomdrawList3(NMHDR *pNMHDR, LRESULT *pResult) { LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); // TODO: 在此新增控制元件通知處理程式程式碼 *pResult = 0; }
boost的asio程式碼
// // chat_client.hpp // ~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include <cstdlib> #include <deque> #include <iostream> #include <boost/array.hpp> #include <boost/bind.hpp> #include <boost/asio.hpp> #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include "chat_message.hpp" using namespace boost::asio; using boost::asio::ip::tcp; typedef std::deque<chat_message> chat_message_queue; class chat_client { public: chat_client(boost::asio::io_service& io_service, tcp::resolver::iterator endpoint_iterator) : io_service_(io_service), socket_(io_service) { data_.assign('\0'); //tcp::endpoint endpoint = *endpoint_iterator; tcp::endpoint endpoint(ip::address_v4::from_string("127.0.0.1"), 7777); socket_.async_connect(endpoint, boost::bind(&chat_client::handle_connect, this, boost::asio::placeholders::error, ++endpoint_iterator)); } void write(const chat_message& msg) { io_service_.post(boost::bind(&chat_client::do_write, this, msg)); } //void write(const std::string& user){ // io_service_.post(boost::bind(&chat_client::do_write, this, user)); //} void close() { io_service_.post(boost::bind(&chat_client::do_close, this)); } public: chat_message& GetChatMessage(){ return read_msg_; } private: void handle_connect(const boost::system::error_code& error, tcp::resolver::iterator endpoint_iterator) { if (!error) { boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.data(), chat_message::header_length), boost::bind(&chat_client::handle_read_header, this, boost::asio::placeholders::error)); } /*else if (endpoint_iterator != tcp::resolver::iterator()) { socket_.close(); tcp::endpoint endpoint = *endpoint_iterator; socket_.async_connect(endpoint, boost::bind(&chat_client::handle_connect, this, boost::asio::placeholders::error, ++endpoint_iterator)); }*/ } void handle_read_header(const boost::system::error_code& error) { /* if(!error &&strcmp(read_msg_.data(),"user")==0){ boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.user(), 1024), boost::bind(&chat_client::handle_read_userlist, this, boost::asio::placeholders::error)); } else */if (!error && read_msg_.decode_header()) { boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.msg_type(), chat_message::msg_type_length), boost::bind(&chat_client::handle_read_msg_type, this, boost::asio::placeholders::error)); } else { do_close(); } } void handle_read_msg_type(const boost::system::error_code& error) { if (!error) { std::cout.write(read_msg_.msg_type(), chat_message::msg_type_length); std::cout << "\n"; boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.body(), read_msg_.body_length()), boost::bind(&chat_client::handle_read_body, this, boost::asio::placeholders::error)); } else { do_close(); } } void handle_read_body(const boost::system::error_code& error) { if (!error) { std::cout.write(read_msg_.body(), read_msg_.body_length()); std::cout << "\n"; boost::asio::async_read(socket_, boost::asio::buffer(read_msg_.data(), chat_message::header_length), boost::bind(&chat_client::handle_read_header, this, boost::asio::placeholders::error)); } else { do_close(); } } void handle_read_userlist(const boost::system::error_code& error) { if (!error) { /*std::cout.write(read_msg_.body(), read_msg_.body_length()); std::cout << "\n";*/ m_userlist.append(data_.data()); data_.assign('\0'); boost::asio::async_read(socket_, boost::asio::buffer(data_), boost::bind(&chat_client::handle_read_userlist, this, boost::asio::placeholders::error)); } else { do_close(); } } void do_write(chat_message msg) { bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), boost::bind(&chat_client::handle_write, this, boost::asio::placeholders::error)); } } //void do_write(std::string& user) //{ // // { // boost::asio::async_write(socket_, // boost::asio::buffer(user, // user.length()), // boost::bind(&chat_client::handle_write, this, // boost::asio::placeholders::error)); // } //} void handle_write(const boost::system::error_code& error) { if (!error) { write_msgs_.pop_front(); if (!write_msgs_.empty()) { boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), boost::bind(&chat_client::handle_write, this, boost::asio::placeholders::error)); } } else { do_close(); } } void do_close() { socket_.close(); } private: boost::asio::io_service& io_service_; tcp::socket socket_; chat_message read_msg_; chat_message_queue write_msgs_; std::string m_userlist; boost::array<char, 1024> data_; }; //int main(int argc, char* argv[]) //{ // try // { // /* if (argc != 3) // { // std::cerr << "Usage: chat_client <host> <port>\n"; // return 1; // }*/ // // boost::asio::io_service io_service; // // boost::asio::ip::tcp::socket socket(io_service); // std::string ip = boost::asio::ip::address().to_string(); // tcp::resolver resolver(io_service); // tcp::resolver::query query("127.0.0.1", "7777"); // tcp::resolver::iterator iterator = resolver.resolve(query); // // //tcp::resolver::iterator end; // End marker. // // while (iterator != end) // // { // // tcp::endpoint ep = *iterator++; // // std::cout << ep.address().to_string() << std::endl; // // std::cout << ep.port() << std::endl; // // } // // getchar(); // chat_client c(io_service, iterator); // // boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service)); // // char line[chat_message::max_body_length + 1]; // while (std::cin.getline(line, chat_message::max_body_length + 1)) // { // using namespace std; // For strlen and memcpy. // chat_message msg; // msg.body_length(strlen(line)); // memcpy(msg.body(), line, msg.body_length()); // msg.encode_header(); // c.write(msg); // } // // c.close(); // t.join(); // } // catch (std::exception& e) // { // std::cerr << "Exception: " << e.what() << "\n"; // } // // return 0; //}
//
// chat_message.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef CHAT_MESSAGE_HPP
#define CHAT_MESSAGE_HPP
#include <cstdio>
#include <cstdlib>
#include <cstring>
class chat_message
{
public:
enum { header_length = 4 };
enum { max_body_length = 2048 };
enum { msg_type_length = 4 };
chat_message()
: body_length_(0)
{
memset(data_,0,header_length + msg_type_length + max_body_length);
memset(msg_type_,0,msg_type_length+1);
}
const char* data() const
{
return data_;
}
char* data()
{
return data_;
}
void clear()
{
memset(data_,0,header_length + msg_type_length + max_body_length);
memset(msg_type_,0,msg_type_length+1);
body_length_ = 0;
}
size_t length() const
{
return header_length + msg_type_length + body_length_;
}
const char* body() const
{
return data_ + header_length + msg_type_length ;
}
char * msg_type(){
return data_ + header_length;
}
const char* type(){
strncat(msg_type_, data_+header_length,msg_type_length );
return msg_type_;
}
char* body()
{
return data_ + header_length+ msg_type_length ;
}
char* user(){
return data_ + header_length+ msg_type_length ;
}
size_t body_length() const
{
return body_length_;
}
void body_length(size_t length)
{
body_length_ = length;
if (body_length_ > max_body_length)
body_length_ = max_body_length;
}
bool decode_header()
{
using namespace std; // For strncat and atoi.
char header[header_length + 1] = "";
strncat(header, data_, header_length);
body_length_ = atoi(header);
if (body_length_ > max_body_length)
{
body_length_ = 0;
return false;
}
return true;
}
void msg_type(std::string stype)
{
memcpy(data_+header_length,stype.c_str(), msg_type_length);
}
void encode_header()
{
using namespace std; // For sprintf and memcpy.
char header[header_length + 1] = "";
sprintf(header, "%4d", body_length_);
memcpy(data_, header, header_length);
}
private:
char data_[header_length + msg_type_length + max_body_length];
char msg_type_[msg_type_length+1];
size_t body_length_;
};
#endif // CHAT_MESSAGE_HPP