大多数的情况下,游戏的服务端都是在Linux下运行,但是Linux下做开发C/C++的开发相对来说是比较困难的。所以一般来说,游戏的服务端都是做成可移植的,这样方便在Windows下做开发。也就是说,服务端既可在Windows下运行,也可以在Linux下运行。说了这么多,看似与网络链接半毛钱关系都没有。
其实不是这样的,就像Lua一样,既可以在Windows下调用它,也可以在Linux调用它,关键就库不同。熟悉网络编程的都知道,绝大多数的网络API在不同的平台调用是不同的。那如何做到可移植呢?其实无论你选择的网络模型是IOCP、select还是epoll。我们使用的时候只关心Accept(谁连接到服务端?)、Receive(怎样接收消息?)和Send(怎样发送消息?)。也就是说我们只需要3个接口。Send的封装可以写进库的源文件中,我们只需单例调用它就可以了。但是如果Accept和Receive写到了源文件中,那我要怎样调用呢?我要什么时候调用呢?到这里感觉好像很困难!!
gamesocket.h
#ifndef GAMESOCKET_H
#define GAMESOCKET_H
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <list>
#include "luaengine.h"
using namespace std;
#define MAXBUF 4096 //4KB may be enough
#define MAXEPOLLSIZE 2048
class GameSocket
{
public:
static GameSocket& Instance()
{
static GameSocket instance;
return instance;
}
~GameSocket();
bool Init();
int Accept(int userID);
int Lose(int userID);
int Listen();
int Recv(int userID, char msg[MAXBUF]);
int Send(int userID, char msg[], short length);
private:
GameSocket();
int SetNonBlocking(int socket_fd);
int m_epfd;
int m_listener;
struct sockaddr_in m_their_addr;
struct epoll_event ev;
};
#endif // SOCKET_H
现在要将这个类封装成静态库,而Accept和Recv是被动触发的,是根据监听到的信息(Listen)而触发的。
gamesocket.cpp
#include "gamesocket.h"
GameSocket::GameSocket()
{
Init();
}
GameSocket::~GameSocket()
{
close(m_listener);
}
/*
setnonblocking - 设置هڈ¥وں„ن¸؛ééک»ه،و–¹ه¼ڈ
*/
int GameSocket::SetNonBlocking(int socket_fd)
{
if (fcntl(socket_fd, F_SETFL, fcntl(socket_fd, F_GETFL, 0)|O_NONBLOCK) == -1)
{
return -1;
}
return 0;
}
bool GameSocket::Init()
{
LuaEngine lua;
int lisnum ;
int port ;
lua.LoadLuaFile("Config/socket.lua");
lua.GetGlobalProc("SocketConfig",0,2);
lua.GetResult(-1,&lisnum,LuaEngine::INTEGER);
lua.GetResult(-2,&port,LuaEngine::INTEGER);
struct sockaddr_in my_addr;
struct rlimit rt;
/* 设置و¯ڈن¸ھè؟›ç¨‹ه…پ许و‰“ه¼€çڑ„وœ€ه¤§و–‡ن»¶و•° */
rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;
if (setrlimit(RLIMIT_NOFILE, &rt) == -1)
{
perror("setrlimit");
exit(1);
}
else
{
printf("Set System Argument Successï¼پ\n");
}
/* ه¼€هگ?socket 监هگ¬ */
if ((m_listener = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket create fail\n");
exit(1);
}
else
{
printf("socket create success!\n");
}
SetNonBlocking(m_listener);
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(m_listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind");
exit(1);
}
else
{
printf("Bind ip address and port success\n");
}
if (listen(m_listener, lisnum) == -1)
{
perror("socket listen\n");
exit(1);
}
else
{
printf("socket listen success\n");
}
m_epfd = epoll_create(MAXEPOLLSIZE);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = m_listener;
if (epoll_ctl(m_epfd, EPOLL_CTL_ADD, m_listener, &ev) < 0)
{
fprintf(stderr, "epoll set insertion error: fd=%d\n", m_listener);
return -1;
}
else
{
printf("add listener to epoll success\n");
}
}
int GameSocket::Listen()
{
static int new_fd;
static int nfds;
static int ret;
static int curfds = 1;
static socklen_t len;
static struct sockaddr_in their_addr;
static struct epoll_event events[MAXEPOLLSIZE];
static struct epoll_event ev;
char recv_buf[MAXBUF];
nfds = epoll_wait(m_epfd, events, curfds, -1);
if (nfds == -1)
{
perror("epoll_wait");
return -1;
}
for (int n = 0; n < nfds; ++n)
{
if (events[n].data.fd == m_listener)
{
new_fd = accept(m_listener, (struct sockaddr *) &their_addr,&len);
if(new_fd < 0)
{
printf("link error\n");
}
if (new_fd < 0)
{
perror("accept");
continue;
}
else
{
printf("connect from %s,the port is %d,the fd is %d\n",inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port), new_fd);
}
SetNonBlocking(new_fd);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = new_fd;
if (epoll_ctl(m_epfd, EPOLL_CTL_ADD, new_fd, &ev) < 0)
{
fprintf(stderr, "add socket %d to epoll fail ,%s\n",new_fd, strerror(errno));
continue;
}
else
{
this->Accept(new_fd);
}
curfds++;
}
else if (events[n].events & EPOLLIN)
{
ret = recv(events[n].data.fd, recv_buf, MAXBUF, 0);
if (ret < 1 && errno != 11)
{
epoll_ctl(m_epfd, EPOLL_CTL_DEL, events[n].data.fd, &ev);
curfds--;
}
else
{
this->Recv(events[n].data.fd, recv_buf);
}
}
}
}
int GameSocket::Send( int userID, char msg[] ,short length)
{
if(send(userID,msg,length,0) == -1)
{
printf("%d :msg can not be send\n",userID);
return -1;
}
return 1;
}
就像上面的代码一个,在Listen会调用下面的代码,但是在gamesocket.cpp并没有对Accept和Recv的实现,其实这是对外接口的关键。
this->Accept(new_fd);
this->Recv(events[n].data.fd, recv_buf);
如果在gamesocket.cpp对Accept和Recv方法实现了,那在调用该库的时候,我们根本不知道怎样去调用Accept和Recv。所以只有Accept和Recv在库外实现,才可能去调用这两个方法。现在将两个方法的实现写在了interfacce.cpp中。
interface.cpp
#include "gamesocket.h"
int GameSocket::Accept(int userID)
{
printf("Accept userID = %d\n",userID);
return 1;
}
int GameSocket::Recv(int userID, char msg[MAXBUF])
{
printf("receive a msg\n");
printf("%s\n",msg);
return 1;
}
int GameSocket::Lose(int userID)
{
printf("Lose userID = %d\n",userID);
exit(0);
return 1;
}
执行结果:

