Error[8]: Undefined offset: 2065, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

C++ select模型聊天室初版 需要了解知识点 memmove与memcpy的区别

  memmove可以防止内存重叠。

  比如:abcdef,目前需要删除ab,然后将cdef往前移动,有些系统的内存移动机制可能存在从后往前移动,比如先把f移动到d所在位置,然后将e移动到c所在位置,但是当我们要把d移动到b所在位置的时候,我们发现d已经是f了,也就变成了把f移动到b,同理本来是把c移动到a所在位置,却变成了把e移动到a所在位置,最终的数据移动 *** 作变成了efef。

  而memmove可以强制要求从前往后移动数据。

C++中的控制字符’\n’和’\r’ C++11 enum class与enum

参考文章:https://blog.csdn.net/weixin_42817477/article/details/109029172

  枚举类的优势:

  1. 降低命名空间的污染

    以下代码在同一个作用域下

    enum Color{black,white,red};	//black、white、red作用域和color作用域相同
    
    auto white = false;	//错误,white已经被声明过了
    
    enum class Color{black,white,red}; //black、white、red作用域仅在大括号内生效
    
    auto white = false;		//正确,这个white并不是Color中的white
    
    Color c = white;	//错误,在作用域范围内没有white这个枚举量
    
    Color c = Color::white;	//正确
    
    auto c = Color::white;	//正确
    
  2. 避免发生隐式转换

    enum Color{black,white,red};
    std::vector<std::size_t> primeFactors(std::size_t x);	//函数返回x的质因数
    
    Color c = red;
    
    if(c < 14.5)	//将color型别和double型别比较,发生隐式转换
    {
    	auto factors = primeFactors(c);  //计算一个color型别的质因数,发生隐式转换
    }
    
    enum class Color{black,white,red};
    Color c = Color::red;
    
    if(c < 14.5)	//错误,不能将枚举类和double进行比较,需要使用static_cast进行强制转换
    {
    	auto factors = primeFactors(c); //错误,Color不能转化为size_t型别
    }
    
  3. 可以提前声明(强制声明)

    enum Color;			//错误
    enum class Color;	//正确
    
C++ map使用[ ]进行查询 *** 作

参考文章:https://blog.csdn.net/albertsh/article/details/103529462

  map的[ ] *** 作符会有副作用,当查找的键不存在时,会在对应键位置插入默认值。

效果展示

TIPS:以下代码初步完成,未进行优化,仅供参照。

数据包规范 NetHeader.h
#pragma once


enum class ENetHeader
{
	LOGIN_C2S,
	LOGIN_S2C,
	MSG_C2S,
	MSG_S2C,

};


struct SNetHeader
{
	int len;//发送数据包的大小
	ENetHeader type;//数据包的类型
};


struct SLoginC2S
{
	SNetHeader header{ sizeof(SLoginC2S),ENetHeader::LOGIN_C2S };
	char userName[128];
	char password[128];
};


struct SLoginS2C
{
	SNetHeader header{ sizeof(SLoginS2C),ENetHeader::LOGIN_S2C };
	bool bLogin;
};


struct SMsgC2S
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgC2S),ENetHeader::MSG_C2S };
	char msg[1016];
};


struct SMsgS2C
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgS2C),ENetHeader::MSG_S2C };
	char msg[1016];
};
客户端源码 Client.h
// Client.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。

#pragma once // TODO: 在此处引用程序需要的其他标头。

#include #include #include #include #include #include #include "NetHeader.h" #pragma comment(lib,"ws2_32.lib") class CharRoom { private: COORD _coord; public: void CharRoomDemo(const char* userName, const std::vector<std::string>& msg) { _coord.X = 1; _coord.Y = 1; for (auto&& msgItem : msg) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << userName << ":" << msgItem; _coord.Y++; } } void ClsMsg() { _coord.X = 1; _coord.Y = 1; for (int i = 0; i < 10; i++) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; } } }; class Register { private: COORD _coord; SLoginC2S _login; Register(int x = 15, int y = 5) { _coord.X = x; _coord.Y = y; } ~Register() {} public: static Register* GetInstance() { static Register reg; return &reg; } void RegisterDemo() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "*************登录*************"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "用户名:"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "密码:"; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "******************************"; _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void PrintCls() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.X = 1; _coord.Y = 14; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "输入发送信息:"; _coord.Y++; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void UserRegister() { _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); int inputLen = 0; bool left = true; while (1) { if (false == left && inputLen == 0) { _coord.Y = 7; _coord.X = 24; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } char c = getch(); std::cout << c; if (left == true) { _login.userName[inputLen++] = c; if (c == '\r') { _login.userName[inputLen - 1] = ';'= inputLen 0 ;= left false ;} } else . { _login[password++inputLen]= ; cif ( ==c '\r' ). { _login[password-inputLen 1 ]= ';' =0 inputLen ; break; }} } } GetUerMsg ( SLoginC2S )return; { } _login} ; classTcpClient : public CharRoomprivate : { ;char SOCKET _sock* ;int _buffer; int _bufferLen; :: _bufferMaxLen< std::vector;stdpublicstring> msg: bool; public isLoginSuccess: TcpClient( int=1024 bufMaxLen ) =new { _buffer char [ ];bufMaxLen=0 _bufferLen ; =; _bufferMaxLen = bufMaxLenfalse isLoginSuccess ; =; _sock } INVALID_SOCKETbool Connect ( constchar* ,int ip) //判断socket是否合法,即socket如果已经创建过一次,那么不能再次被创建 portif { ( != )_sock return INVALID_SOCKETfalse ; =socket _sock ( ,,AF_INET0 SOCK_STREAM) ;if( == )INVALID_SOCKET closesocket _sock( { );_sock=; _sock return INVALID_SOCKETfalse ; }; . SOCKADDR_IN serverAddr= serverAddr;sin_family . AF_INET. serverAddr=sin_addrinet_addrs_addr ( );ip.= serverAddrhtonssin_port ( );portif( == connectINVALID_SOCKET ( ,(_sock* )sockaddr&,sizeofserverAddr( ))SOCKADDR_IN)closesocket( { );_sock=; _sock return INVALID_SOCKETfalse ; }//std::cout << "连接服务端成功" << std::endl; return true ; }void Update ( )//判断socket是否合法,如果socket没有被创建,那么不能使用update功能if { ( == )_sock return INVALID_SOCKET; ;FD_ZERO FD_SET reads( &);readsFD_SET( ,&_sock) ;reads0, timeval timer{ 1000}; //1000微妙,即1毫秒int= select nRet ( 0,&, nullptrreads, nullptr, &) ;timerif( <= 0nRet ) return; int= recv nRecvLen ( ,+_sock, _buffer - _bufferLen, _bufferMaxLen 0 _bufferLen) ;if( <= 0nRecvLen ) //断开连接Close { ( );return; }+= ; _bufferLen int nRecvLen= OnNetMsg nRetLen ( ,)_buffer; _bufferLenif( < 0nRetLen ) //数据数据包错误,即发送的数据包无法经过检验Close { ( );return; }//没有处理数据包 if ( == 0nRetLen ) return; memmove( ,+_buffer, _buffer - nRetLen) _bufferLen ; nRetLen-=; _bufferLen } nRetLenint OnNetMsg ( constchar* ,int buf) if len( { < sizeoflen ( structSNetHeader) )return0 ; structSNetHeader * =( header struct SNetHeader* );//解决连包问题bufif ( < )len return header->len0 ; switch( ) caseheader->type:: { : ENetHeaderstructLOGIN_S2CSLoginS2C { * =( login struct SLoginS2C* );ifheader( //std::cout << login->bLogin << std::endl; == truelogin->bLogin ) =true isLoginSuccess ; break; }case :: : ENetHeaderstructMSG_S2CSMsgS2C { * =( serverMsg struct SMsgS2C* );ifheader( . sizemsg()==10 ) .erase { msg(.beginmsg());ClsMsg( );}. push_back msg();serverMsg->msgCharRoomDemo( Register::GetInstance()GetUerMsg(->).,)userName; msgbreak; }default : break; }return ; } lenvoid Close ( )if( { == )_sock return INVALID_SOCKET; OnNetMsg( nullptr,0) ;closesocket( );_sock=; _sock } INVALID_SOCKETvoid Send ( constchar* ,int buf) int len= { send nSendLen ( ,,_sock, buf0 len) ;}} ; //#

