当前位置: 首页>編程日記>正文

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

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

笔记目录导航

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;
}


https://www.fengoutiyan.com/post/15837.html

相关文章:

  • ssm框架封装
  • c游戏框架
  • c socket编程
  • java socket框架
  • 封装java
  • 封装框架
  • 引线框架封装
  • 什么是服务器
  • 鏡像模式如何設置在哪,圖片鏡像操作
  • 什么軟件可以把圖片鏡像翻轉,C#圖片處理 解決左右鏡像相反(旋轉圖片)
  • 手機照片鏡像翻轉,C#圖像鏡像
  • 視頻鏡像翻轉軟件,python圖片鏡像翻轉_python中鏡像實現方法
  • 什么軟件可以把圖片鏡像翻轉,利用PS實現圖片的鏡像處理
  • 照片鏡像翻轉app,java實現圖片鏡像翻轉
  • 什么軟件可以把圖片鏡像翻轉,python圖片鏡像翻轉_python圖像處理之鏡像實現方法
  • matlab下載,matlab如何鏡像處理圖片,matlab實現圖像鏡像
  • 圖片鏡像翻轉,MATLAB:鏡像圖片
  • 鏡像翻轉圖片的軟件,圖像處理:實現圖片鏡像(基于python)
  • canvas可畫,JavaScript - canvas - 鏡像圖片
  • 圖片鏡像翻轉,UGUI優化:使用鏡像圖片
  • Codeforces,CodeForces 1253C
  • MySQL下載安裝,Mysql ERROR: 1253 解決方法
  • 勝利大逃亡英雄逃亡方案,HDU - 1253 勝利大逃亡 BFS
  • 大一c語言期末考試試題及答案匯總,電大計算機C語言1253,1253《C語言程序設計》電大期末精彩試題及其問題詳解
  • lu求解線性方程組,P1253 [yLOI2018] 扶蘇的問題 (線段樹)
  • c語言程序設計基礎題庫,1253號C語言程序設計試題,2016年1月試卷號1253C語言程序設計A.pdf
  • 信奧賽一本通官網,【信奧賽一本通】1253:抓住那頭牛(詳細代碼)
  • c語言程序設計1253,1253c語言程序設計a(2010年1月)
  • 勝利大逃亡英雄逃亡方案,BFS——1253 勝利大逃亡
  • 直流電壓測量模塊,IM1253B交直流電能計量模塊(艾銳達光電)
  • c語言程序設計第三版課后答案,【渝粵題庫】國家開放大學2021春1253C語言程序設計答案
  • 18轉換為二進制,1253. 將數字轉換為16進制
  • light-emitting diode,LightOJ-1253 Misere Nim
  • masterroyale魔改版,1253 Dungeon Master
  • codeformer官網中文版,codeforces.1253 B
  • c語言程序設計考研真題及答案,2020C語言程序設計1253,1253計算機科學與技術專業C語言程序設計A科目2020年09月國家開 放大學(中央廣播電視大學)
  • c語言程序設計基礎題庫,1253本科2016c語言程序設計試題,1253電大《C語言程序設計A》試題和答案200901
  • 肇事逃逸車輛無法聯系到車主怎么辦,1253尋找肇事司機