PS:服务端都是消息响应的,也就是说,当我们获得消息(Recv)的时候,我们可以对下面的逻辑做多线程或者多进程的操作。
如有不正确,欢迎指出!
交流群:315249378
分享到:
相关推荐
在C++编程中,HTTP(超文本传输协议)和HTTPS(安全超文本传输协议)是两种广泛用于网络通信的协议。...在这个项目中,我们关注的是C++中实现...理解和使用这些接口,可以帮助开发者轻松地在C++项目中实现网络通信功能。
在实现过程中,C++Builder的VCL库和FireMonkey(FMX)库可能被用来构建用户界面,而DataSnap框架提供了必要的网络通信和数据传输支持。DataSnap的特性包括: - **数据透明性**:客户端应用程序无需关心数据是如何从...
3. **接口定义**:服务端通过接口定义语言(IDL)声明对外暴露的服务接口,这些接口会被编译成各种目标语言的代码,供客户端使用。 4. **对象代理**:ICE服务端会为每个远程接口创建一个对象实例,客户端通过对象...
在描述中提到的"VC实现的webserivce服务端代码",意味着这个项目使用VC++和GSOAP库来创建了一个能够对外提供服务的Web服务服务器。服务端代码通常包括以下组成部分: 1. **服务接口定义**:这是Web服务的核心,定义...
C++是COM编程的主要语言之一,ATL(Active Template Library)和WTL(Windows Template Library)是两个常用的C++库,它们提供了方便的COM编程支持。 COM的核心概念包括组件、接口、类工厂、线程模型和引用计数等。...
在IT行业中,远程控制系统接口是实现远程协助功能的关键技术,它允许用户通过网络对另一台计算机进行操作,仿佛自己就在那台电脑前一样。本文将深入探讨“远程控制系统接口”以及如何利用VC(Visual C++)进行编程...
`asio`库由Boost库的一部分发展而来,现在也作为单独的开源项目,被广泛用于C++的网络编程,因为它提供了简洁、高效的API来处理TCP、UDP等网络协议。 在`asio`库中,你可以使用`io_service`对象来管理并发操作,它...
1. 安装C++编译环境:如Visual Studio或MinGW,因为gRPC库是用C++编写的。 2. 获取gRPC库:这通常通过Git克隆gRPC仓库或者下载预编译的库来完成。在这个案例中,库可能已经包含了预编译的二进制文件。 3. 配置项目:...
在这个项目中,我们主要利用了Winsock库,这是Windows系统下的一个网络编程接口,它提供了TCP/IP协议栈的功能,方便开发者进行网络编程。在C++中,我们可以通过包含`winsock2.h`头文件并链接`ws2_32.lib`库来使用...
webrouter:接口路由网关服务,对外提供统一的流量入口,主要负责请求分发以及黑白名称配置。 cppweb在读数据采用epoll网络模型,以任务队列的方式处理具体请求,回包也在任务队列中处理,理论上cppweb可支持单机...
Bsphp服服务端提供很多对外API接口可以实现客户端调用用户中心功能,从而实现控制软件开发软件授权使用,支持PC端,安卓,苹果系统,编程语言有C++,易语言,C#,VB,TC,Delphi,E4a,VC,JAVA,只要支持http协议就可以调用Bsphp...
这个接口将作为对外暴露的远程服务。例如,你可以创建一个名为`CalculatorService`的接口,包含加法、减法等方法。然后,使用Hessian工具生成服务端的代理类,并实现这个接口。 2. **部署服务**:将实现的服务部署...
服务端通常负责提供对外的服务接口。在HessionDemo中,会定义一些服务接口(如Service Interface),然后实现这些接口(Service Implementation)。接着,使用Hession的Server类来启动服务,将服务绑定到特定的HTTP...
1. **接口声明**:定义一个接口,如`IAidlService.aidl`,包含服务端对外暴露的方法。 2. **数据类型**:AIDL支持基本数据类型(如int、String等),以及自定义的数据结构(如Parcelable对象)。自定义对象需要实现...
服务提供者会暴露一个接口,这个接口通过Hessian协议对外提供服务;服务消费者则通过Hessian调用这些接口,实现远程方法的执行。 1. **配置服务提供者** - 首先,定义一个接口,比如`IService`,包含需要暴露的...
在本项目中,我们可能可以看到一个`.thrift`文件,其中包含了服务端和客户端之间的接口定义,如函数签名、返回值和参数类型等。例如: ```thrift service PLCService { void fetchData(1:i32 deviceId), void ...
- 创建服务接口:定义你需要对外提供的服务方法,例如`IService`接口,包含一个`sayHello`方法。 - 实现服务接口:创建`ServiceImpl`类,实现`IService`接口中的方法,提供实际的业务逻辑。 - 配置Hession服务:...
1. **服务端(Service)**:服务端通常是一个实现了特定接口的类,它运行在一个独立的进程中,提供对外的服务。在这个测试中,服务端会包含一个Binder对象,该对象实现了客户端需要调用的方法。 2. **客户端...
Golang可以处理服务端的逻辑和网络通信,而C++则负责处理性能敏感的部分,如图像处理和计算密集型的面部识别算法。 5. **人脸识别服务**:这个服务通过SeetaFace提供的API接口,可以对外提供人脸检测(定位面部位置...