Client.cpp
// Client.cpp: 定义应用程序的入口点。

include "Client.h"int main ( );WSAStartup { WSADATA wsaData( MAKEWORD(2,2) ,&) ;wsaData;. TcpClient clientConnect client("127.0.0.1",7890) ;Register:: GetInstance()RegisterDemo(->);char[ 1024 inputBuf]0}{ ; int= 0 IBLen ; bool= false isInput ; ;while COORD coord( 1 )if( { . )clientifisLoginSuccess( { ! )=isInputtrue { isInput ; Register:: GetInstance()PrintCls(->);.= coord1X ; .= coord0Y ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord.= coord1X ; .= coord15Y ; ::<< std"接收服务端信息:"cout ; ;. CONSOLE_CURSOR_INFO cursor= cursor;bVisible . FALSE= cursorsizeofdwSize ( );cursorSetConsoleCursorInfo( GetStdHandle(),STD_OUTPUT_HANDLE&) ;cursor}} if ( _kbhit ())if( { ! .)clientRegisterisLoginSuccess:: { GetInstance()UserRegister(->);.Send client((char*)(&Register::GetInstance()GetUerMsg(->)),sizeof( ))SLoginC2S;}else SetConsoleCursorPosition ( { GetStdHandle(),STD_OUTPUT_HANDLE); coordchar= getch c ( );::<< std;cout . c++ coord;X[++ inputBuf]IBLen=; if c( == '\r'c ) .++ { coord;Y.= coord1X ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord[- inputBuf1IBLen ] =';' ; strcpy( SMsgC2S clientMsg. ,)clientMsg;msg. inputBufSend( client(char*)&,sizeof(clientMsg) );SMsgC2S=0; IBLen } }} . Update ( client);}WSACleanup( ) ;return0; } // ChatRoom.h: 标准系统包含文件的包含文件# pragma

服务端源码 ChatRoom.h
once
// 或项目特定的包含文件。

#include # // TODO: 在此处引用程序需要的其他标头。

include# include"NetHeader.h" #include # include# include# pragmacomment (, "ws2_32.lib")libusingnamespace; class TcpConnecter stdpublic : char { [1024 ] _recvBuf;int;int ; _recvLenpublic : _recvMaxLenTcpConnecter () =0; { _recvLen = 1024; _recvMaxLen } }; class TcpServerpublic : TcpServer { () .insert( { _mapmake_pair("xxx","123")) ;.insert( _mapmake_pair("xhh","123")) ;}boolListen ( const char*, int) ip//创建socket = portsocket { ( _serverSock , ,0AF_INET) SOCK_STREAM; if(== ) gotoINVALID_SOCKET ; _serverSock//绑定IP和端口号 ; Exit. . SOCKADDR_IN addr= addrinet_addrsin_addr(s_addr ) ;.ip=; addr.sin_family = AF_INEThtons addr(sin_port ) ;ifport(== bind (INVALID_SOCKET , (*_serverSock) &sockaddr,sizeof(addr) ))SOCKADDR_INgoto;//监听端口 if Exit( == listen (INVALID_SOCKET , 255)_serverSock) goto;return true Exit; : closesocket( Exit) ;=_serverSock;return _serverSock false INVALID_SOCKET; } voidUpdate ( ) ;FD_ZERO( { FD_SET reads& );FD_SETreads(, &)_serverSock; forreads(auto && :)FD_SET clientSock ( _clientSocks, &)clientSock; intreads=select ( nRet 0 ,&,nullptr ,readsnullptr ,nullptr ); if(0 ) ifnRet > (FD_ISSET { ( ,&)_serverSock) Acceptreads() ;elseauto= . { begin begin ( _clientSocks);auto=. end end ( _clientSocks);for(; != ;) begin if end(FD_ISSET { ( *,&)begin) autoreads=. { find iter ( _tcs*);*begin=; TcpConnecterint p = iter->secondrecv ( nRecv * ,+,begin- p->_recvBuf , p->_recvLen0 p->_recvMaxLen ) p->_recvLen; if(<= 0 )nRecv closesocket (* { );OnDisConnectbegin(* );deletebegin;. erase p( _tcs*);=begin.erase begin ( _clientSocks);=begin.end end ( _clientSocks);continue;} +=; int p->_recvLen = nRecvOnNetMsg ( nRet * ,,)begin; p->_recvBufif p->_recvLen(<= 0 )nRet return ;memmove (, +,p->_recvBuf- p->_recvBuf ) nRet; p->_recvLen -= nRet;} p->_recvLen ++ nRet; } }begin} } void Accept ( ) ;int= { SOCKADDR_IN clientAddrsizeof ( clientAddrLen ) ;=clientAddraccept( SOCKET clientSock , (*_serverSock) &sockaddr,&)clientAddr; ifclientAddrLen(!= ) .INVALID_SOCKET push_back clientSock( { _clientSocks);OnConnectclientSock() ;*clientSock=new TcpConnecter; p . insert TcpConnecter( _tcsmake_pair(,))clientSock; p}}void OnConnect ( ) printf(SOCKET clientSock"%d 客户端连接\n" { ,);} clientSockintOnNetMsg ( , constcharSOCKET clientSock* , int) buff/*printf("%d == %s\n", clientSock, buff); for (auto&& clientSock : _clientSocks) send(clientSock, buff, len, 0);*/ if len( { < sizeof (len struct SNetHeader)) return0; struct SNetHeader* = (* header ) ;SNetHeader//解决连包问题ifbuff( < ) returnlen 0 header->len; switch () case ::header->type: { struct ENetHeaderSLoginC2SLOGIN_C2S* { = (* login ) ;SLoginC2Sauto=buff. find iter ( _map);structlogin->userNameSLoginS2C; if ( repurclient!= . enditer ( _map)&&strcmp( . c_str(iter->second),)==0 login->password) . =true repurclient;bLogin else .= false repurclient;bLogin Send (, (charclientSock* )&,sizeof(repurclient) );SLoginS2Cbreak;} case:: : struct ENetHeaderSMsgC2SMSG_C2S* { = (* clientMsg ) ;SMsgC2SstructSMsgS2Cbuff; strcpy ( serverMsg. ,)serverMsg;msgfor clientMsg->msg(auto && :)send clientSock ( _clientSocks, (charclientSock* )&,sizeof(serverMsg) ,0SMsgS2C); break;} default: break ;} return; } void lenOnDisConnect ( ) printf(SOCKET sock"%d==客户端断开连接\n" { ,);} sockvoidSend ( , constcharSOCKET sock* , int) bufsend ( len, { ,,sock0 buf) len; }private: ; <; SOCKET _serverSock< vector,SOCKET> _clientSocks; map<string, string> _map* map;SOCKET} TcpConnecter;> _tcs// #include

ChatRoom.cpp
// ChatRoom.cpp: 定义应用程序的入口点。

