笔记目录导航

C++游戏服务器框架笔记(一)_封装数据包类

C++游戏服务器框架笔记(二)_封装Socket类

C++游戏服务器框架笔记(三)_封装ByteBuffer类​​​​​​​

......

网络通信可以直接调用socket系统api接口进行通信,不过在不同的系统中,部分代码是有区别的,Windows和Linux系统中 一些接口或者接口参数的类型是有区别的,需要进行兼容,所以通常会将底层的socket api接口进行封装,已达到跨平台的目的,因为这里是C++语言,所以封装成一个类。

差别之处利用宏判断是windows系统或者是其他系统,这里只处理Windows系统和Linux系统

#ifdef _WIN32

这里是Windows系统下的接口调用

#else

这里是Linux系统下的接口调用

#endif

Socket类头文件如下:

#ifndef _SOCKET_H_
#define _SOCKET_H_
/*
基础socket类,封装c socket接口
*/
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <assert.h>
#ifdef _WIN32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
//由于bzero在windows系统下没有 这里定义一个bzero
#define bzero(point, size) memset(point, 0, size)
#else
#include <string.h>
//统一socket句柄的类型名
typedef int SOCKET;
#define INVALID_HANDLE_VALUE (-1)
#endifclass Socket
{
public:Socket();Socket(SOCKET sock, const char* ip, int port);~Socket();//Windows系统中使用socket函数需要先调用WSAStartup需要初始化版本static bool InitSocket();//Windows系统中使用socket函数完毕后需要调用WSACleanup释放static void UnInitSocket();//设置可重用地址void SetReuseAddr();//设置非阻塞void SetSockNonBlock();//设置接收缓冲区大小void SetRecvBuffSize(int size);//设置发送缓冲区大小void SetSendBuffSize(int size);//地址绑定bool Bind(const char* ip, int port);//启动监听bool Listen(int backlog);//接收连接,返回一个已分配内存上的Socket指针, 需要手动管理释放,失败返回 nullptrSocket* Accept();//接收连接, 返回连接的socketSOCKET Accept(struct sockaddr_in * addr, int * addrlen);//发起连接int Connect(const char* ip, int port);//发起连接int Connect(SOCKET sock, const char* ip, int port);//发送数据int Send(const char* buf, int size);//接收数据int Recv(char* buf, int size);//关闭套接字int Close();public:SOCKET m_Sock;char m_Ip[16];short m_Port;
};
#endif

InitSocket() 和UnInitSocket() 内部有做分平台处理,因为在windows下 socket通信需要一个初始化和释放的步骤,需要进行兼容

SetReuseAddr():设置可重用地址接口, 设置端口可以被重复使用,可以被多个进程bind,  防止服务器端先调用close关闭socket后系统处于TIME_WAIT状态下还没有释放端口,这时重新启服的时候 在调用bind时 会绑定失败,提示ADDR已经在使用中。

SetSockNonBlock():设置非阻塞,socket默认是阻塞模式,例如调用recv的时候 程序会阻塞在这里,一直等到读取到数据后,才会返回读取到的字节数,设置了非阻塞后,调用recv函数的时候会立即返回读取到的字节数,如果没有字节可读取,则会返回对应的错误码, 后续会涉及到使用多路IO复用机制中的Epoll,需要设置socket为非阻塞模式

SetRecvBuffSize():设置socket的接收缓冲区大小

SetSendBuffSize():设置socket的发送缓冲区大小

系统中会为每个创建的socket分配一个接收缓冲区和一个发送缓冲区,例如调用send()函数发送一段数据,函数返回时,数据实际上是被加到了socket的发送缓冲区中,并没有实时通过底层网卡发送出去,会由系统在合适的时候再来将发送缓冲区中的数据发送出去。可以根据程序对网络通信数据量和网络的情况设置合理大小来提高效率和避免数据错误等问题。

Socket类的Cpp文件如下:

