自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

凌云天空的博客

关于服务器开发

  • 博客(50)
  • 收藏
  • 关注

原创 服务器程序日志介绍以及实现封装

日志的一般有诊断日志和交易日志,诊断日志简单说就是以文本形式供人阅读的日志。而交易日志是数据库的write-ahead log、文件系统的journaing等,通过回放日志可以恢复每一次修改后的状态。这里说的日志是诊断日志。日志的作用:1.诊断定位程序执行问题2.追踪(trace)程序执行过程和数据变化。3.程序的性能分析4.记录数据采集、分析和统计在服务器编程中日志必不可少,对于程序问题的诊断和分析,GDB不一定是好的方法,通过日志定位问题是必要的。日...

2021-09-30 11:00:24 440

原创 golang学习笔记(十七)

Go语言提供了一种机制在运行时更新和检查变量的值或者调用它们的方法,但是编译时并不知道这些变量的具体类型,这就是反射机制。 什么场景需要反射? 比如要写一个函数,但是不知道传入的参数类型是什么,可能是没有约定好,也可能传入的类型很多,这些类型并不能统一表示。或者需要根据某些条件决定调用哪个函数。 反射的缺点是什么?Go语言是一门静态语言,编译过程中会提前发现一些错误,但是对反射无能为力,因为反射是在运行时检查,所以可能...

2021-09-21 21:09:14 173

原创 thread_local关键字

thread_local是C++11为了线程安全引入的变量声明符,跟gcc内置的线程局部存储设备__thread功能类似。thread_local声明的对象的存储在每个线程开始时分配,在线程结束是销毁。每一个thread_local变量在每个线程有一份独立实体,各个线程的变量互不干扰。__thread只能用于修饰POD类型,不能修饰class类型因为无法自动调用构造函数和析构函数,而thread_local可以修饰class类型,在线程结束时,会调用class的析构函数。...

2021-09-18 11:26:02 459

原创 golang学习笔记(十六)

go语言中的select有些类似于switch也包括case和defult,而select是用来监听和channel有关的IO操作的,当有case中触发发送或接收操作则会被立即执行。如果没有channel的发送操作也没有defult语句,则会被阻塞。所以每一个case必须有一个通讯,如果多个case都可以执行,select会随机的选出一个执行。在没有channel有IO操作时,如果有defult语句,则会执行该语句,如果没有select将会阻塞,知道某个case下的cha...

2021-09-09 22:04:17 105

原创 golang学习笔记(十五)

channel可以被看成协程之间通信的管道,跟glibc中进程与进程间通信的管道类似。在golang中要传递某个数据从一个协程到另一个协程,可以将数据封装成一个数据对象,将数据对象的指针传入channel中,另外一个协程从管道中读出这个指针,并处理其指向的对象。Go从语言层面保证同一时间只有一个协程能够访问channel里面的数据。//声明var chanName chan chanType//创建通道,如果只是声明通道为nil,需要创建chanName = make(chan ...

2021-09-08 22:23:59 176

原创 golang学习笔记(十四)

go语言中引入包时会隐式的调用包中的init函数,并且在main函数执行前执行。init函数一般用来对变量的初始化,或者某些一次性操作,init函数没有返回值和参数,一个源文件中可以允许包含多个init函数。go语言不允许引入不使用的包,有时候我们需要引入一个包,但是不直接使用包里面的函数,而是调用包中的init函数,可以在引入包的前面加空白标识符_import(_ “fmt”)目录结构test1.gopackage testimport...

2021-08-31 21:29:33 73

原创 golang学习笔记(十三)

go语言实现了协程,使得异步调用程序变得容易,而不用像线程一样考虑资源的申请释放。但是对于共享资源的访问,也需要通过加锁来保证数据的安全。虽然go语言不希望这么做,而是希望通过channel来实现将共享状态和共享状态的变化在各个协程间传递,这样同样能保证同一时间只有一个协程访问共享状态。go提供了sync包,其中提供了等待组对象(sync.WaitGroup)、互斥锁(sync.Mutex)、读写锁(sync.RWMutex)等。 下面是关于互斥锁和等待...