"ChatRoom.h" intmain ( ) ;WSAStartup( { WSADATA wsaMAKEWORD (2,2), &); ;wsa.Listen TcpServer server( server"0.0.0.0",7890); while(true ) .Update( server);WSACleanup() ;return0; } [+++][+++] [+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 2066, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

C++ select模型聊天室初版 需要了解知识点 memmove与memcpy的区别

  memmove可以防止内存重叠。

  比如:abcdef,目前需要删除ab,然后将cdef往前移动,有些系统的内存移动机制可能存在从后往前移动,比如先把f移动到d所在位置,然后将e移动到c所在位置,但是当我们要把d移动到b所在位置的时候,我们发现d已经是f了,也就变成了把f移动到b,同理本来是把c移动到a所在位置,却变成了把e移动到a所在位置,最终的数据移动 *** 作变成了efef。

  而memmove可以强制要求从前往后移动数据。

C++中的控制字符’\n’和’\r’ C++11 enum class与enum

参考文章:https://blog.csdn.net/weixin_42817477/article/details/109029172

  枚举类的优势:

  1. 降低命名空间的污染

    以下代码在同一个作用域下

    enum Color{black,white,red};	//black、white、red作用域和color作用域相同
    
    auto white = false;	//错误,white已经被声明过了
    
    enum class Color{black,white,red}; //black、white、red作用域仅在大括号内生效
    
    auto white = false;		//正确,这个white并不是Color中的white
    
    Color c = white;	//错误,在作用域范围内没有white这个枚举量
    
    Color c = Color::white;	//正确
    
    auto c = Color::white;	//正确
    
  2. 避免发生隐式转换

    enum Color{black,white,red};
    std::vector<std::size_t> primeFactors(std::size_t x);	//函数返回x的质因数
    
    Color c = red;
    
    if(c < 14.5)	//将color型别和double型别比较,发生隐式转换
    {
    	auto factors = primeFactors(c);  //计算一个color型别的质因数,发生隐式转换
    }
    
    enum class Color{black,white,red};
    Color c = Color::red;
    
    if(c < 14.5)	//错误,不能将枚举类和double进行比较,需要使用static_cast进行强制转换
    {
    	auto factors = primeFactors(c); //错误,Color不能转化为size_t型别
    }
    
  3. 可以提前声明(强制声明)

    enum Color;			//错误
    enum class Color;	//正确
    
C++ map使用[ ]进行查询 *** 作

参考文章:https://blog.csdn.net/albertsh/article/details/103529462

  map的[ ] *** 作符会有副作用,当查找的键不存在时,会在对应键位置插入默认值。

效果展示

TIPS:以下代码初步完成,未进行优化,仅供参照。

数据包规范 NetHeader.h
#pragma once


enum class ENetHeader
{
	LOGIN_C2S,
	LOGIN_S2C,
	MSG_C2S,
	MSG_S2C,

};


struct SNetHeader
{
	int len;//发送数据包的大小
	ENetHeader type;//数据包的类型
};


struct SLoginC2S
{
	SNetHeader header{ sizeof(SLoginC2S),ENetHeader::LOGIN_C2S };
	char userName[128];
	char password[128];
};


struct SLoginS2C
{
	SNetHeader header{ sizeof(SLoginS2C),ENetHeader::LOGIN_S2C };
	bool bLogin;
};


struct SMsgC2S
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgC2S),ENetHeader::MSG_C2S };
	char msg[1016];
};


struct SMsgS2C
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgS2C),ENetHeader::MSG_S2C };
	char msg[1016];
};
客户端源码 Client.h
// Client.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。

#pragma once // TODO: 在此处引用程序需要的其他标头。

#include #include #include #include #include #include #include "NetHeader.h" #pragma comment(lib,"ws2_32.lib") class CharRoom { private: COORD _coord; public: void CharRoomDemo(const char* userName, const std::vector<std::string>& msg) { _coord.X = 1; _coord.Y = 1; for (auto&& msgItem : msg) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << userName << ":" << msgItem; _coord.Y++; } } void ClsMsg() { _coord.X = 1; _coord.Y = 1; for (int i = 0; i < 10; i++) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; } } }; class Register { private: COORD _coord; SLoginC2S _login; Register(int x = 15, int y = 5) { _coord.X = x; _coord.Y = y; } ~Register() {} public: static Register* GetInstance() { static Register reg; return &reg; } void RegisterDemo() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "*************登录*************"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "用户名:"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "密码:"; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "******************************"; _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void PrintCls() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.X = 1; _coord.Y = 14; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "输入发送信息:"; _coord.Y++; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void UserRegister() { _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); int inputLen = 0; bool left = true; while (1) { if (false == left && inputLen == 0) { _coord.Y = 7; _coord.X = 24; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } char c = getch(); std::cout << c; if (left == true) { _login.userName[inputLen++] = c; if (c == '\r') { _login.userName[inputLen - 1] = ';'= inputLen 0 ;= left false ;} } else . { _login[password++inputLen]= ; cif ( ==c '\r' ). { _login[password-inputLen 1 ]= ';' =0 inputLen ; break; }} } } GetUerMsg ( SLoginC2S )return; { } _login} ; classTcpClient : public CharRoomprivate : { ;char SOCKET _sock* ;int _buffer; int _bufferLen; :: _bufferMaxLen< std::vector;stdpublicstring> msg: bool; public isLoginSuccess: TcpClient( int=1024 bufMaxLen ) =new { _buffer char [ ];bufMaxLen=0 _bufferLen ; =; _bufferMaxLen = bufMaxLenfalse isLoginSuccess ; =; _sock } INVALID_SOCKETbool Connect ( constchar* ,int ip) //判断socket是否合法,即socket如果已经创建过一次,那么不能再次被创建 portif { ( != )_sock return INVALID_SOCKETfalse ; =socket _sock ( ,,AF_INET0 SOCK_STREAM) ;if( == )INVALID_SOCKET closesocket _sock( { );_sock=; _sock return INVALID_SOCKETfalse ; }; . SOCKADDR_IN serverAddr= serverAddr;sin_family . AF_INET. serverAddr=sin_addrinet_addrs_addr ( );ip.= serverAddrhtonssin_port ( );portif( == connectINVALID_SOCKET ( ,(_sock* )sockaddr&,sizeofserverAddr( ))SOCKADDR_IN)closesocket( { );_sock=; _sock return INVALID_SOCKETfalse ; }//std::cout << "连接服务端成功" << std::endl; return true ; }void Update ( )//判断socket是否合法,如果socket没有被创建,那么不能使用update功能if { ( == )_sock return INVALID_SOCKET; ;FD_ZERO FD_SET reads( &);readsFD_SET( ,&_sock) ;reads0, timeval timer{ 1000}; //1000微妙,即1毫秒int= select nRet ( 0,&, nullptrreads, nullptr, &) ;timerif( <= 0nRet ) return; int= recv nRecvLen ( ,+_sock, _buffer - _bufferLen, _bufferMaxLen 0 _bufferLen) ;if( <= 0nRecvLen ) //断开连接Close { ( );return; }+= ; _bufferLen int nRecvLen= OnNetMsg nRetLen ( ,)_buffer; _bufferLenif( < 0nRetLen ) //数据数据包错误,即发送的数据包无法经过检验Close { ( );return; }//没有处理数据包 if ( == 0nRetLen ) return; memmove( ,+_buffer, _buffer - nRetLen) _bufferLen ; nRetLen-=; _bufferLen } nRetLenint OnNetMsg ( constchar* ,int buf) if len( { < sizeoflen ( structSNetHeader) )return0 ; structSNetHeader * =( header struct SNetHeader* );//解决连包问题bufif ( < )len return header->len0 ; switch( ) caseheader->type:: { : ENetHeaderstructLOGIN_S2CSLoginS2C { * =( login struct SLoginS2C* );ifheader( //std::cout << login->bLogin << std::endl; == truelogin->bLogin ) =true isLoginSuccess ; break; }case :: : ENetHeaderstructMSG_S2CSMsgS2C { * =( serverMsg struct SMsgS2C* );ifheader( . sizemsg()==10 ) .erase { msg(.beginmsg());ClsMsg( );}. push_back msg();serverMsg->msgCharRoomDemo( Register::GetInstance()GetUerMsg(->).,)userName; msgbreak; }default : break; }return ; } lenvoid Close ( )if( { == )_sock return INVALID_SOCKET; OnNetMsg( nullptr,0) ;closesocket( );_sock=; _sock } INVALID_SOCKETvoid Send ( constchar* ,int buf) int len= { send nSendLen ( ,,_sock, buf0 len) ;}} ; //#

