memmove可以防止内存重叠。
比如:abcdef,目前需要删除ab,然后将cdef往前移动,有些系统的内存移动机制可能存在从后往前移动,比如先把f移动到d所在位置,然后将e移动到c所在位置,但是当我们要把d移动到b所在位置的时候,我们发现d已经是f了,也就变成了把f移动到b,同理本来是把c移动到a所在位置,却变成了把e移动到a所在位置,最终的数据移动 *** 作变成了efef。
而memmove可以强制要求从前往后移动数据。
- ASCII码不同,\n为10,而\r是13。
- 在Windows系统中,\r只回车不换行,\n是换行。
在有些系统中,单独的\n是不换行的。
参考文章:https://blog.csdn.net/weixin_42817477/article/details/109029172
枚举类的优势:
-
降低命名空间的污染
以下代码在同一个作用域下
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; //正确
-
避免发生隐式转换
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型别 }
-
可以提前声明(强制声明)
enum Color; //错误 enum class Color; //正确
参考文章:https://blog.csdn.net/albertsh/article/details/103529462
map的[ ] *** 作符会有副作用,当查找的键不存在时,会在对应键位置插入默认值。
数据包规范 NetHeader.hTIPS:以下代码初步完成,未进行优化,仅供参照。
#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 ®
}
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;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)