学习目标:
1、掌握多平台C++程序的设计、编码、调试和发布
2、掌握Select、IOCP、epoll 网络通信模型
3、掌握主流平台下使用C++ 进行TCP网络通信
4、掌握游戏引擎中使用C++ TCP通信
5、掌握后端开发性能优化技巧
6、掌握多线程、内存管理(内存池、对象池)的实现和运用
第一讲:运用C++和Socket API构建百万级处理能力网络通信引擎
首先感谢伟大的刘远东老师!!!
1、不到2000行代码的Select模型服务端程序
(1)Socket Select 模型客户端 SocketClient
(2)Socket Select 模型服务端 CELLSocketServer
(3)网络消息协议解封包结构 CELLSocket MessageHeader
(4)内存代理 CELLDelegate
(5)临界区锁 CELLMutex
(6)多线程 CELLThread
(7)精准计时器 CELLTimestamp
第一节:Socket基础API
Socket可以当成文件操作。
客户端中:建立一个Socket相当于声明一个文件指针;连接服务器相当于打开文件;向服务器发送数据相当于fwrite文件,接收服务端数据相当于fread文件;关闭连接相当于关闭文件。 客户端通过IP地址和端口号定向连接到服务器。
服务端中:7步实现网络交互。与客户端不同的是,服务端需要绑定端口,监听端口并阻塞等待客户端连接。服务端共3步阻塞,需要搭配多线程编程实现相应功能。
第二节:建立Windows Socket程序 (VS2015)
建立一个空的Win32控制台程序
#define WIN32_LEAN_AND_MEAN
#include<Windows.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib") //windows socket2 32的lib库
int main()
{
//启动 windows socket 2.x 环境
WORD versionCode = MAKEWORD(2, 2); //创建一个版本号
WSADATA data;
WSAStartup(versionCode, &data); //启动Socket网络API的函数
///
///
WSACleanup();
return 0;
}
第三节: 建立建议的TCP服务端和客户端
(1) 用Socket API建立简易的TCP服务端
- 建立一个Socket
- 绑定接受客户端连接的端口 bind
- 监听网络端口 listen
- 等待接受客户端连接 accept
- 向客户端发送一条数据 send
- 关闭socket
定义建议服务端的基本SOCKET代码
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<Windows.h>
#include<WinSock2.h>
#include<iostream>
#include<string>
using namespace std;
#pragma comment(lib,"ws2_32.lib") //windows socket2 32的lib库
int main()
{
//启动 windows socket 2.x 环境
WORD versionCode = MAKEWORD(2, 2); //创建一个版本号
WSADATA data;
WSAStartup(versionCode, &data); //启动Socket网络API的函数
///
//(1) 用Socket API建立简易的TCP服务端
// 1. 建立一个Socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // ipv4 面向字节流的 tcp协议
// 2. 绑定接受客户端连接的端口 bind
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567); //端口号 host to net sockaddr_in中port是USHORT类型 网络中port是 unsigend short类型 因此需要Htons进行转换
//_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //服务器绑定的IP地址 127.0.0.1是本地地址
_sin.sin_addr.S_un.S_addr = INADDR_ANY; //不限定访问该服务端的IP
if (bind(_sock, (sockaddr*)&_sin, sizeof(_sin)) == SOCKET_ERROR) //sockaddr 不利于编码
{
cout << "ERROR: 绑定用于接受客户端连接的网络端口失败..." << endl;
}
else
{
cout << "SUCCESS: 绑定端口成功..." << endl;
}
// 3. 监听网络端口 listen
if (listen(_sock, 5) == SOCKET_ERROR)//第二个参数 backbag 最大允许连接数量
{
cout << "ERROR: 监听用于接受客户端连接的网络端口失败..." << endl;
}
else
{
cout << "SUCCESS: 监听端口成功..." << endl;
}
// 4. 等待接受客户端连接 accept
sockaddr_in _clientAddr = {};
int cliendAddrLen = sizeof(_clientAddr);
SOCKET _clientSock = INVALID_SOCKET; // 初始化无效的socket 用来存储接入的客户端
char msgBuf[] = "Hello, I'm Server";
while (true)
{
_clientSock = accept(_sock, (sockaddr*)&_clientAddr, &cliendAddrLen);//当客户端接入时 会得到连入客户端的socket地址和长度
if (INVALID_SOCKET == _clientSock) //接受到无效接入
{
cout << "ERROR: 接受到无效客户端SOCKET..." << endl;
}
else
{
cout << "新Client加入: IP = " << inet_ntoa(_clientAddr.sin_addr) << endl; //inet_ntoa 将ip地址转换成可读的字符串
// 5. 向客户端发送一条数据 send
send(_clientSock, msgBuf, strlen(msgBuf) + 1, 0); //+1是为了把\0算进去
}
}
// 6. 关闭socket
closesocket(_sock);
// 清除Windows socket环境
WSACleanup();
return 0;
}
//这段是socket地址中绑定IP地址时的数据结构 S_un是一个联合体
typedef struct in_addr {
union {
struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { USHORT s_w1,s_w2; } S_un_w;
ULONG S_addr;
} S_un;
#define s_addr S_un.S_addr /* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2 // host on imp
#define s_net S_un.S_un_b.s_b1 // network
#define s_imp S_un.S_un_w.s_w2 // imp
#define s_impno S_un.S_un_b.s_b4 // imp #
#define s_lh S_un.S_un_b.s_b3 // logical host
} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
(2) 用Socket API建立简易的TCP客户端
- 建立一个Socket
- 连接服务器 connect
- 接受服务器信息 recive
- 关闭 socket