2021-08-29 22:53:44 129

原创 golang学习笔记(十二)

go语言中给我们提供了一些包,通过调用包中的函数,可以方便我们编程。下面是time包中与时间相关的函数的基础用法,通过一个例子说明:package mainimport ( "fmt" "math/rand" //随机数 "time" //时间相关函数)func main() { //获取当前时间 返回一个Time的结构体变量记录当前时间 fmt.Println("////获取当前时间/////") t1 := time.Now() fmt.Print...

2021-08-29 17:07:38 75

原创 golang学习笔记(十一)

Go语言中Goroutine与线程的区别1、什么是Goroutine?Goroutine是建立在线程之上的轻量级的抽象。它允许我们以非常低的代价在同一个地址空间中并行地执行多个函数或者方法。相比于线程,它的创建和销毁的代价要小很多,并且它的调度是独立于线程的。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 packag

2021-07-19 23:41:39 86

原创 golang学习笔记(十)

go语言提供了一种数据类型叫做接口(interface)。就是定义了一组方法或者或者说函数。可以看做对象的一组行为。既然对象难免有数据,我们可以通过struct定义对象类型,然后根据struct实现这一组方法。这样就实现了多态,不同对象调用相同接口方法,实现不同功能。这样的好处是将定义与实现分离,降低了耦合性。这个跟C语言中我们定义一个结构体,里面定义一组函数指针,通过实现定义的函数指针赋值给结构体对象,形成一个模块,有异曲同工之妙。struct...

2021-07-16 23:56:32 112

原创 golang学习笔记(九)

go语言内嵌了map数据结构,这是非常有用的数据结构。go中的map是一种无序的key-value方式的数据集合。go中的map是通过hash表实现的,而C++中的std::map是通过红黑树实现的。 map的声明:var map_name map[key_type]value_type注意这时候还不能使用map,此时map_name变量等于nil,我们还需要make函数初始化map_name,这样才能使用:map_name = make...

2021-07-15 23:02:39 113

原创 golang学习笔记(八)

slice英文切片的意思,给黄瓜切片,给面包切片。这里的slice是给数组切片,截取数组的一部分。go语言的slice不但是一个动词,而且是一个名词,既一种数据结构类似于数组的数据结构,甚至底层就是用数组实现的。数组所有的优点slice都有,而且slice更加灵活,slice支持可以通过append向slice中追加元素,长度不够时会动态扩展,通过再次slice切片,可以得到得到更小的slice结构,可以迭代、遍历。数组是一组数据类型相同,连续且长度固...

2021-07-13 00:46:54 108

原创 golang学习笔记(七)

go语言中的指针跟C/C++没有什么区别,存放变量的内存地址。go指针的的空指针是nil,定义一个指针变量,它的默认值是 nil。 指针声明: var var_name *var_type 例: var Val1 *int 跟C/C++用法一样,通过操作符 "&" 取变量地址, "*" 通过指针访问目标对象。package mainimport ( "fmt")fu...

2021-07-10 23:04:33 165 2

原创 golang学习笔记(六)

go语言中的结构体与C/C++中类型很像,结构体是相同类型和不同类型数据构成的数据集合。 定义结构体: type st_name struct{ var_name1 var_type1 var_name2 var_type2 ... var_namen var_typen } ...

2021-07-10 22:33:18 94

原创 std::mutex和std::condition_variable理解以及实现阻塞队列和生产者与消费者模型

C++11中的std::mutex和std::condition_variable以及std::thread跟glibc下面的pthread_mutex_t和pthread_cond_t以及pthread_create系列线程函数实现功能差不多。但是std::mutex和std::condition_variable以及std::thread可以跨平台使用,而pthread_mutex_t和pthread_cond_t以及pthread_create系列线程函数只能在Linux下使用。...

2021-07-07 16:56:27 990

原创 函数指针与function以及bind用法

函数指针顾名思义是指向的函数的指针。和其他指针类型一样,函数指针指向某种特定类型(特定参数和返回值) C/C++中定义函数指针: 普通函数指针定义:typedef 返回值类型(*函数指针类型名)(参数列表) 类成员函数指针定义:typedef 返回值类型(类名::*函数指针类型名)(参数列表) typedef int(*FuncPtr)(int, int); // 定义函数指针 FuncPtr是定义的函数指针...

2021-07-03 16:49:00 820

原创 shared_from_this用法,以及shared_ptr陷阱

c++11中的智能指针源于boost,所以也将 类enable_shared_from_this 和 及其成员函数shared_from_this()也给收编了。通过模板方式继承enable_shared_from_this<T> 然后调用shared_from_this()函数返回对象T的shared_ptr指针,非常方便。使用时需要引用头文件 :#include <memory>用例:#include <stdio.h>...

2021-07-03 13:49:32 7283

原创 golang学习笔记(五)

go语言接收用户传递的参数,需要引入os包,用很简单,而不用像C/C++中那样,在main函数中传递参数int main(int argc, char **argv){//argc参数个数,argv保存参数 return 0;}go语言接收用户参数如下:package mainimport ( "fmt" "os")func main() { //接收用户传递的参数,都是以字符串方式传递的 argv := os.Args n := len(argv) //

2021-07-01 22:58:57 186

原创 golang学习笔记(四)

go中的函数用关键字func开头,形式是func 函数名(参数列表) 返回值类型 { //todo}go语言中的函数支持多个返回值,这个是C/C++中没有的,在写C/C++程序的时候,经常遇到要返回多个数值的时候,因为不支持,只能通过引用参数带出函数中的计算数值,感觉不是特别方便。示例代码如下:package mainimport ( "fmt" "strconv")//...

2021-07-01 22:53:02 163

原创 golang学习笔记(三)

go语言的关键字type跟C/C++中的typedef比较像,相当于给类型取一个别名。在C/C++经常这么写:typedef unsigned int Uint32;typedef void (*OutputFunc)(const char* msg, int len);struct Person{ int no; char name[12];} P;而go语言type可以这么用:package mainimport "fmt"type long = int64

2021-06-27 23:16:44 377 2

原创 关于weak_ptr在工作中的使用

前一段时间遇到一个问题,做的一个buff管理器,buff执行过程中需要用到释放skill的对象,当时为了方便和执行效率,直接保存了对象的指针(没有用智能指针)。后来业务逻辑修改了,在buff存在期间,释放skill对象可能已经销毁了。这就尴尬了,buff执行过程中可能会用到已经销毁的对象,但是buff管理器不知道,这样会导致未知的错误。 后来处理方法是buff对象直接保存释放skill对象的ID,每次执行的时候,从map中取一下,如果可以取到就是表明对象还活着,取不到表示对象已经死了。这...

2021-06-24 15:17:30 426

原创 golang学习笔记(二)

go语言是静态强类型语言,与C语言类似,数据类型常用的主要有布尔类型(true、false),数字类型(整形int ,浮点类型float32,float64),字符类型(byte)以及字符串类型(string)。变量名由字母,下划线,数字构成,不能与数字开头,这点和C语言一样。变量声明以关键字var开头,var 变量名 数据类型 也可以一次声明多个 var 变量名1,变量名2 数据类型package mainimport "fmt"func main() { //go语言如果没有初

2021-06-23 23:42:44 124 2

原创 golang学习笔记(一)

golang是一门静态强类型、编译型语言,是谷歌公司发布2009年发布的。算是一门比较年轻的语言,不同于C++没有很多包袱,轻装上阵,所以go语言要简单很多,同时对并发的支持很好。设计上没有依赖,不像C/C++依赖很多动态库(通过命令ldd,可以查看依赖库情况),实现一次编译,到处拷贝,部署简单。作为一个一直做后端开发的码农,C++一直是我的主要用的开发语言,如今打算学习一下go,最后用go将C++项目重构一下。go语言安装很简单,一直next就可以,唯一注意目录不要有中文,需要配置一下环境变量

2021-06-22 22:45:24 122 3

原创 最短路径-----Dijkstra算法相关以及实现

最近开始时开发时,需要计算行军的最短路径,重温大学时的数据结构那本书中关于Dijkstra算法相关内容,看了网上一些博客,将我对算法的浅显的理解记下来。如图是一个多个节点,每个节点多条路劲的,没有方向的路径图。以A为起点找出A到B、C、D、E、F的最短路径。 Dijkstra算法只会关注当前结点能连接到的节点,不关注远处连接不了的点。比如要求A到B、C、D、E、F的最短路径,只会关注A->B和A->C,而不会关注A到D、E、F节点。A->B...

2021-06-04 13:42:58 194

原创 网络服务器端程序线程划分

1.I/O线程,这种线程的主循环是多路复用,阻塞地等待在select/poll/epoll_wait系统调用上。这类线程也处理定时事件。当然它的功能不止IO,有些简单计算也可以放在其中,比如消息的编码或者解码。2.计算线程(习惯叫事件线程),这类线程的主循环是阻塞的事件队列,阻塞地等在条件变量上,这样的线程可以有多个位于线程池中,这种线程通常不涉及I/O,一般要避免任何阻塞操作(尽量不要有系统调用函数,如read write之类的函数)。当然根据实际情况也可以试非阻塞的事件循环,不断轮询从事件队列中取得

2021-02-05 10:28:05 298

原创 多线程编程,互斥锁与自旋锁注意事项

多线程编程中会涉及公共资源相互竞争问题,就像火车上的厕所,只用一个人可以上,当有人进入后就会加锁。多线程一样为了避免多个线程同时访问公共资源也需要加锁。一般会用到互斥锁或者自旋锁。互斥锁线程请求锁,如果请求到加锁执行,然后解锁。如果没有请求到锁,那么就把线程放入等待队列里,将线程挂起。如果持有锁的线程执行完并且释放锁,则会唤醒等待队列里的某个线程去执行,那么它就能获取到锁。互斥锁会涉及线程挂起,导致线程的状态切换(用户态->内核态),这样会有上下文切换,这样性能会有损耗。Linux内核提供

2020-11-12 10:25:49 483

原创 多线程编程类封装,线程id注意事项

一般在多线程编程的时候,我们会封装一个thread类,其中封装了线程的开启、回调以及终止等操作。c++的线程不同于java,线程没有名字。我们通过thread类还可以定义线程的名字,同时在类实现时,每一个线程都应该有一个线程id。 POSIX threads库提供了pthread_self函数,调用pthread_self函数可以返回当前线程的标识符,返回值类型是pthread_t,通过调用pthread_equal函数来比对两个线程标识符是否相等。而通过调用pthread_self函数返...

2020-10-29 16:14:28 345

原创 C++内存问题以及解决办法和shared_ptr基本原理实现

C++这门复杂而灵活的语言不同于其他很多语言诸如Java,需要编程者通过new/delete来动态管理内存,这一不留神就容易造成内存泄漏,严重影响程序的健壮性。 shared_ptr作为一种智能指针,原理很简单,它的作用跟指针一样,不过shared_ptr保存了一个引用计数,记录了有多少个shared_ptr共同指向同一对象。每当有新的shared_ptr指向该对象时计数加一,如果指向对象的shared_ptr销毁时计数减一,一旦对象的引用计数变成0,这个对象自动被销毁,这样对内存泄漏的控制...

2020-10-26 15:58:35 809

原创 通过setjmp与longjmp实现try catch

函数头文件:#include <setjmp.h>int setjmp(jmp_buf env);调用setjmp将堆栈上下文保存在jmp_buf结构体中(入栈),供longjmp稍后使用。如果直接调用返回0,如果使用保存的上下文从longjmp返回,则返回保存值非零。void longjmp(jmp_buf env, int val);调用longjmp程序跳转到最后一次使用相应env参数调用setjmp处,调用longjmp后setjmp不能返回0,如果longjmp第二个参数

2020-10-24 12:58:38 519

原创 论原子性操作使用场景以及原子类封装

原子性操作,意思是说CPU执行的过程中,操作的粒度如同原子,不可再细分中途不能被打断。众所周知如今CPU执行程序是分时间片执行的,线程是CPU调度的单位。每一个线程的执行都会分到一个时间片,当线程的时间片用完后,线程挂起等待下次调度,然后在从调度队列中取出其他线程开始执行。 对于多线程程序中,如果有公共资源,多个线程可以并行,就会涉及到一个线程在操作公共资源时,另一线程也开始操作公共资源,造成数据错乱的问题。 解决办法就是加锁,通过加锁实现了线程对公共资源操作一次的完整性,哪怕操作...

2020-10-13 18:00:56 876

原创 在开发中用lua做配置文件

我们在开发中常用ini或者xml做配置文件,ini配置只有简单的节、键、值组成,无法应对复杂的多层次配置,或者列表配置。xml文件层次分明,写起来比较复杂。lua脚本作为一门轻量小巧的脚本语言,常用在游戏开发中或者web应用中。lua脚本简单清晰,很适合做配置文件。可以做服务器端程序的配置文件,下面是封装了一个类,用于来读取lua配置文件。#include <stdio.h>#include <stdlib.h>#include <unistd.h>#...

2020-10-12 15:37:11 1415

原创 游戏的大地图中对象状态的同步

游戏战场的地图很大,在地图上活跃着成百上千的玩家。服务器是如何同步地图上玩家英雄、怪物、建筑等地图对象的状态呢?肯定不能把地图上所有对象的状态同步给玩家,因为数据量实在太大,服务器只要将玩家视野周围的地图对象状态同步给玩家即可,这样既可以满足需求,下面介绍如何同步玩家视野周围的地图对象状态。 可以把地图看成由很多个小方格组成的,而玩家英雄、怪物、建筑都在方格上,而服务器只需要将视野所在格子周围格子上的地图对象的状态变化同步给玩家即可,这正是玩家屏幕所看到的区域。如图: 我们可...

2020-09-22 17:17:59 912

原创 UDP通讯相关以及与epoll结合的回射服务器

UDP属于用户数据报协议,属于传输层协议。提供面向无连接的、不可靠的传输,没有拥塞控制和超时重传机制。相对于TCP面向连接的,提供可靠传输的传输层协议,UDP也有其应用场景。UDP在首部开销小,传输速度快的优点,应用也很广泛比音视频通话,网络直播,游戏中帧同步等等。不同于TCP这样的流式套接字,对于UDP不用处理粘包问题。UDP是面向报文的,对应用层交付的报文,直接添加协议头就交付给IP层,不会对报文进行合并或者拆分,保留了报文的边界。所以接收端的socket缓冲区采用链式结构保存每一个到达的UDP数据

2020-09-22 13:00:24 1435

原创 网络游戏中使用TCP处理粘包的两种思路

TCP作为面向连接的、可靠的、基于字节流的传输层网络通讯协议,应用非常广泛。由于是基于字节流的网络协议,我们使用TCP协议进行网络通讯,底层会将发送的数据进行拆分组合,这样一般都会遇到粘包和半包的问题。一般我们为了解决粘包问题,解决粘包和半包问题有两种思路:将发送数据封装成包,就像TCP协议一样加一个包头。 使用分隔符,对发送数据时在数据尾部加入分割符。 第一种方式处理粘包,可以在要发送数据的首部加2字节大小的包头储存数据包的长度,在数据结束位置加一个字节的包尾用来校验数据。校...

2020-09-07 19:15:05 656

原创 eventfd与多路复用结合实现进程/线程间通讯

eventfeventfd是Linux2.6.27内核版本加入的系统调用,主要可以用来进程/线程间通讯。用法很简单,通过调 用inteventfd(unsignedintinitval,intflags)函数返回一个文件描述符,通过read和write函数进行进程/线程间通讯。 调用eventfd函数会创建一个eventfd对象,可以被用户空间的应用程序用来作为事件等待/通知机制。eventfd函数返回eventfd对象的文件描述符,通过操作文件描述符来进行事件等待和...

2020-09-07 14:48:54 736

原创 timerfd与多路复用完美融合实现定时器

在服务器开发中一般都会用到多路复用技术来处理网络I/O事件,至于服务器中的定时器我们可以用timerfd来实现,可以想监听网络事件一样处理定时器事件,与select/poll/epoll配合使用。使用timefd做定时器需要三个函数:timerfd_create、timerfd_settime、timerfd_gettime。timerfd_create函数#include <sys/timerfd.h>int timerfd_create(int clockid, int ...

2020-09-03 09:31:45 646

原创 网络游戏战斗系统之skill系统具体设计实现

网络游戏的战斗系统中,技能是战斗单元释放作用在目标上造成一系列的技能效果。一个战斗单元可以拥有多个技能,根据玩家操作或者自动,判断技能释放满足条件(cd,魔力等条件)释放技能,这时候根据技能生成一个技能Action对象,最终在目标上生效。 每一个战斗单元需要一个skillManger对象管理其拥有技能(skill对象),skill对象保存技能的基础配置数据,技能上次释放时间等信息,用于生成skillAction对象。而skillAction对象根据时间线执行,最终在目标上产生技能效果。...

2020-08-31 18:57:03 1513

原创 网络游戏战斗系统之buff系统具体设计实现

在网络游戏中的战斗形式多种多样,不同游戏的战斗逻辑也有很大的差异。但是一般都会涉及技能系统和buff系统,两种之间相互关联,技能可以产生buff作用在目标上,影响目标。同时buff也会影响技能的释放效果,两者都可以算得上游戏战斗系统最重要的元素。 在游戏战斗中有许多各种各样复杂的buff和技能,这些buff和技能是由基础的buff和技能效果机制组合形成的。通过基础的机制效果的组合,形成各种各样的复杂的buff和技能。这些基础的buff效果机制和技能效果机制支持策划通过配置表配置数据,来影响...

2020-08-29 10:50:09 3585

原创 对Linux下系统调用的理解

系统调用是应用程序在执行过程中向系统内核申请服务的过程,这些服务包含硬件相关的服务、申请资源、创建进程或者线程。比如要程序要从硬盘读取数据,需要调用fopen打开文件,fread读取文件内容等。应用程序运行在用户空间,系统以及驱动程序运行在内核空间。如图:举一个形象的例子,我们去银行柜台取钱,客户与银行柜员被玻璃窗分开,柜员所在空间可以看成内核空间,客户所在银行大厅可以看做用户空间。客户把银行卡递给银行柜员,然后根据银行卡上面的存款数目,取出相应的钱,可以看成一次系统调用。至于为什么用户空.

2020-08-24 11:28:43 280

原创 网络游戏对战数据同步浅析

在网络游戏中玩家间对战,数据同步一般有两种方式,状态同步和帧同步。 在状态同步中战斗逻辑放在服务器端,客户端上传玩家操作,根据操作由服务器验算战斗逻辑,然后服务器周期性的将逻辑数据比如玩家的坐标、朝向、属性值等数据发送给客户端。而客户端就相当于一个播放器。 状态同步中这样的优点是不会存在数据不一致问题,因为是同步状态断线重连恢复比较快,由于逻辑在服务器端所以作弊比较困难,但是缺点是流量消耗大,对于实时性要求比较高的游戏,手感也会差。 在帧同步中战斗逻辑放在客户端,服务器会...

2020-08-21 16:54:25 1099

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除