Client.cpp
// Client.cpp: 定义应用程序的入口点。

include "Client.h"int main ( );WSAStartup { WSADATA wsaData( MAKEWORD(2,2) ,&) ;wsaData;. TcpClient clientConnect client("127.0.0.1",7890) ;Register:: GetInstance()RegisterDemo(->);char[ 1024 inputBuf]0}{ ; int= 0 IBLen ; bool= false isInput ; ;while COORD coord( 1 )if( { . )clientifisLoginSuccess( { ! )=isInputtrue { isInput ; Register:: GetInstance()PrintCls(->);.= coord1X ; .= coord0Y ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord.= coord1X ; .= coord15Y ; ::<< std"接收服务端信息:"cout ; ;. CONSOLE_CURSOR_INFO cursor= cursor;bVisible . FALSE= cursorsizeofdwSize ( );cursorSetConsoleCursorInfo( GetStdHandle(),STD_OUTPUT_HANDLE&) ;cursor}} if ( _kbhit ())if( { ! .)clientRegisterisLoginSuccess:: { GetInstance()UserRegister(->);.Send client((char*)(&Register::GetInstance()GetUerMsg(->)),sizeof( ))SLoginC2S;}else SetConsoleCursorPosition ( { GetStdHandle(),STD_OUTPUT_HANDLE); coordchar= getch c ( );::<< std;cout . c++ coord;X[++ inputBuf]IBLen=; if c( == '\r'c ) .++ { coord;Y.= coord1X ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord[- inputBuf1IBLen ] =';' ; strcpy( SMsgC2S clientMsg. ,)clientMsg;msg. inputBufSend( client(char*)&,sizeof(clientMsg) );SMsgC2S=0; IBLen } }} . Update ( client);}WSACleanup( ) ;return0; } // ChatRoom.h: 标准系统包含文件的包含文件# pragma

服务端源码 ChatRoom.h
once
// 或项目特定的包含文件。

#include # // TODO: 在此处引用程序需要的其他标头。

include# include"NetHeader.h" #include # include# include# pragmacomment (, "ws2_32.lib")libusingnamespace; class TcpConnecter stdpublic : char { [1024 ] _recvBuf;int;int ; _recvLenpublic : _recvMaxLenTcpConnecter () =0; { _recvLen = 1024; _recvMaxLen } }; class TcpServerpublic : TcpServer { () .insert( { _mapmake_pair("xxx","123")) ;.insert( _mapmake_pair("xhh","123")) ;}boolListen ( const char*, int) ip//创建socket = portsocket { ( _serverSock , ,0AF_INET) SOCK_STREAM; if(== ) gotoINVALID_SOCKET ; _serverSock//绑定IP和端口号 ; Exit. . SOCKADDR_IN addr= addrinet_addrsin_addr(s_addr ) ;.ip=; addr.sin_family = AF_INEThtons addr(sin_port ) ;ifport(== bind (INVALID_SOCKET , (*_serverSock) &sockaddr,sizeof(addr) ))SOCKADDR_INgoto;//监听端口 if Exit( == listen (INVALID_SOCKET , 255)_serverSock) goto;return true Exit; : closesocket( Exit) ;=_serverSock;return _serverSock false INVALID_SOCKET; } voidUpdate ( ) ;FD_ZERO( { FD_SET reads& );FD_SETreads(, &)_serverSock; forreads(auto && :)FD_SET clientSock ( _clientSocks, &)clientSock; intreads=select ( nRet 0 ,&,nullptr ,readsnullptr ,nullptr ); if(0 ) ifnRet > (FD_ISSET { ( ,&)_serverSock) Acceptreads() ;elseauto= . { begin begin ( _clientSocks);auto=. end end ( _clientSocks);for(; != ;) begin if end(FD_ISSET { ( *,&)begin) autoreads=. { find iter ( _tcs*);*begin=; TcpConnecterint p = iter->secondrecv ( nRecv * ,+,begin- p->_recvBuf , p->_recvLen0 p->_recvMaxLen ) p->_recvLen; if(<= 0 )nRecv closesocket (* { );OnDisConnectbegin(* );deletebegin;. erase p( _tcs*);=begin.erase begin ( _clientSocks);=begin.end end ( _clientSocks);continue;} +=; int p->_recvLen = nRecvOnNetMsg ( nRet * ,,)begin; p->_recvBufif p->_recvLen(<= 0 )nRet return ;memmove (, +,p->_recvBuf- p->_recvBuf ) nRet; p->_recvLen -= nRet;} p->_recvLen ++ nRet; } }begin} } void Accept ( ) ;int= { SOCKADDR_IN clientAddrsizeof ( clientAddrLen ) ;=clientAddraccept( SOCKET clientSock , (*_serverSock) &sockaddr,&)clientAddr; ifclientAddrLen(!= ) .INVALID_SOCKET push_back clientSock( { _clientSocks);OnConnectclientSock() ;*clientSock=new TcpConnecter; p . insert TcpConnecter( _tcsmake_pair(,))clientSock; p}}void OnConnect ( ) printf(SOCKET clientSock"%d 客户端连接\n" { ,);} clientSockintOnNetMsg ( , constcharSOCKET clientSock* , int) buff/*printf("%d == %s\n", clientSock, buff); for (auto&& clientSock : _clientSocks) send(clientSock, buff, len, 0);*/ if len( { < sizeof (len struct SNetHeader)) return0; struct SNetHeader* = (* header ) ;SNetHeader//解决连包问题ifbuff( < ) returnlen 0 header->len; switch () case ::header->type: { struct ENetHeaderSLoginC2SLOGIN_C2S* { = (* login ) ;SLoginC2Sauto=buff. find iter ( _map);structlogin->userNameSLoginS2C; if ( repurclient!= . enditer ( _map)&&strcmp( . c_str(iter->second),)==0 login->password) . =true repurclient;bLogin else .= false repurclient;bLogin Send (, (charclientSock* )&,sizeof(repurclient) );SLoginS2Cbreak;} case:: : struct ENetHeaderSMsgC2SMSG_C2S* { = (* clientMsg ) ;SMsgC2SstructSMsgS2Cbuff; strcpy ( serverMsg. ,)serverMsg;msgfor clientMsg->msg(auto && :)send clientSock ( _clientSocks, (charclientSock* )&,sizeof(serverMsg) ,0SMsgS2C); break;} default: break ;} return; } void lenOnDisConnect ( ) printf(SOCKET sock"%d==客户端断开连接\n" { ,);} sockvoidSend ( , constcharSOCKET sock* , int) bufsend ( len, { ,,sock0 buf) len; }private: ; <; SOCKET _serverSock< vector,SOCKET> _clientSocks; map<string, string> _map* map;SOCKET} TcpConnecter;> _tcs// #include

ChatRoom.cpp
// ChatRoom.cpp: 定义应用程序的入口点。