#include "Socket.h"
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>#if _WIN32
#else
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#endifSocket::Socket()
{m_Sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);assert(m_Sock != -1);SetReuseAddr();
}Socket::Socket(SOCKET sock, const char* ip, int port) {m_Sock = sock;strncpy(m_Ip, ip, sizeof(ip));m_Port = port;SetReuseAddr();
}Socket::~Socket(){Close();
}   bool Socket::InitSocket() {
#if _WIN32WORD wVersionRequested = MAKEWORD(2, 2);WSADATA wsaData;int nErrorID = ::WSAStartup(wVersionRequested, &wsaData);if (nErrorID != 0) return false;if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {UnInitSocket();return false;}
#endifreturn true;
}void Socket::UnInitSocket() {
#if _WIN32::WSACleanup();
#endif
}void Socket::SetReuseAddr() {int on = 1;setsockopt(m_Sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
}void Socket::SetSockNonBlock()
{int ret = -1;
#if _WIN32u_long argp = 1;ret = ioctlsocket(m_Sock, FIONBIO, &argp);
#elseint old_flag = fcntl(m_Sock, F_GETFL, 0);ret = fcntl(m_Sock, F_SETFL, old_flag | O_NONBLOCK);
#endifif (ret == -1) {return;}
}void Socket::SetRecvBuffSize(int size)
{int ret = -1;
#if _WIN32 int len = sizeof(size);ret = setsockopt(m_Sock, SOL_SOCKET, SO_RCVBUF, (char *)&size, len);
#elsesocklen_t len = sizeof(size);ret = setsockopt(m_Sock, SOL_SOCKET, SO_RCVBUF, &size, len);
#endifif (ret < 0){return;}
}void Socket::SetSendBuffSize(int size)
{int ret = -1;
#if _WIN32 int len = sizeof(size);ret = setsockopt(m_Sock, SOL_SOCKET, SO_SNDBUF, (char *)&size, len);
#elsesocklen_t len = sizeof(size);ret = setsockopt(m_Sock, SOL_SOCKET, SO_SNDBUF, &size, len);
#endifif (ret < 0) {return;}
}bool Socket::Bind(const char * ip, int port)
{struct sockaddr_in addr;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port);if (ip == nullptr)addr.sin_addr.s_addr = htonl(INADDR_ANY);else
#if _WIN32addr.sin_addr.s_addr = inet_addr(ip);
#elseinet_pton(AF_INET, ip, &addr.sin_addr.s_addr);
#endifint ret = bind(m_Sock, (struct sockaddr *)&addr, sizeof(addr));if (ret < 0) {return false;}return true;
}bool Socket::Listen( int backlog)
{int ret = listen(m_Sock, backlog);if (ret == -1) {return false;}return true;
}Socket* Socket::Accept()
{struct sockaddr_in addr;
#if _WIN32int len = sizeof(addr);
#elsesocklen_t len = sizeof(addr);
#endif SOCKET sock = accept(m_Sock, (struct sockaddr*)&addr, &len);if (-1 == sock)return nullptr;Socket* pSock = new Socket(sock, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));return pSock;
}SOCKET Socket::Accept(struct sockaddr_in* addr, int * addrlen)
{
#if _WIN32SOCKET sock = accept(m_Sock, (struct sockaddr*)addr, addrlen);
#elseSOCKET sock = accept(m_Sock, (struct sockaddr*)addr,(socklen_t *)addrlen);
#endifreturn sock;
}int Socket::Connect(const char* ip, int port)
{return Connect(m_Sock, ip, port);
}int Socket::Connect(SOCKET Sock, const char* ip, int port)
{struct sockaddr_in addr;bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port);#if _WIN32int len = sizeof(addr);addr.sin_addr.s_addr = inet_addr(ip);
#elsesocklen_t len = sizeof(addr);inet_pton(AF_INET, ip, &addr.sin_addr.s_addr);
#endifreturn connect(Sock, (struct sockaddr*)&addr, len);;
}int Socket::Send(const char* buf, int Len)
{return send(m_Sock, buf, Len, 0);;
}int Socket::Recv(char* buf, int Len)
{return recv(m_Sock, buf, Len, 0);
}int Socket::Close()
{
#if _WIN32int ret = closesocket(m_Sock);
#elseint ret = close(m_Sock);
#endifreturn ret;
}

这部分代码都比较简单,只是在底层api接口上分平台处理了差异之处,测试代码如下:

#include <iostream>
#include "net\Socket.h"using namespace std;int main(void)
{Socket::InitSocket();cout << "init Socket" << endl;Socket * pListenSock = new Socket();pListenSock->Bind(nullptr, 8888);pListenSock->Listen(100);Socket * pSock = pListenSock->Accept();assert(pSock != nullptr);char buf[10240];bzero(buf, 10240);while (true) {int ret = pSock->Recv(buf, 10240);if (ret <= 0) break;cout << "recv data:" << buf << endl;if (buf[0] == '#') break;bzero(buf, 10240);}delete pSock;delete pListenSock;Socket::UnInitSocket();cout << "My ServerEngine" << endl;system("pause");return 0;
}