"ChatRoom.h" intmain ( ) ;WSAStartup( { WSADATA wsaMAKEWORD (2,2), &); ;wsa.Listen TcpServer server( server"0.0.0.0",7890); while(true ) .Update( server);WSACleanup() ;return0; } [+++] [+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 2067, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

C++ select模型聊天室初版 需要了解知识点 memmove与memcpy的区别

  memmove可以防止内存重叠。

  比如:abcdef,目前需要删除ab,然后将cdef往前移动,有些系统的内存移动机制可能存在从后往前移动,比如先把f移动到d所在位置,然后将e移动到c所在位置,但是当我们要把d移动到b所在位置的时候,我们发现d已经是f了,也就变成了把f移动到b,同理本来是把c移动到a所在位置,却变成了把e移动到a所在位置,最终的数据移动 *** 作变成了efef。

  而memmove可以强制要求从前往后移动数据。

C++中的控制字符’\n’和’\r’ C++11 enum class与enum

参考文章:https://blog.csdn.net/weixin_42817477/article/details/109029172

  枚举类的优势:

  1. 降低命名空间的污染

    以下代码在同一个作用域下

    enum Color{black,white,red};	//black、white、red作用域和color作用域相同
    
    auto white = false;	//错误,white已经被声明过了
    
    enum class Color{black,white,red}; //black、white、red作用域仅在大括号内生效
    
    auto white = false;		//正确,这个white并不是Color中的white
    
    Color c = white;	//错误,在作用域范围内没有white这个枚举量
    
    Color c = Color::white;	//正确
    
    auto c = Color::white;	//正确
    
  2. 避免发生隐式转换

    enum Color{black,white,red};
    std::vector<std::size_t> primeFactors(std::size_t x);	//函数返回x的质因数
    
    Color c = red;
    
    if(c < 14.5)	//将color型别和double型别比较,发生隐式转换
    {
    	auto factors = primeFactors(c);  //计算一个color型别的质因数,发生隐式转换
    }
    
    enum class Color{black,white,red};
    Color c = Color::red;
    
    if(c < 14.5)	//错误,不能将枚举类和double进行比较,需要使用static_cast进行强制转换
    {
    	auto factors = primeFactors(c); //错误,Color不能转化为size_t型别
    }
    
  3. 可以提前声明(强制声明)

    enum Color;			//错误
    enum class Color;	//正确
    
C++ map使用[ ]进行查询 *** 作

参考文章:https://blog.csdn.net/albertsh/article/details/103529462

  map的[ ] *** 作符会有副作用,当查找的键不存在时,会在对应键位置插入默认值。

效果展示

TIPS:以下代码初步完成,未进行优化,仅供参照。

数据包规范 NetHeader.h
#pragma once


enum class ENetHeader
{
	LOGIN_C2S,
	LOGIN_S2C,
	MSG_C2S,
	MSG_S2C,

};


struct SNetHeader
{
	int len;//发送数据包的大小
	ENetHeader type;//数据包的类型
};


struct SLoginC2S
{
	SNetHeader header{ sizeof(SLoginC2S),ENetHeader::LOGIN_C2S };
	char userName[128];
	char password[128];
};


struct SLoginS2C
{
	SNetHeader header{ sizeof(SLoginS2C),ENetHeader::LOGIN_S2C };
	bool bLogin;
};


struct SMsgC2S
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgC2S),ENetHeader::MSG_C2S };
	char msg[1016];
};


struct SMsgS2C
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgS2C),ENetHeader::MSG_S2C };
	char msg[1016];
};
客户端源码 Client.h
// Client.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。

#pragma once // TODO: 在此处引用程序需要的其他标头。

#include #include #include #include #include #include #include "NetHeader.h" #pragma comment(lib,"ws2_32.lib") class CharRoom { private: COORD _coord; public: void CharRoomDemo(const char* userName, const std::vector<std::string>& msg) { _coord.X = 1; _coord.Y = 1; for (auto&& msgItem : msg) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << userName << ":" << msgItem; _coord.Y++; } } void ClsMsg() { _coord.X = 1; _coord.Y = 1; for (int i = 0; i < 10; i++) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; } } }; class Register { private: COORD _coord; SLoginC2S _login; Register(int x = 15, int y = 5) { _coord.X = x; _coord.Y = y; } ~Register() {} public: static Register* GetInstance() { static Register reg; return &reg; } void RegisterDemo() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "*************登录*************"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "用户名:"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "密码:"; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "******************************"; _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void PrintCls() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.X = 1; _coord.Y = 14; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "输入发送信息:"; _coord.Y++; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void UserRegister() { _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); int inputLen = 0; bool left = true; while (1) { if (false == left && inputLen == 0) { _coord.Y = 7; _coord.X = 24; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } char c = getch(); std::cout << c; if (left == true) { _login.userName[inputLen++] = c; if (c == '\r') { _login.userName[inputLen - 1] = ';'= inputLen 0 ;= left false ;} } else . { _login[password++inputLen]= ; cif ( ==c '\r' ). { _login[password-inputLen 1 ]= ';' =0 inputLen ; break; }} } } GetUerMsg ( SLoginC2S )return; { } _login} ; classTcpClient : public CharRoomprivate : { ;char SOCKET _sock* ;int _buffer; int _bufferLen; :: _bufferMaxLen< std::vector;stdpublicstring> msg: bool; public isLoginSuccess: TcpClient( int=1024 bufMaxLen ) =new { _buffer char [ ];bufMaxLen=0 _bufferLen ; =; _bufferMaxLen = bufMaxLenfalse isLoginSuccess ; =; _sock } INVALID_SOCKETbool Connect ( constchar* ,int ip) //判断socket是否合法,即socket如果已经创建过一次,那么不能再次被创建 portif { ( != )_sock return INVALID_SOCKETfalse ; =socket _sock ( ,,AF_INET0 SOCK_STREAM) ;if( == )INVALID_SOCKET closesocket _sock( { );_sock=; _sock return INVALID_SOCKETfalse ; }; . SOCKADDR_IN serverAddr= serverAddr;sin_family . AF_INET. serverAddr=sin_addrinet_addrs_addr ( );ip.= serverAddrhtonssin_port ( );portif( == connectINVALID_SOCKET ( ,(_sock* )sockaddr&,sizeofserverAddr( ))SOCKADDR_IN)closesocket( { );_sock=; _sock return INVALID_SOCKETfalse ; }//std::cout << "连接服务端成功" << std::endl; return true ; }void Update ( )//判断socket是否合法,如果socket没有被创建,那么不能使用update功能if { ( == )_sock return INVALID_SOCKET; ;FD_ZERO FD_SET reads( &);readsFD_SET( ,&_sock) ;reads0, timeval timer{ 1000}; //1000微妙,即1毫秒int= select nRet ( 0,&, nullptrreads, nullptr, &) ;timerif( <= 0nRet ) return; int= recv nRecvLen ( ,+_sock, _buffer - _bufferLen, _bufferMaxLen 0 _bufferLen) ;if( <= 0nRecvLen ) //断开连接Close { ( );return; }+= ; _bufferLen int nRecvLen= OnNetMsg nRetLen ( ,)_buffer; _bufferLenif( < 0nRetLen ) //数据数据包错误,即发送的数据包无法经过检验Close { ( );return; }//没有处理数据包 if ( == 0nRetLen ) return; memmove( ,+_buffer, _buffer - nRetLen) _bufferLen ; nRetLen-=; _bufferLen } nRetLenint OnNetMsg ( constchar* ,int buf) if len( { < sizeoflen ( structSNetHeader) )return0 ; structSNetHeader * =( header struct SNetHeader* );//解决连包问题bufif ( < )len return header->len0 ; switch( ) caseheader->type:: { : ENetHeaderstructLOGIN_S2CSLoginS2C { * =( login struct SLoginS2C* );ifheader( //std::cout << login->bLogin << std::endl; == truelogin->bLogin ) =true isLoginSuccess ; break; }case :: : ENetHeaderstructMSG_S2CSMsgS2C { * =( serverMsg struct SMsgS2C* );ifheader( . sizemsg()==10 ) .erase { msg(.beginmsg());ClsMsg( );}. push_back msg();serverMsg->msgCharRoomDemo( Register::GetInstance()GetUerMsg(->).,)userName; msgbreak; }default : break; }return ; } lenvoid Close ( )if( { == )_sock return INVALID_SOCKET; OnNetMsg( nullptr,0) ;closesocket( );_sock=; _sock } INVALID_SOCKETvoid Send ( constchar* ,int buf) int len= { send nSendLen ( ,,_sock, buf0 len) ;}} ; //#

Client.cpp
// Client.cpp: 定义应用程序的入口点。

include "Client.h"int main ( );WSAStartup { WSADATA wsaData( MAKEWORD(2,2) ,&) ;wsaData;. TcpClient clientConnect client("127.0.0.1",7890) ;Register:: GetInstance()RegisterDemo(->);char[ 1024 inputBuf]0}{ ; int= 0 IBLen ; bool= false isInput ; ;while COORD coord( 1 )if( { . )clientifisLoginSuccess( { ! )=isInputtrue { isInput ; Register:: GetInstance()PrintCls(->);.= coord1X ; .= coord0Y ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord.= coord1X ; .= coord15Y ; ::<< std"接收服务端信息:"cout ; ;. CONSOLE_CURSOR_INFO cursor= cursor;bVisible . FALSE= cursorsizeofdwSize ( );cursorSetConsoleCursorInfo( GetStdHandle(),STD_OUTPUT_HANDLE&) ;cursor}} if ( _kbhit ())if( { ! .)clientRegisterisLoginSuccess:: { GetInstance()UserRegister(->);.Send client((char*)(&Register::GetInstance()GetUerMsg(->)),sizeof( ))SLoginC2S;}else SetConsoleCursorPosition ( { GetStdHandle(),STD_OUTPUT_HANDLE); coordchar= getch c ( );::<< std;cout . c++ coord;X[++ inputBuf]IBLen=; if c( == '\r'c ) .++ { coord;Y.= coord1X ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord[- inputBuf1IBLen ] =';' ; strcpy( SMsgC2S clientMsg. ,)clientMsg;msg. inputBufSend( client(char*)&,sizeof(clientMsg) );SMsgC2S=0; IBLen } }} . Update ( client);}WSACleanup( ) ;return0; } // ChatRoom.h: 标准系统包含文件的包含文件# pragma

服务端源码 ChatRoom.h
once
// 或项目特定的包含文件。

#include # // TODO: 在此处引用程序需要的其他标头。

include# include"NetHeader.h" #include # include# include# pragmacomment (, "ws2_32.lib")libusingnamespace; class TcpConnecter stdpublic : char { [1024 ] _recvBuf;int;int ; _recvLenpublic : _recvMaxLenTcpConnecter () =0; { _recvLen = 1024; _recvMaxLen } }; class TcpServerpublic : TcpServer { () .insert( { _mapmake_pair("xxx","123")) ;.insert( _mapmake_pair("xhh","123")) ;}boolListen ( const char*, int) ip//创建socket = portsocket { ( _serverSock , ,0AF_INET) SOCK_STREAM; if(== ) gotoINVALID_SOCKET ; _serverSock//绑定IP和端口号 ; Exit. . SOCKADDR_IN addr= addrinet_addrsin_addr(s_addr ) ;.ip=; addr.sin_family = AF_INEThtons addr(sin_port ) ;ifport(== bind (INVALID_SOCKET , (*_serverSock) &sockaddr,sizeof(addr) ))SOCKADDR_INgoto;//监听端口 if Exit( == listen (INVALID_SOCKET , 255)_serverSock) goto;return true Exit; : closesocket( Exit) ;=_serverSock;return _serverSock false INVALID_SOCKET; } voidUpdate ( ) ;FD_ZERO( { FD_SET reads& );FD_SETreads(, &)_serverSock; forreads(auto && :)FD_SET clientSock ( _clientSocks, &)clientSock; intreads=select ( nRet 0 ,&,nullptr ,readsnullptr ,nullptr ); if(0 ) ifnRet > (FD_ISSET { ( ,&)_serverSock) Acceptreads() ;elseauto= . { begin begin ( _clientSocks);auto=. end end ( _clientSocks);for(; != ;) begin if end(FD_ISSET { ( *,&)begin) autoreads=. { find iter ( _tcs*);*begin=; TcpConnecterint p = iter->secondrecv ( nRecv * ,+,begin- p->_recvBuf , p->_recvLen0 p->_recvMaxLen ) p->_recvLen; if(<= 0 )nRecv closesocket (* { );OnDisConnectbegin(* );deletebegin;. erase p( _tcs*);=begin.erase begin ( _clientSocks);=begin.end end ( _clientSocks);continue;} +=; int p->_recvLen = nRecvOnNetMsg ( nRet * ,,)begin; p->_recvBufif p->_recvLen(<= 0 )nRet return ;memmove (, +,p->_recvBuf- p->_recvBuf ) nRet; p->_recvLen -= nRet;} p->_recvLen ++ nRet; } }begin} } void Accept ( ) ;int= { SOCKADDR_IN clientAddrsizeof ( clientAddrLen ) ;=clientAddraccept( SOCKET clientSock , (*_serverSock) &sockaddr,&)clientAddr; ifclientAddrLen(!= ) .INVALID_SOCKET push_back clientSock( { _clientSocks);OnConnectclientSock() ;*clientSock=new TcpConnecter; p . insert TcpConnecter( _tcsmake_pair(,))clientSock; p}}void OnConnect ( ) printf(SOCKET clientSock"%d 客户端连接\n" { ,);} clientSockintOnNetMsg ( , constcharSOCKET clientSock* , int) buff/*printf("%d == %s\n", clientSock, buff); for (auto&& clientSock : _clientSocks) send(clientSock, buff, len, 0);*/ if len( { < sizeof (len struct SNetHeader)) return0; struct SNetHeader* = (* header ) ;SNetHeader//解决连包问题ifbuff( < ) returnlen 0 header->len; switch () case ::header->type: { struct ENetHeaderSLoginC2SLOGIN_C2S* { = (* login ) ;SLoginC2Sauto=buff. find iter ( _map);structlogin->userNameSLoginS2C; if ( repurclient!= . enditer ( _map)&&strcmp( . c_str(iter->second),)==0 login->password) . =true repurclient;bLogin else .= false repurclient;bLogin Send (, (charclientSock* )&,sizeof(repurclient) );SLoginS2Cbreak;} case:: : struct ENetHeaderSMsgC2SMSG_C2S* { = (* clientMsg ) ;SMsgC2SstructSMsgS2Cbuff; strcpy ( serverMsg. ,)serverMsg;msgfor clientMsg->msg(auto && :)send clientSock ( _clientSocks, (charclientSock* )&,sizeof(serverMsg) ,0SMsgS2C); break;} default: break ;} return; } void lenOnDisConnect ( ) printf(SOCKET sock"%d==客户端断开连接\n" { ,);} sockvoidSend ( , constcharSOCKET sock* , int) bufsend ( len, { ,,sock0 buf) len; }private: ; <; SOCKET _serverSock< vector,SOCKET> _clientSocks; map<string, string> _map* map;SOCKET} TcpConnecter;> _tcs// #include

ChatRoom.cpp
// ChatRoom.cpp: 定义应用程序的入口点。

"ChatRoom.h" intmain ( ) ;WSAStartup( { WSADATA wsaMAKEWORD (2,2), &); ;wsa.Listen TcpServer server( server"0.0.0.0",7890); while(true ) .Update( server);WSACleanup() ;return0; } [+++]

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
C++ select模型聊天室初版_C_内存溢出

C++ select模型聊天室初版

C++ select模型聊天室初版,第1张

C++ select模型聊天室初版 需要了解知识点 memmove与memcpy的区别

  memmove可以防止内存重叠。

  比如:abcdef,目前需要删除ab,然后将cdef往前移动,有些系统的内存移动机制可能存在从后往前移动,比如先把f移动到d所在位置,然后将e移动到c所在位置,但是当我们要把d移动到b所在位置的时候,我们发现d已经是f了,也就变成了把f移动到b,同理本来是把c移动到a所在位置,却变成了把e移动到a所在位置,最终的数据移动 *** 作变成了efef。

  而memmove可以强制要求从前往后移动数据。