C++游戏服务器框架笔记(二)_封装Socket类相关推荐

  1. golang游戏服务器框架_教你从头写游戏服务器框架

    需求 由于"越通用的代码,就是越没用的代码",所以在设计之初,我就认为应该使用分层的模式来构建整个系统.按照游戏服务器的一般需求划分,最基本的可以分为两层: 底层基础功能:包括通信 ...

  2. 开源游戏服务器框架NoahGameFrame(NF)服务器端环境搭建(二)

    一.下载NoahGameFrame 1.进入到开源游戏服务器框架NoahGameFrame在GitHub的官方界面NoahGameFrame 2.复制要Checkout的资源目录URL 3.在任意一个 ...

  3. arduino nano 蓝牙_探索 Golang 云原生游戏服务器开发,5 分钟上手 Nano 游戏服务器框架...

    介绍 Nano 是什么? 轻量级,方便,高性能 golang 的游戏服务器框架. nano 是一个轻量级的服务器框架,它最适合的应用领域是网页游戏.社交游戏.移动游戏的服务端.当然还不仅仅是游戏,用  ...

  4. python游戏服务器框架_有那些比较成熟的开源游戏服务器引擎/框架(编程语言不限)?...

    更新,没有看到服务器(逃. 下面仅为游戏引擎和框架推荐,需要的小伙伴简单看一下,正确的答案努力撰写中. 从角色扮演游戏到即时策略游戏,从冒险解谜游戏到动作射击游戏,甚至是只有一兆大小的迷你游戏,都有起 ...

  5. 游戏服务器框架概括分析

    游戏服务器框架概括分析 关注公众号 风色年代(itfantasycc) 500G游戏开发资料随便拿! 这篇blog题目涉及的范围真大!以至于在这里需要先写一篇前言把范围缩小.选择写这样一个系列的文章, ...

  6. golang游戏服务器框架_Go开源游戏服务器框架——Pitaya

    简介 Pitaya是一款由国外游戏公司topfreegames使用golang进行编写,易于使用,快速且轻量级的开源分布式游戏服务器框架 Pitaya使用etcd作为默认的服务发现组件,提供使用nat ...

  7. 《MFC游戏开发》笔记二 建立工程、调整窗口

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9300383 作者:七十一雾央 新浪微博:http:// ...

  8. 高性能分布式游戏服务器框架

    欢迎大家Fork mqant开源框架 为什么决定要重新造一个轮子? 目前网上优秀的开源游戏服务器框架也不少(当然与web框架比起来就少太多了),但总结起来都各有各的优缺点,下面列出我在选型过程中的一些 ...

  9. Go开源游戏服务器框架——Pitaya

    Go开源游戏服务器框架--Pitaya 简介 抽象分析 框架流程 处理细节 简介 Pitaya是一款由国外游戏公司topfreegames使用golang进行编写,易于使用,快速且轻量级的开源分布式游 ...

  10. 教你从头写游戏服务器框架

    本文由云+社区发表 作者:韩伟 前言 大概已经有差不多一年没写技术文章了,原因是今年投入了一些具体游戏项目的开发.这些新的游戏项目,比较接近独立游戏的开发方式.我觉得公司的"祖传" ...

最新文章

  1. 浅析:setsockopt()改善程序的健壮性【转】
  2. 限制输入字符串的长度
  3. 生日快乐程序_别@官方了!云开发教你制作个性头像小程序,以后过节想加啥就加啥!...
  4. android关键应用程序,安卓开发:Android应用程序的四个关键点
  5. sql server:查詢系統表
  6. C++ primer第六章函数的学习
  7. Vaadin介绍与开发练习之二(创建第一个Vaadin类)
  8. 智能移动项目打包发布经验交流
  9. 类加载过程(时机)略解
  10. SelfUpdate 树不起作用
  11. MSRA-USTC 计算机科学领域前沿进展新年第一讲
  12. 可靠消息服务在支付领域的应用
  13. java小球反弹_java实现小球碰撞反弹(示例代码)
  14. TextView属性设置
  15. 基于微信小程序视频点播系统 开题报告
  16. android bitmap iplimage,IplImage和Bitmap相互转换
  17. [渝粤教育] 四川大学 食物营养学 参考 资料
  18. Latex中的括号用法总结
  19. eaxsinbx_高等数学导数与微分练习题
  20. 数据结构-2019春 07-图4 哈利·波特的考试 (25 分)

热门文章

  1. android5.1导航系统,北斗地图导航系统
  2. java实现户籍管理系统_基于jsp的户籍管理系统-JavaEE实现户籍管理系统 - java项目源码...
  3. 户籍管理员按1进入函数1c语言,户籍管理系统信息系统.doc
  4. 微软私有云部署及讲解专题
  5. VirtualBox扩容教程
  6. 问答社区php源码,cpf开源SNS问答社区源码 php版 v0.7.1
  7. Windows 2003声卡驱动的安装.
  8. 问题解决-Visio2016和Office不能并行
  9. 在FireFox中使用IE Tab插件
  10. 微信小程序:网课查题微信小程序源码下载,题库资源丰富自动采集,支持语音拍照识别