C++中的控制字符’\n’和’\r’
  • ASCII码不同,\n为10,而\r是13。

  • 在Windows系统中,\r只回车不换行,\n是换行。

    在有些系统中,单独的\n是不换行的。

C++11 enum class与enum

参考文章:https://blog.csdn.net/weixin_42817477/article/details/109029172

  枚举类的优势:

  1. 降低命名空间的污染

    以下代码在同一个作用域下

    enum Color{black,white,red};	//black、white、red作用域和color作用域相同
    
    auto white = false;	//错误,white已经被声明过了
    
    enum class Color{black,white,red}; //black、white、red作用域仅在大括号内生效
    
    auto white = false;		//正确,这个white并不是Color中的white
    
    Color c = white;	//错误,在作用域范围内没有white这个枚举量
    
    Color c = Color::white;	//正确
    
    auto c = Color::white;	//正确
    
  2. 避免发生隐式转换

    enum Color{black,white,red};
    std::vector<std::size_t> primeFactors(std::size_t x);	//函数返回x的质因数
    
    Color c = red;
    
    if(c < 14.5)	//将color型别和double型别比较,发生隐式转换
    {
    	auto factors = primeFactors(c);  //计算一个color型别的质因数,发生隐式转换
    }
    
    enum class Color{black,white,red};
    Color c = Color::red;
    
    if(c < 14.5)	//错误,不能将枚举类和double进行比较,需要使用static_cast进行强制转换
    {
    	auto factors = primeFactors(c); //错误,Color不能转化为size_t型别
    }
    
  3. 可以提前声明(强制声明)

    enum Color;			//错误
    enum class Color;	//正确
    
C++ map使用[ ]进行查询 *** 作

参考文章:https://blog.csdn.net/albertsh/article/details/103529462

  map的[ ] *** 作符会有副作用,当查找的键不存在时,会在对应键位置插入默认值。

效果展示

TIPS:以下代码初步完成,未进行优化,仅供参照。

数据包规范 NetHeader.h
#pragma once


enum class ENetHeader
{
	LOGIN_C2S,
	LOGIN_S2C,
	MSG_C2S,
	MSG_S2C,

};


struct SNetHeader
{
	int len;//发送数据包的大小
	ENetHeader type;//数据包的类型
};


struct SLoginC2S
{
	SNetHeader header{ sizeof(SLoginC2S),ENetHeader::LOGIN_C2S };
	char userName[128];
	char password[128];
};


struct SLoginS2C
{
	SNetHeader header{ sizeof(SLoginS2C),ENetHeader::LOGIN_S2C };
	bool bLogin;
};


struct SMsgC2S
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgC2S),ENetHeader::MSG_C2S };
	char msg[1016];
};


struct SMsgS2C
{
	//总计1024字节,与服务端和客户端的缓存容量相同
	SNetHeader header{ sizeof(SMsgS2C),ENetHeader::MSG_S2C };
	char msg[1016];
};
客户端源码 Client.h
// Client.h: 标准系统包含文件的包含文件
// 或项目特定的包含文件。

#pragma once // TODO: 在此处引用程序需要的其他标头。

#include #include #include #include #include #include #include "NetHeader.h" #pragma comment(lib,"ws2_32.lib") class CharRoom { private: COORD _coord; public: void CharRoomDemo(const char* userName, const std::vector<std::string>& msg) { _coord.X = 1; _coord.Y = 1; for (auto&& msgItem : msg) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << userName << ":" << msgItem; _coord.Y++; } } void ClsMsg() { _coord.X = 1; _coord.Y = 1; for (int i = 0; i < 10; i++) { SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; } } }; class Register { private: COORD _coord; SLoginC2S _login; Register(int x = 15, int y = 5) { _coord.X = x; _coord.Y = y; } ~Register() {} public: static Register* GetInstance() { static Register reg; return &reg; } void RegisterDemo() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "*************登录*************"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "用户名:"; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "密码:"; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "******************************"; _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void PrintCls() { _coord.X = 15; _coord.Y = 5; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 18; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.Y++; _coord.X = 15; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << " "; _coord.X = 1; _coord.Y = 14; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); std::cout << "输入发送信息:"; _coord.Y++; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } void UserRegister() { _coord.Y = 6; _coord.X = 26; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); int inputLen = 0; bool left = true; while (1) { if (false == left && inputLen == 0) { _coord.Y = 7; _coord.X = 24; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), _coord); } char c = getch(); std::cout << c; if (left == true) { _login.userName[inputLen++] = c; if (c == '\r') { _login.userName[inputLen - 1] = ';'= inputLen 0 ;= left false ;} } else . { _login[password++inputLen]= ; cif ( ==c '\r' ). { _login[password-inputLen 1 ]= ';' =0 inputLen ; break; }} } } GetUerMsg ( SLoginC2S )return; { } _login} ; classTcpClient : public CharRoomprivate : { ;char SOCKET _sock* ;int _buffer; int _bufferLen; :: _bufferMaxLen< std::vector;stdpublicstring> msg: bool; public isLoginSuccess: TcpClient( int=1024 bufMaxLen ) =new { _buffer char [ ];bufMaxLen=0 _bufferLen ; =; _bufferMaxLen = bufMaxLenfalse isLoginSuccess ; =; _sock } INVALID_SOCKETbool Connect ( constchar* ,int ip) //判断socket是否合法,即socket如果已经创建过一次,那么不能再次被创建 portif { ( != )_sock return INVALID_SOCKETfalse ; =socket _sock ( ,,AF_INET0 SOCK_STREAM) ;if( == )INVALID_SOCKET closesocket _sock( { );_sock=; _sock return INVALID_SOCKETfalse ; }; . SOCKADDR_IN serverAddr= serverAddr;sin_family . AF_INET. serverAddr=sin_addrinet_addrs_addr ( );ip.= serverAddrhtonssin_port ( );portif( == connectINVALID_SOCKET ( ,(_sock* )sockaddr&,sizeofserverAddr( ))SOCKADDR_IN)closesocket( { );_sock=; _sock return INVALID_SOCKETfalse ; }//std::cout << "连接服务端成功" << std::endl; return true ; }void Update ( )//判断socket是否合法,如果socket没有被创建,那么不能使用update功能if { ( == )_sock return INVALID_SOCKET; ;FD_ZERO FD_SET reads( &);readsFD_SET( ,&_sock) ;reads0, timeval timer{ 1000}; //1000微妙,即1毫秒int= select nRet ( 0,&, nullptrreads, nullptr, &) ;timerif( <= 0nRet ) return; int= recv nRecvLen ( ,+_sock, _buffer - _bufferLen, _bufferMaxLen 0 _bufferLen) ;if( <= 0nRecvLen ) //断开连接Close { ( );return; }+= ; _bufferLen int nRecvLen= OnNetMsg nRetLen ( ,)_buffer; _bufferLenif( < 0nRetLen ) //数据数据包错误,即发送的数据包无法经过检验Close { ( );return; }//没有处理数据包 if ( == 0nRetLen ) return; memmove( ,+_buffer, _buffer - nRetLen) _bufferLen ; nRetLen-=; _bufferLen } nRetLenint OnNetMsg ( constchar* ,int buf) if len( { < sizeoflen ( structSNetHeader) )return0 ; structSNetHeader * =( header struct SNetHeader* );//解决连包问题bufif ( < )len return header->len0 ; switch( ) caseheader->type:: { : ENetHeaderstructLOGIN_S2CSLoginS2C { * =( login struct SLoginS2C* );ifheader( //std::cout << login->bLogin << std::endl; == truelogin->bLogin ) =true isLoginSuccess ; break; }case :: : ENetHeaderstructMSG_S2CSMsgS2C { * =( serverMsg struct SMsgS2C* );ifheader( . sizemsg()==10 ) .erase { msg(.beginmsg());ClsMsg( );}. push_back msg();serverMsg->msgCharRoomDemo( Register::GetInstance()GetUerMsg(->).,)userName; msgbreak; }default : break; }return ; } lenvoid Close ( )if( { == )_sock return INVALID_SOCKET; OnNetMsg( nullptr,0) ;closesocket( );_sock=; _sock } INVALID_SOCKETvoid Send ( constchar* ,int buf) int len= { send nSendLen ( ,,_sock, buf0 len) ;}} ; //#

Client.cpp
// Client.cpp: 定义应用程序的入口点。

include "Client.h"int main ( );WSAStartup { WSADATA wsaData( MAKEWORD(2,2) ,&) ;wsaData;. TcpClient clientConnect client("127.0.0.1",7890) ;Register:: GetInstance()RegisterDemo(->);char[ 1024 inputBuf]0}{ ; int= 0 IBLen ; bool= false isInput ; ;while COORD coord( 1 )if( { . )clientifisLoginSuccess( { ! )=isInputtrue { isInput ; Register:: GetInstance()PrintCls(->);.= coord1X ; .= coord0Y ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord.= coord1X ; .= coord15Y ; ::<< std"接收服务端信息:"cout ; ;. CONSOLE_CURSOR_INFO cursor= cursor;bVisible . FALSE= cursorsizeofdwSize ( );cursorSetConsoleCursorInfo( GetStdHandle(),STD_OUTPUT_HANDLE&) ;cursor}} if ( _kbhit ())if( { ! .)clientRegisterisLoginSuccess:: { GetInstance()UserRegister(->);.Send client((char*)(&Register::GetInstance()GetUerMsg(->)),sizeof( ))SLoginC2S;}else SetConsoleCursorPosition ( { GetStdHandle(),STD_OUTPUT_HANDLE); coordchar= getch c ( );::<< std;cout . c++ coord;X[++ inputBuf]IBLen=; if c( == '\r'c ) .++ { coord;Y.= coord1X ; SetConsoleCursorPosition( GetStdHandle(),STD_OUTPUT_HANDLE); coord[- inputBuf1IBLen ] =';' ; strcpy( SMsgC2S clientMsg. ,)clientMsg;msg. inputBufSend( client(char*)&,sizeof(clientMsg) );SMsgC2S=0; IBLen } }} . Update ( client);}WSACleanup( ) ;return0; } // ChatRoom.h: 标准系统包含文件的包含文件# pragma

服务端源码 ChatRoom.h
once
// 或项目特定的包含文件。

#include # // TODO: 在此处引用程序需要的其他标头。

include# include"NetHeader.h" #include # include# include# pragmacomment (, "ws2_32.lib")libusingnamespace; class TcpConnecter stdpublic : char { [1024 ] _recvBuf;int;int ; _recvLenpublic : _recvMaxLenTcpConnecter () =0; { _recvLen = 1024; _recvMaxLen } }; class TcpServerpublic : TcpServer { () .insert( { _mapmake_pair("xxx","123")) ;.insert( _mapmake_pair("xhh","123")) ;}boolListen ( const char*, int) ip//创建socket = portsocket { ( _serverSock , ,0AF_INET) SOCK_STREAM; if(== ) gotoINVALID_SOCKET ; _serverSock//绑定IP和端口号 ; Exit. . SOCKADDR_IN addr= addrinet_addrsin_addr(s_addr ) ;.ip=; addr.sin_family = AF_INEThtons addr(sin_port ) ;ifport(== bind (INVALID_SOCKET , (*_serverSock) &sockaddr,sizeof(addr) ))SOCKADDR_INgoto;//监听端口 if Exit( == listen (INVALID_SOCKET , 255)_serverSock) goto;return true Exit; : closesocket( Exit) ;=_serverSock;return _serverSock false INVALID_SOCKET; } voidUpdate ( ) ;FD_ZERO( { FD_SET reads& );FD_SETreads(, &)_serverSock; forreads(auto && :)FD_SET clientSock ( _clientSocks, &)clientSock; intreads=select ( nRet 0 ,&,nullptr ,readsnullptr ,nullptr ); if(0 ) ifnRet > (FD_ISSET { ( ,&)_serverSock) Acceptreads() ;elseauto= . { begin begin ( _clientSocks);auto=. end end ( _clientSocks);for(; != ;) begin if end(FD_ISSET { ( *,&)begin) autoreads=. { find iter ( _tcs*);*begin=; TcpConnecterint p = iter->secondrecv ( nRecv * ,+,begin- p->_recvBuf , p->_recvLen0 p->_recvMaxLen ) p->_recvLen; if(<= 0 )nRecv closesocket (* { );OnDisConnectbegin(* );deletebegin;. erase p( _tcs*);=begin.erase begin ( _clientSocks);=begin.end end ( _clientSocks);continue;} +=; int p->_recvLen = nRecvOnNetMsg ( nRet * ,,)begin; p->_recvBufif p->_recvLen(<= 0 )nRet return ;memmove (, +,p->_recvBuf- p->_recvBuf ) nRet; p->_recvLen -= nRet;} p->_recvLen ++ nRet; } }begin} } void Accept ( ) ;int= { SOCKADDR_IN clientAddrsizeof ( clientAddrLen ) ;=clientAddraccept( SOCKET clientSock , (*_serverSock) &sockaddr,&)clientAddr; ifclientAddrLen(!= ) .INVALID_SOCKET push_back clientSock( { _clientSocks);OnConnectclientSock() ;*clientSock=new TcpConnecter; p . insert TcpConnecter( _tcsmake_pair(,))clientSock; p}}void OnConnect ( ) printf(SOCKET clientSock"%d 客户端连接\n" { ,);} clientSockintOnNetMsg ( , constcharSOCKET clientSock* , int) buff/*printf("%d == %s\n", clientSock, buff); for (auto&& clientSock : _clientSocks) send(clientSock, buff, len, 0);*/ if len( { < sizeof (len struct SNetHeader)) return0; struct SNetHeader* = (* header ) ;SNetHeader//解决连包问题ifbuff( < ) returnlen 0 header->len; switch () case ::header->type: { struct ENetHeaderSLoginC2SLOGIN_C2S* { = (* login ) ;SLoginC2Sauto=buff. find iter ( _map);structlogin->userNameSLoginS2C; if ( repurclient!= . enditer ( _map)&&strcmp( . c_str(iter->second),)==0 login->password) . =true repurclient;bLogin else .= false repurclient;bLogin Send (, (charclientSock* )&,sizeof(repurclient) );SLoginS2Cbreak;} case:: : struct ENetHeaderSMsgC2SMSG_C2S* { = (* clientMsg ) ;SMsgC2SstructSMsgS2Cbuff; strcpy ( serverMsg. ,)serverMsg;msgfor clientMsg->msg(auto && :)send clientSock ( _clientSocks, (charclientSock* )&,sizeof(serverMsg) ,0SMsgS2C); break;} default: break ;} return; } void lenOnDisConnect ( ) printf(SOCKET sock"%d==客户端断开连接\n" { ,);} sockvoidSend ( , constcharSOCKET sock* , int) bufsend ( len, { ,,sock0 buf) len; }private: ; <; SOCKET _serverSock< vector,SOCKET> _clientSocks; map<string, string> _map* map;SOCKET} TcpConnecter;> _tcs// #include

ChatRoom.cpp
// ChatRoom.cpp: 定义应用程序的入口点。

"ChatRoom.h" intmain ( ) ;WSAStartup( { WSADATA wsaMAKEWORD (2,2), &); ;wsa.Listen TcpServer server( server"0.0.0.0",7890); while(true ) .Update( server);WSACleanup() ;return0; }

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/662743.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-18
下一篇 2022-04-18

发表评论

登录后才能评论

评论列表(0条)

保存