mirror of
https://gitee.com/johng/gf.git
synced 2024-12-03 04:37:49 +08:00
commit
ebcc81c1ee
@ -11,6 +11,7 @@
|
||||
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|gitee|¥50.00
|
||||
|[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00
|
||||
|[arden](https://github.com/arden)|alipay|¥10.00
|
||||
|潘兄|wechat|¥100.00
|
||||
|土豆相公|alipay|¥66.60
|
||||
|上海金保证网络科技|bank|¥2000.00
|
||||
|
60
RELEASE.MD
60
RELEASE.MD
@ -1,3 +1,63 @@
|
||||
# `v1.7.0`
|
||||
## 新功能/改进
|
||||
1. 重构改进`glog`模块:
|
||||
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效
|
||||
- 增加日志内容的异步输出特性:https://goframe.org/os/glog/async
|
||||
- 增加日志输出内容的`Json`格式支持:https://goframe.org/os/glog/json
|
||||
- 增加`Flags`额外特性支持,包括文件行号打印、自定义时间格式、异步输出等特性控制:https://goframe.org/os/glog/flags
|
||||
- 增加`Writer`接口支持,便于开发者进行自定义的日志功能扩展,或者与第三方服务/模块对接集成:https://goframe.org/os/glog/writer
|
||||
- 修改`SetStdPrint`方法名为`SetStdoutPrint`
|
||||
- 修改链式方法`StdPrint`方法名为`Stdout`
|
||||
- 标记淘汰`*fln`日志输出方法,`*f`方法支持自动的换行输出
|
||||
- 新增更多的链式方法支持:https://goframe.org/os/glog/chain
|
||||
1. 重构改进`gmap`模块:
|
||||
- 增加更多数据格式支持:`HashMap`/`ListMap`/`TreeMap`
|
||||
- 简化类型名称,如`gmap.StringInterfaceMap`简化为`gmap.StrAnyMap`
|
||||
- 改进`Map/Keys/Values`方法以提高性能
|
||||
- 修改`BatchSet`/`BatchRemove`方法名为`Sets`/`Removes`
|
||||
- 新增更多功能方法支持:https://goframe.org/container/gmap/index
|
||||
1. 改进`gtime`时间模块:
|
||||
- 增加并完善更多的类`PHP`时间格式支持
|
||||
- 新增更多功能方法,如`FormatTo`/`LayoutTo`等等
|
||||
- 详见开发文档:https://goframe.org/os/gtime/index
|
||||
1. 改进`gdb`数据库模块:
|
||||
- 增加对继承结构体的数据转换支持:https://goframe.org/database/gdb/senior
|
||||
- 新增`GetLastSql`方法,用以在调试模式下获取最近一条执行的SQL语句
|
||||
- 其他的细节处理改进
|
||||
1. 改进`gtcp`通信模块:
|
||||
- 完善处理细节,提高通信性能;
|
||||
- 增加`TLS`服务端/客户端通信支持:https://goframe.org/net/gtcp/tls
|
||||
- 增加简单协议支持,便于开发者封包/解包,并解决粘包/半包问题:https://goframe.org/net/gtcp/conn/pkg
|
||||
- TCP服务端增加`Close`方法
|
||||
- 更多细节查看开发文档:https://goframe.org/net/gtcp/index
|
||||
1. 改进`gconv`类型转换模块
|
||||
- 修改`gconv.TimeDuration`转换方法名称为`gconv.Duration`
|
||||
- 新增`gconv.StructDeep`及`gconv.MapDeep`方法,支持递归转换
|
||||
- 详见开发文档:https://goframe.org/util/gconv/struct
|
||||
1. 改进`ghttp`模块:
|
||||
- 日志输出增加`http/https`字段:https://goframe.org/net/ghttp/logs
|
||||
- 新增`ghttp.Server.SetKeepAlive`设置方法,用以开启/关闭`KeepAlive`特性
|
||||
- 增加`ghttp.Request.GetUrl`方法,用以获取当前完整的URL请求地址
|
||||
- `ghttp.Client`客户端支持开发者自定义`Transport`属性,`ghttp.Client.Post`方法支持`浏览器模式`:https://goframe.org/net/ghttp/client
|
||||
1. 新增`gtree`树形数据结构容器支持:https://goframe.org/container/gtree/index
|
||||
1. 改进`gudp`通信模块,具体请参考开发文档:https://goframe.org/net/gudp/index
|
||||
1. 改进`gcfg`配置管理模块,所有`Get*`方法增加默认值支持:https://goframe.org/os/gcfg/index
|
||||
1. `gredis`模块新增`DoVar`/`ReceiveVar`方法以便于开发者对执行结果进行灵活的数据格式转换:https://goframe.org/database/gredis/index
|
||||
1. `gcache`模块`BatchSet`/`BatchRemove`方法名修改为`Sets`/`Removes`
|
||||
1. 改进`gjson`/`gparser`模块,增加更多方法:https://goframe.org/encoding/gjson/index
|
||||
1. 改进`gfile.MainPkgPath`方法,以支持不同平台的开发环境;
|
||||
1. 改进`grpool`协程池模块,提高执行性能:https://goframe.org/os/grpool/index
|
||||
1. 改进`TryCatch`方法,当开发者不传递`Catch`参数时,默认抑制并忽略错误的处理
|
||||
1. 改进`gmlock`模块,增加`TryLockFunc`/`TryRLockFunc`方法,并且为`gmlock.Mutex`高级互斥锁对象增加`TryLockFunc`/`TryRLockFunc`方法
|
||||
1. 去除`gvar.VarRead`接口类型支持
|
||||
|
||||
## Bug Fix
|
||||
1. 解决`gdb`模块与其他第三方`ORM`模块同时使用的冲突;
|
||||
1. 修复`gcron.AddOnce`方法的细节逻辑问题;
|
||||
1. 修复内部`empty`模块的`IsEmpty`方法对结构体属性的空校验错误;
|
||||
1. 修复`gview`模板引擎的并发安全问题;
|
||||
1. 修复`ghttp.Server`的SESSION初始化过期时间问题;
|
||||
|
||||
# `v1.6.0` (2019-04-09)
|
||||
|
||||
## 新功能/改进
|
||||
|
2
TODO.MD
2
TODO.MD
@ -47,7 +47,7 @@
|
||||
1. gtimer增加DelayAdd*方法返回Entry对象,以便DelayAdd*的定时任务也能进行状态控制;gcron同理需要改进;
|
||||
1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式;
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
|
||||
1. grpool增加支持阻塞添加任务接口;
|
||||
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gchan provides graceful channel for safe operations.
|
||||
// Package gchan provides graceful channel for no panic operations.
|
||||
//
|
||||
// It's safe to call Chan.Push/Close functions repeatedly.
|
||||
package gchan
|
||||
@ -14,12 +14,13 @@ import (
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
)
|
||||
|
||||
// Graceful channel.
|
||||
type Chan struct {
|
||||
channel chan interface{}
|
||||
closed *gtype.Bool
|
||||
}
|
||||
|
||||
// New creates a graceful channel with given limit.
|
||||
// New creates a graceful channel with given <limit>.
|
||||
func New(limit int) *Chan {
|
||||
return &Chan {
|
||||
channel : make(chan interface{}, limit),
|
||||
@ -31,7 +32,7 @@ func New(limit int) *Chan {
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Push(value interface{}) error {
|
||||
if c.closed.Val() {
|
||||
return errors.New("closed")
|
||||
return errors.New("channel is closed")
|
||||
}
|
||||
c.channel <- value
|
||||
return nil
|
||||
@ -39,6 +40,7 @@ func (c *Chan) Push(value interface{}) error {
|
||||
|
||||
// Pop pops value from channel.
|
||||
// If there's no value in channel, it would block to wait.
|
||||
// If the channel is closed, it will return a nil value immediately.
|
||||
func (c *Chan) Pop() interface{} {
|
||||
return <- c.channel
|
||||
}
|
||||
|
@ -37,8 +37,8 @@ const (
|
||||
)
|
||||
|
||||
// New returns an empty queue object.
|
||||
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited by default.
|
||||
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib chan.
|
||||
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited in default.
|
||||
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib channel.
|
||||
func New(limit...int) *Queue {
|
||||
q := &Queue {
|
||||
closed : make(chan struct{}, 0),
|
||||
@ -103,7 +103,7 @@ func (q *Queue) Pop() interface{} {
|
||||
|
||||
// Close closes the queue.
|
||||
// Notice: It would notify all goroutines return immediately,
|
||||
// which are being blocked reading by Pop method.
|
||||
// which are being blocked reading using Pop method.
|
||||
func (q *Queue) Close() {
|
||||
close(q.C)
|
||||
close(q.events)
|
||||
|
@ -25,18 +25,18 @@ func Encrypt(plainText []byte, key []byte, iv...[]byte) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
blockSize := block.BlockSize()
|
||||
plainText = PKCS5Padding(plainText, blockSize)
|
||||
ivValue := ([]byte)(nil)
|
||||
plainText = PKCS5Padding(plainText, blockSize)
|
||||
ivValue := ([]byte)(nil)
|
||||
if len(iv) > 0 {
|
||||
ivValue = iv[0]
|
||||
} else {
|
||||
ivValue = []byte(ivDefValue)
|
||||
}
|
||||
blockMode := cipher.NewCBCEncrypter(block, ivValue)
|
||||
ciphertext := make([]byte, len(plainText))
|
||||
blockMode.CryptBlocks(ciphertext, plainText)
|
||||
cipherText := make([]byte, len(plainText))
|
||||
blockMode.CryptBlocks(cipherText, plainText)
|
||||
|
||||
return ciphertext, nil
|
||||
return cipherText, nil
|
||||
}
|
||||
|
||||
// AES解密, 使用CBC模式,注意key必须为16/24/32位长度,iv初始化向量为非必需参数
|
||||
|
@ -136,7 +136,10 @@ func LoadContent(data interface{}, unsafe...bool) (*Json, error) {
|
||||
var err error
|
||||
var result interface{}
|
||||
b := gconv.Bytes(data)
|
||||
t := "json"
|
||||
t := ""
|
||||
if len(b) == 0 {
|
||||
return New(nil, unsafe...), nil
|
||||
}
|
||||
// auto check data type
|
||||
if json.Valid(b) {
|
||||
t = "json"
|
||||
|
@ -219,13 +219,13 @@ func Redis(name...string) *gredis.Redis {
|
||||
Pass : array[4],
|
||||
})
|
||||
} else {
|
||||
glog.Errorfln(`invalid redis node configuration: "%s"`, line)
|
||||
glog.Errorf(`invalid redis node configuration: "%s"`, line)
|
||||
}
|
||||
} else {
|
||||
glog.Errorfln(`configuration for redis not found for group "%s"`, group)
|
||||
glog.Errorf(`configuration for redis not found for group "%s"`, group)
|
||||
}
|
||||
} else {
|
||||
glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath())
|
||||
glog.Errorf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
@ -7,7 +7,7 @@
|
||||
package empty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 判断给定的变量是否为空。
|
||||
@ -35,17 +35,22 @@ func IsEmpty(value interface{}) bool {
|
||||
case string: return value == ""
|
||||
case []byte: return len(value) == 0
|
||||
default:
|
||||
// 最后通过反射来判断
|
||||
// Finally using reflect.
|
||||
rv := reflect.ValueOf(value)
|
||||
if rv.IsNil() {
|
||||
return true
|
||||
}
|
||||
kind := rv.Kind()
|
||||
switch kind {
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
switch rv.Kind() {
|
||||
case reflect.Chan,
|
||||
reflect.Map,
|
||||
reflect.Slice,
|
||||
reflect.Array:
|
||||
return rv.Len() == 0
|
||||
|
||||
case reflect.Func,
|
||||
reflect.Ptr,
|
||||
reflect.Interface,
|
||||
reflect.UnsafePointer:
|
||||
if rv.IsNil() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@ -134,7 +134,9 @@ func (r *Response) WriteStatus(status int, content...string) {
|
||||
// 状态码注册回调函数处理
|
||||
if status != http.StatusOK {
|
||||
if f := r.request.Server.getStatusHandler(status, r.request); f != nil {
|
||||
f(r.request)
|
||||
r.Server.niceCallFunc(func() {
|
||||
f(r.request)
|
||||
})
|
||||
// 防止多次设置(http: multiple response.WriteHeader calls)
|
||||
if r.Status == 0 {
|
||||
r.WriteHeader(status)
|
||||
|
@ -52,7 +52,10 @@ func (r *Response) buildInVars(params...map[string]interface{}) map[string]inter
|
||||
} else {
|
||||
vars = make(map[string]interface{})
|
||||
}
|
||||
vars["Config"] = gins.Config().GetMap("")
|
||||
// 当配置文件不存在时就不赋值该模板变量,不然会报错
|
||||
if c := gins.Config(); c.FilePath() != "" {
|
||||
vars["Config"] = c.GetMap("")
|
||||
}
|
||||
vars["Cookie"] = r.request.Cookie.Map()
|
||||
vars["Session"] = r.request.Session.Map()
|
||||
vars["Get"] = r.request.GetQueryMap()
|
||||
|
@ -389,7 +389,7 @@ func (s *Server) Run() error {
|
||||
// 阻塞等待服务执行完成
|
||||
<- s.closeChan
|
||||
|
||||
glog.Printfln("%d: all servers shutdown", gproc.Pid())
|
||||
glog.Printf("%d: all servers shutdown", gproc.Pid())
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ func Wait() {
|
||||
// 阻塞等待服务执行完成
|
||||
<- allDoneChan
|
||||
|
||||
glog.Printfln("%d: all servers shutdown", gproc.Pid())
|
||||
glog.Printf("%d: all servers shutdown", gproc.Pid())
|
||||
}
|
||||
|
||||
|
||||
|
@ -129,7 +129,7 @@ func forkReloadProcess(newExeFilePath...string) error {
|
||||
buffer, _ := gjson.Encode(sfm)
|
||||
p.Env = append(p.Env, gADMIN_ACTION_RELOAD_ENVKEY + "=" + string(buffer))
|
||||
if _, err := p.Start(); err != nil {
|
||||
glog.Errorfln("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer))
|
||||
glog.Errorf("%d: fork process failed, error:%s, %s", gproc.Pid(), err.Error(), string(buffer))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -147,7 +147,7 @@ func forkRestartProcess(newExeFilePath...string) error {
|
||||
env = append(env, gADMIN_ACTION_RESTART_ENVKEY + "=1")
|
||||
p := gproc.NewProcess(path, os.Args, env)
|
||||
if _, err := p.Start(); err != nil {
|
||||
glog.Errorfln("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
|
||||
glog.Errorf("%d: fork process failed, error:%s", gproc.Pid(), err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -197,14 +197,14 @@ func restartWebServers(signal string, newExeFilePath...string) error {
|
||||
}
|
||||
} else {
|
||||
if err := forkReloadProcess(newExeFilePath...); err != nil {
|
||||
glog.Printfln("%d: server restarts failed", gproc.Pid())
|
||||
glog.Printf("%d: server restarts failed", gproc.Pid())
|
||||
serverProcessStatus.Set(gADMIN_ACTION_NONE)
|
||||
return err
|
||||
} else {
|
||||
if len(signal) > 0 {
|
||||
glog.Printfln("%d: server restarting by signal: %s", gproc.Pid(), signal)
|
||||
glog.Printf("%d: server restarting by signal: %s", gproc.Pid(), signal)
|
||||
} else {
|
||||
glog.Printfln("%d: server restarting by web admin", gproc.Pid())
|
||||
glog.Printf("%d: server restarting by web admin", gproc.Pid())
|
||||
}
|
||||
|
||||
}
|
||||
@ -216,12 +216,12 @@ func restartWebServers(signal string, newExeFilePath...string) error {
|
||||
func shutdownWebServers(signal...string) {
|
||||
serverProcessStatus.Set(gADMIN_ACTION_SHUTINGDOWN)
|
||||
if len(signal) > 0 {
|
||||
glog.Printfln("%d: server shutting down by signal: %s", gproc.Pid(), signal[0])
|
||||
glog.Printf("%d: server shutting down by signal: %s", gproc.Pid(), signal[0])
|
||||
// 在终端信号下,立即执行关闭操作
|
||||
forceCloseWebServers()
|
||||
allDoneChan <- struct{}{}
|
||||
} else {
|
||||
glog.Printfln("%d: server shutting down by api", gproc.Pid())
|
||||
glog.Printf("%d: server shutting down by api", gproc.Pid())
|
||||
// 非终端信号下,异步1秒后再执行关闭,
|
||||
// 目的是让接口能够正确返回结果,否则接口会报错(因为web server关闭了)
|
||||
gtimer.SetTimeout(time.Second, func() {
|
||||
|
@ -130,7 +130,7 @@ func (s *gracefulServer) doServe() error {
|
||||
if s.fd != 0 {
|
||||
action = "reloaded"
|
||||
}
|
||||
glog.Printfln("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.addr)
|
||||
glog.Printf("%d: %s server %s listening on [%s]", gproc.Pid(), s.getProto(), action, s.addr)
|
||||
s.status = SERVER_STATUS_RUNNING
|
||||
err := s.httpServer.Serve(s.listener)
|
||||
s.status = SERVER_STATUS_STOPPED
|
||||
@ -173,7 +173,7 @@ func (s *gracefulServer) shutdown() {
|
||||
return
|
||||
}
|
||||
if err := s.httpServer.Shutdown(context.Background()); err != nil {
|
||||
glog.Errorfln("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.addr, err)
|
||||
glog.Errorf("%d: %s server [%s] shutdown error: %v", gproc.Pid(), s.getProto(), s.addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ func (s *gracefulServer) close() {
|
||||
return
|
||||
}
|
||||
if err := s.httpServer.Close(); err != nil {
|
||||
glog.Errorfln("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.addr, err)
|
||||
glog.Errorf("%d: %s server [%s] closed error: %v", gproc.Pid(), s.getProto(), s.addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ func (s *Server) setHandler(pattern string, handler *handlerItem, hook ... strin
|
||||
caller := s.getHandlerRegisterCallerLine(handler)
|
||||
if len(hook) == 0 {
|
||||
if item, ok := s.routesMap[regkey]; ok {
|
||||
glog.Errorfln(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file)
|
||||
glog.Errorf(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func (d *Domain) Group(prefix...string) *RouterGroup {
|
||||
func (g *RouterGroup) Bind(items []GroupItem) {
|
||||
for _, item := range items {
|
||||
if len(item) < 3 {
|
||||
glog.Fatalfln("invalid router item: %s", item)
|
||||
glog.Fatalf("invalid router item: %s", item)
|
||||
}
|
||||
if strings.EqualFold(gconv.String(item[0]), "REST") {
|
||||
g.bind("REST", gconv.String(item[0]) + ":" + gconv.String(item[1]), item[2])
|
||||
@ -124,7 +124,7 @@ func (g *RouterGroup) bind(bindType string, pattern string, object interface{},
|
||||
if len(g.prefix) > 0 {
|
||||
domain, method, path, err := g.server.parsePattern(pattern)
|
||||
if err != nil {
|
||||
glog.Fatalfln("invalid pattern: %s", pattern)
|
||||
glog.Fatalf("invalid pattern: %s", pattern)
|
||||
}
|
||||
if bindType == "HANDLER" {
|
||||
pattern = g.server.serveHandlerKey(method, g.prefix + "/" + strings.TrimLeft(path, "/"), domain)
|
||||
@ -196,7 +196,7 @@ func (g *RouterGroup) bind(bindType string, pattern string, object interface{},
|
||||
g.domain.BindHookHandler(pattern, methods[0], h)
|
||||
}
|
||||
} else {
|
||||
glog.Fatalfln("invalid hook handler for pattern:%s", pattern)
|
||||
glog.Fatalf("invalid hook handler for pattern:%s", pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,11 +50,11 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) {
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
if len(methodMap) > 0 {
|
||||
// 指定的方法名称注册,那么需要使用错误提示
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
|
||||
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
|
||||
pkgPath, ctlName, mname, v.Method(i).Type().String())
|
||||
} else {
|
||||
// 否则只是Debug提示
|
||||
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
|
||||
glog.Debugf(`ignore route method: %s.%s.%s defined as "%s", no match "func()"`,
|
||||
pkgPath, ctlName, mname, v.Method(i).Type().String())
|
||||
}
|
||||
continue
|
||||
@ -108,7 +108,7 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
if _, ok := fval.Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
|
||||
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
|
||||
pkgPath, ctlName, mname, fval.Type().String())
|
||||
return
|
||||
}
|
||||
@ -147,7 +147,7 @@ func (s *Server)BindControllerRest(pattern string, c Controller) {
|
||||
ctlName = fmt.Sprintf(`(%s)`, ctlName)
|
||||
}
|
||||
if _, ok := v.Method(i).Interface().(func()); !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
|
||||
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func()" is required for controller registry`,
|
||||
pkgPath, ctlName, mname, v.Method(i).Type().String())
|
||||
return
|
||||
}
|
||||
|
@ -57,11 +57,11 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) {
|
||||
if !ok {
|
||||
if len(methodMap) > 0 {
|
||||
// 指定的方法名称注册,那么需要使用错误提示
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
|
||||
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
|
||||
pkgPath, objName, mname, v.Method(i).Type().String())
|
||||
} else {
|
||||
// 否则只是Debug提示
|
||||
glog.Debugfln(`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)"`,
|
||||
glog.Debugf(`ignore route method: %s.%s.%s defined as "%s", no match "func(*ghttp.Request)"`,
|
||||
pkgPath, objName, mname, v.Method(i).Type().String())
|
||||
}
|
||||
continue
|
||||
@ -127,7 +127,7 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
|
||||
}
|
||||
faddr, ok := fval.Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
|
||||
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
|
||||
pkgPath, objName, mname, fval.Type().String())
|
||||
return
|
||||
}
|
||||
@ -174,7 +174,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) {
|
||||
}
|
||||
faddr, ok := v.Method(i).Interface().(func(*Request))
|
||||
if !ok {
|
||||
glog.Errorfln(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
|
||||
glog.Errorf(`invalid route method: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" is required for object registry`,
|
||||
pkgPath, objName, mname, v.Method(i).Type().String())
|
||||
continue
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ import (
|
||||
|
||||
// SESSION对象
|
||||
type Session struct {
|
||||
id string // SessionId
|
||||
id string // SessionId
|
||||
data *gmap.StrAnyMap // Session数据
|
||||
server *Server // 所属Server
|
||||
request *Request // 关联的请求
|
||||
server *Server // 所属Server
|
||||
request *Request // 关联的请求
|
||||
}
|
||||
|
||||
// 生成一个唯一的SessionId字符串,长度18位。
|
||||
@ -58,7 +58,7 @@ func (s *Session) init() {
|
||||
// 否则执行初始化创建
|
||||
s.id = s.request.Cookie.MakeSessionId()
|
||||
s.data = gmap.NewStrAnyMap()
|
||||
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge())
|
||||
s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,48 @@ func Test_Params_Json(t *testing.T) {
|
||||
Pass2 : "456",
|
||||
})
|
||||
})
|
||||
s.BindHandler("/json3", func(r *ghttp.Request){
|
||||
type Message struct {
|
||||
Code int `json:"code"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
type ResponseJson struct {
|
||||
Success bool `json:"success"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
ExtData interface{} `json:"ext_data,omitempty"`
|
||||
Paginate interface{} `json:"paginate,omitempty"`
|
||||
Message Message `json:"message,omitempty"`
|
||||
}
|
||||
responseJson := &ResponseJson{
|
||||
Success: true,
|
||||
Data: nil,
|
||||
ExtData: nil,
|
||||
Message: Message{3, "测试", "error"},
|
||||
}
|
||||
r.Response.WriteJson(responseJson)
|
||||
})
|
||||
s.BindHandler("/json4", func(r *ghttp.Request){
|
||||
type Message struct {
|
||||
Code int `json:"code"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
type ResponseJson struct {
|
||||
Success bool `json:"success"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
ExtData interface{} `json:"ext_data,omitempty"`
|
||||
Paginate interface{} `json:"paginate,omitempty"`
|
||||
Message *Message `json:"message,omitempty"`
|
||||
}
|
||||
responseJson := ResponseJson{
|
||||
Success: true,
|
||||
Data: nil,
|
||||
ExtData: nil,
|
||||
Message: &Message{3, "测试", "error"},
|
||||
}
|
||||
r.Response.WriteJson(responseJson)
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
@ -67,7 +109,7 @@ func Test_Params_Json(t *testing.T) {
|
||||
gtest.Assert(map1["password2"], "456")
|
||||
|
||||
map2 := make(map[string]interface{})
|
||||
err2 := json.Unmarshal([]byte(client.GetContent("/json1")), &map2)
|
||||
err2 := json.Unmarshal([]byte(client.GetContent("/json2")), &map2)
|
||||
gtest.Assert(err2, nil)
|
||||
gtest.Assert(len(map2), 4)
|
||||
gtest.Assert(map2["Name"], "john")
|
||||
@ -75,5 +117,18 @@ func Test_Params_Json(t *testing.T) {
|
||||
gtest.Assert(map2["password1"], "123")
|
||||
gtest.Assert(map2["password2"], "456")
|
||||
|
||||
map3 := make(map[string]interface{})
|
||||
err3 := json.Unmarshal([]byte(client.GetContent("/json3")), &map3)
|
||||
gtest.Assert(err3, nil)
|
||||
gtest.Assert(len(map3), 2)
|
||||
gtest.Assert(map3["success"], "true")
|
||||
gtest.Assert(map3["message"], g.Map{"body":"测试", "code":3, "error":"error"})
|
||||
|
||||
map4 := make(map[string]interface{})
|
||||
err4 := json.Unmarshal([]byte(client.GetContent("/json4")), &map4)
|
||||
gtest.Assert(err4, nil)
|
||||
gtest.Assert(len(map4), 2)
|
||||
gtest.Assert(map4["success"], "true")
|
||||
gtest.Assert(map4["message"], g.Map{"body":"测试", "code":3, "error":"error"})
|
||||
})
|
||||
}
|
||||
|
@ -85,3 +85,36 @@ func Test_Router_Hook_Priority(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_Hook_Multi(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/multi-hook", func(r *ghttp.Request) {
|
||||
r.Response.Write("show")
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/multi-hook", map[string]ghttp.HandlerFunc {
|
||||
"BeforeServe" : func(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
},
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
// 等待启动完成
|
||||
time.Sleep(time.Second)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/multi-hook"), "12show")
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,12 @@
|
||||
package gtcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
"io"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 封装的链接对象
|
||||
@ -21,7 +22,7 @@ type Conn struct {
|
||||
buffer []byte // 读取缓冲区(用于数据读取时的缓冲区处理)
|
||||
recvDeadline time.Time // 读取超时时间
|
||||
sendDeadline time.Time // 写入超时时间
|
||||
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取完毕后的写入等待间隔
|
||||
recvBufferWait time.Duration // 读取全部缓冲区数据时,读取缓冲区完毕后的等待间隔
|
||||
}
|
||||
|
||||
const (
|
||||
@ -38,6 +39,24 @@ func NewConn(addr string, timeout...int) (*Conn, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建支持TLS加密通信的TCP链接
|
||||
func NewConnTLS(addr string, tlsConfig *tls.Config) (*Conn, error) {
|
||||
if conn, err := NewNetConnTLS(addr, tlsConfig); err == nil {
|
||||
return NewConnByNetConn(conn), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 根据证书和密钥文件创建支持TLS加密通信的TCP链接
|
||||
func NewConnKeyCrt(addr, crtFile, keyFile string) (*Conn, error) {
|
||||
if conn, err := NewNetConnKeyCrt(addr, crtFile, keyFile); err == nil {
|
||||
return NewConnByNetConn(conn), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 将net.Conn接口对象转换为*gtcp.Conn对象
|
||||
func NewConnByNetConn(conn net.Conn) *Conn {
|
||||
return &Conn {
|
||||
@ -50,16 +69,14 @@ func NewConnByNetConn(conn net.Conn) *Conn {
|
||||
}
|
||||
|
||||
// 关闭连接
|
||||
func (c *Conn) Close() {
|
||||
c.conn.Close()
|
||||
func (c *Conn) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
func (c *Conn) Send(data []byte, retry...Retry) error {
|
||||
length := 0
|
||||
for {
|
||||
n, err := c.conn.Write(data)
|
||||
if err != nil {
|
||||
if _, err := c.conn.Write(data); err != nil {
|
||||
// 链接已关闭
|
||||
if err == io.EOF {
|
||||
return err
|
||||
@ -76,18 +93,17 @@ func (c *Conn) Send(data []byte, retry...Retry) error {
|
||||
time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
length += n
|
||||
if length == len(data) {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取数据,指定读取的数据长度(length < 1表示获取所有可读数据),以及重试策略(retry)
|
||||
// 阻塞等待获取指定读取的数据长度,并给定重试策略。
|
||||
//
|
||||
// 需要注意:
|
||||
// 1、往往在socket通信中需要指定固定的数据结构,并在设定对应的长度字段,并在读取数据时便于区分包大小;
|
||||
// 2、当length < 1时表示获取缓冲区所有的数据,但是可能会引起包解析问题(可能出现非完整的包情况),因此需要解析端注意解析策略;
|
||||
// 2、当length < 0时表示获取缓冲区所有的数据,但是可能会引起包解析问题(可能出现粘包/断包情况),因此需要解析端注意解析策略;
|
||||
// 3、当length = 0时表示获取一次缓冲区的数据后立即返回;
|
||||
func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
var err error // 读取错误
|
||||
var size int // 读取长度
|
||||
@ -106,9 +122,11 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
// 如果已经读取到数据(这点很关键,表明缓冲区已经有数据,剩下的操作就是将所有数据读取完毕),
|
||||
// 那么可以设置读取全部缓冲数据的超时时间;如果没有接收到任何数据,那么将会进入读取阻塞(或者自定义的超时阻塞);
|
||||
// 仅对读取全部缓冲区数据操作有效
|
||||
if length <= 0 && index > 0 {
|
||||
if length < 0 && index > 0 {
|
||||
bufferWait = true
|
||||
c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait))
|
||||
if err = c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
size, err = c.reader.Read(buffer[index:])
|
||||
if size > 0 {
|
||||
@ -137,7 +155,9 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
}
|
||||
// 判断数据是否全部读取完毕(由于超时机制的存在,获取的数据完整性不可靠)
|
||||
if bufferWait && isTimeout(err) {
|
||||
c.conn.SetReadDeadline(c.recvDeadline)
|
||||
if err = c.conn.SetReadDeadline(c.recvDeadline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
@ -155,6 +175,10 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
}
|
||||
break
|
||||
}
|
||||
// 只获取一次数据
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer[:index], err
|
||||
}
|
||||
@ -184,14 +208,18 @@ func (c *Conn) RecvLine(retry...Retry) ([]byte, error) {
|
||||
|
||||
// 带超时时间的数据获取
|
||||
func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
c.SetRecvDeadline(time.Now().Add(timeout))
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
return c.Recv(length, retry...)
|
||||
}
|
||||
|
||||
// 带超时时间的数据发送
|
||||
func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
|
||||
c.SetSendDeadline(time.Now().Add(timeout))
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
return c.Send(data, retry...)
|
||||
}
|
||||
|
@ -14,88 +14,118 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// 允许最大的简单协议包大小(byte), 15MB
|
||||
PKG_MAX_SIZE = 0xFFFFFF
|
||||
// 消息包头大小: "总长度"3字节+"校验码"4字节
|
||||
PKG_HEADER_SIZE = 7
|
||||
// 默认允许最大的简单协议包大小(byte), 65535 byte
|
||||
gPKG_MAX_DATA_SIZE = 65535
|
||||
// 简单协议包头大小
|
||||
gPKG_HEADER_SIZE = 3
|
||||
)
|
||||
|
||||
// 根据简单协议发送数据包。
|
||||
// 简单协议数据格式:总长度(24bit)|校验码(32bit)|数据(变长)。
|
||||
// 注意:
|
||||
// 1. "总长度"包含自身3字节及"校验码"4字节。
|
||||
// 2. 由于"总长度"为3字节,并且使用的BigEndian字节序,因此最后返回的buffer使用了buffer[1:]。
|
||||
func (c *Conn) SendPkg(data []byte, retry...Retry) error {
|
||||
length := uint32(len(data))
|
||||
if length > PKG_MAX_SIZE - PKG_HEADER_SIZE {
|
||||
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, PKG_MAX_SIZE - PKG_HEADER_SIZE))
|
||||
// 数据读取选项
|
||||
type PkgOption struct {
|
||||
MaxSize int // (byte)数据读取的最大包大小,最大不能超过3字节(0xFFFFFF,15MB),默认为65535byte
|
||||
Retry Retry // 失败重试
|
||||
}
|
||||
|
||||
// getPkgOption wraps and returns the PkgOption.
|
||||
// If no option given, it returns a new option with default value.
|
||||
func getPkgOption(option...PkgOption) (*PkgOption, error) {
|
||||
pkgOption := PkgOption{}
|
||||
if len(option) > 0 {
|
||||
pkgOption = option[0]
|
||||
}
|
||||
if pkgOption.MaxSize == 0 {
|
||||
pkgOption.MaxSize = gPKG_MAX_DATA_SIZE
|
||||
} else if pkgOption.MaxSize > 0xFFFFFF {
|
||||
return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxSize, 0xFFFFFF)
|
||||
}
|
||||
return &pkgOption, nil
|
||||
}
|
||||
|
||||
// 根据简单协议发送数据包。
|
||||
//
|
||||
// 简单协议数据格式:数据长度(24bit)|数据字段(变长)。
|
||||
//
|
||||
// 注意:
|
||||
// 1. "数据长度"仅为"数据字段"的长度,不包含头信息的长度字段3字节。
|
||||
// 2. 由于"数据长度"为3字节,并且使用的BigEndian字节序,因此这里最后返回的buffer使用了buffer[1:]。
|
||||
func (c *Conn) SendPkg(data []byte, option...PkgOption) error {
|
||||
pkgOption, err := getPkgOption(option...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
length := len(data)
|
||||
if length > pkgOption.MaxSize {
|
||||
return errors.New(fmt.Sprintf(`data size %d exceeds max pkg size %d`, length, gPKG_MAX_DATA_SIZE))
|
||||
}
|
||||
buffer := make([]byte, gPKG_HEADER_SIZE + 1 + len(data))
|
||||
binary.BigEndian.PutUint32(buffer[0 : ], uint32(length))
|
||||
copy(buffer[gPKG_HEADER_SIZE + 1 : ], data)
|
||||
if pkgOption.Retry.Count > 0 {
|
||||
return c.Send(buffer[1:], pkgOption.Retry)
|
||||
}
|
||||
buffer := make([]byte, PKG_HEADER_SIZE + 1 + len(data))
|
||||
copy(buffer[PKG_HEADER_SIZE + 1 : ], data)
|
||||
binary.BigEndian.PutUint32(buffer[0 : ], PKG_HEADER_SIZE + length)
|
||||
binary.BigEndian.PutUint32(buffer[4 : ], Checksum(data))
|
||||
//fmt.Println("SendPkg:", buffer[1:])
|
||||
return c.Send(buffer[1:], retry...)
|
||||
return c.Send(buffer[1:])
|
||||
}
|
||||
|
||||
// 简单协议: 带超时时间的数据发送
|
||||
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
|
||||
c.SetSendDeadline(time.Now().Add(timeout))
|
||||
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) error {
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
return c.SendPkg(data, retry...)
|
||||
return c.SendPkg(data, option...)
|
||||
}
|
||||
|
||||
// 简单协议: 发送数据并等待接收返回数据
|
||||
func (c *Conn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
|
||||
if err := c.SendPkg(data, retry...); err == nil {
|
||||
return c.RecvPkg(retry...)
|
||||
func (c *Conn) SendRecvPkg(data []byte, option...PkgOption) ([]byte, error) {
|
||||
if err := c.SendPkg(data, option...); err == nil {
|
||||
return c.RecvPkg(option...)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 简单协议: 发送数据并等待接收返回数据(带返回超时等待时间)
|
||||
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
if err := c.SendPkg(data, retry...); err == nil {
|
||||
return c.RecvPkgWithTimeout(timeout, retry...)
|
||||
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) ([]byte, error) {
|
||||
if err := c.SendPkg(data, option...); err == nil {
|
||||
return c.RecvPkgWithTimeout(timeout, option...)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 简单协议: 获取一个数据包。
|
||||
func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
|
||||
func (c *Conn) RecvPkg(option...PkgOption) (result []byte, err error) {
|
||||
var temp []byte
|
||||
var length uint32
|
||||
var length int
|
||||
pkgOption, err := getPkgOption(option...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
// 先根据对象的缓冲区数据进行计算
|
||||
for {
|
||||
if len(c.buffer) >= PKG_HEADER_SIZE {
|
||||
// 注意"总长度"为3个字节,不满足4个字节的uint32类型,因此这里"低位"补0
|
||||
length = binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]})
|
||||
// 解析的大小是否符合规范
|
||||
if length == 0 || length + PKG_HEADER_SIZE > PKG_MAX_SIZE {
|
||||
c.buffer = c.buffer[1:]
|
||||
continue
|
||||
if len(c.buffer) >= gPKG_HEADER_SIZE {
|
||||
// 注意"数据长度"为3个字节,不满足4个字节的uint32类型,因此这里"低位"补0
|
||||
length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
|
||||
// 解析的大小是否符合规范,清空从该连接接收到的所有数据包
|
||||
if length < 0 || length > pkgOption.MaxSize {
|
||||
c.buffer = c.buffer[:0]
|
||||
return nil, fmt.Errorf(`invalid package size %d`, length)
|
||||
}
|
||||
// 不满足包大小,需要继续读取
|
||||
if uint32(len(c.buffer)) < length {
|
||||
if len(c.buffer) < length + gPKG_HEADER_SIZE {
|
||||
break
|
||||
}
|
||||
// 数据校验
|
||||
if binary.BigEndian.Uint32(c.buffer[3 : PKG_HEADER_SIZE]) != Checksum(c.buffer[PKG_HEADER_SIZE : length]) {
|
||||
c.buffer = c.buffer[1:]
|
||||
continue
|
||||
}
|
||||
result = c.buffer[PKG_HEADER_SIZE : length]
|
||||
c.buffer = c.buffer[length: ]
|
||||
result = c.buffer[gPKG_HEADER_SIZE : gPKG_HEADER_SIZE + length]
|
||||
c.buffer = c.buffer[gPKG_HEADER_SIZE + length: ]
|
||||
return
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
// 读取系统socket缓冲区的完整数据
|
||||
temp, err = c.Recv(-1, retry...)
|
||||
temp, err = c.Recv(0, pkgOption.Retry)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@ -108,8 +138,10 @@ func (c *Conn) RecvPkg(retry...Retry) (result []byte, err error) {
|
||||
}
|
||||
|
||||
// 简单协议: 带超时时间的消息包获取
|
||||
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
c.SetRecvDeadline(time.Now().Add(timeout))
|
||||
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option...PkgOption) ([]byte, error) {
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
return c.RecvPkg(retry...)
|
||||
return c.RecvPkg(option...)
|
||||
}
|
@ -7,13 +7,16 @@
|
||||
package gtcp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_RETRY_INTERVAL = 100 // (毫秒)默认重试时间间隔
|
||||
gDEFAULT_READ_BUFFER_SIZE = 1024 // 默认数据读取缓冲区大小
|
||||
gDEFAULT_RETRY_INTERVAL = 100 // (毫秒)默认重试时间间隔
|
||||
gDEFAULT_READ_BUFFER_SIZE = 128 // (byte)默认数据读取缓冲区大小
|
||||
)
|
||||
|
||||
type Retry struct {
|
||||
@ -21,6 +24,7 @@ type Retry struct {
|
||||
Interval int // 重试间隔(毫秒)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// 常见的二进制数据校验方式,生成校验结果
|
||||
func Checksum(buffer []byte) uint32 {
|
||||
var checksum uint32
|
||||
@ -39,6 +43,20 @@ func NewNetConn(addr string, timeout...int) (net.Conn, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建支持TLS的原生TCP链接, addr地址格式形如:127.0.0.1:80
|
||||
func NewNetConnTLS(addr string, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
return tls.Dial("tcp", addr, tlsConfig)
|
||||
}
|
||||
|
||||
// 根据给定的证书和密钥文件创建支持TLS的原生TCP链接, addr地址格式形如:127.0.0.1:80
|
||||
func NewNetConnKeyCrt(addr, crtFile, keyFile string) (net.Conn, error) {
|
||||
tlsConfig, err := LoadKeyCrt(crtFile, keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewNetConnTLS(addr, tlsConfig)
|
||||
}
|
||||
|
||||
// (面向短链接)发送数据
|
||||
func Send(addr string, data []byte, retry...Retry) error {
|
||||
conn, err := NewConn(addr)
|
||||
@ -88,4 +106,25 @@ func isTimeout(err error) bool {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 根据证书和密钥生成TLS对象
|
||||
func LoadKeyCrt(crtFile, keyFile string) (*tls.Config, error) {
|
||||
crtPath, err := gfile.Search(crtFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyPath, err := gfile.Search(keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crt, err := tls.LoadX509KeyPair(crtPath, keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig := &tls.Config{}
|
||||
tlsConfig.Certificates = []tls.Certificate{crt}
|
||||
tlsConfig.Time = time.Now
|
||||
tlsConfig.Rand = rand.Reader
|
||||
return tlsConfig, nil
|
||||
}
|
@ -9,41 +9,41 @@ package gtcp
|
||||
import "time"
|
||||
|
||||
// 简单协议: (面向短链接)发送消息包
|
||||
func SendPkg(addr string, data []byte, retry...Retry) error {
|
||||
func SendPkg(addr string, data []byte, option...PkgOption) error {
|
||||
conn, err := NewConn(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
return conn.SendPkg(data, retry...)
|
||||
return conn.SendPkg(data, option...)
|
||||
}
|
||||
|
||||
// 简单协议: (面向短链接)发送数据并等待接收返回数据
|
||||
func SendRecvPkg(addr string, data []byte, retry...Retry) ([]byte, error) {
|
||||
func SendRecvPkg(addr string, data []byte, option...PkgOption) ([]byte, error) {
|
||||
conn, err := NewConn(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
return conn.SendRecvPkg(data, retry...)
|
||||
return conn.SendRecvPkg(data, option...)
|
||||
}
|
||||
|
||||
// 简单协议: (面向短链接)带超时时间的数据发送
|
||||
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) error {
|
||||
func SendPkgWithTimeout(addr string, data []byte, timeout time.Duration, option...PkgOption) error {
|
||||
conn, err := NewConn(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
return conn.SendPkgWithTimeout(data, timeout, retry...)
|
||||
return conn.SendPkgWithTimeout(data, timeout, option...)
|
||||
}
|
||||
|
||||
// 简单协议: (面向短链接)发送数据并等待接收返回数据(带返回超时等待时间)
|
||||
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
func SendRecvPkgWithTimeout(addr string, data []byte, timeout time.Duration, option...PkgOption) ([]byte, error) {
|
||||
conn, err := NewConn(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
return conn.SendRecvPkgWithTimeout(data, timeout, retry...)
|
||||
return conn.SendRecvPkgWithTimeout(data, timeout, option...)
|
||||
}
|
@ -67,7 +67,7 @@ func (c *PoolConn) Close() error {
|
||||
c.status = gCONN_STATUS_UNKNOWN
|
||||
c.pool.Put(c)
|
||||
} else {
|
||||
c.Conn.Close()
|
||||
return c.Conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -114,15 +114,19 @@ func (c *PoolConn) RecvLine(retry...Retry) ([]byte, error) {
|
||||
}
|
||||
|
||||
// (方法覆盖)带超时时间的数据获取
|
||||
func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
c.SetRecvDeadline(time.Now().Add(timeout))
|
||||
func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) (data []byte, err error) {
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
return c.Recv(length, retry...)
|
||||
}
|
||||
|
||||
// (方法覆盖)带超时时间的数据发送
|
||||
func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
|
||||
c.SetSendDeadline(time.Now().Add(timeout))
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
return c.Send(data, retry...)
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ import (
|
||||
)
|
||||
|
||||
// 简单协议: (方法覆盖)发送数据
|
||||
func (c *PoolConn) SendPkg(data []byte, retry...Retry) (err error) {
|
||||
if err = c.Conn.SendPkg(data, retry...); err != nil && c.status == gCONN_STATUS_UNKNOWN {
|
||||
func (c *PoolConn) SendPkg(data []byte, option...PkgOption) (err error) {
|
||||
if err = c.Conn.SendPkg(data, option...); err != nil && c.status == gCONN_STATUS_UNKNOWN {
|
||||
if v, e := c.pool.NewFunc(); e == nil {
|
||||
c.Conn = v.(*PoolConn).Conn
|
||||
err = c.Conn.SendPkg(data, retry...)
|
||||
err = c.Conn.SendPkg(data, option...)
|
||||
} else {
|
||||
err = e
|
||||
}
|
||||
@ -29,8 +29,8 @@ func (c *PoolConn) SendPkg(data []byte, retry...Retry) (err error) {
|
||||
}
|
||||
|
||||
// 简单协议: (方法覆盖)接收数据
|
||||
func (c *PoolConn) RecvPkg(retry...Retry) ([]byte, error) {
|
||||
data, err := c.Conn.RecvPkg(retry...)
|
||||
func (c *PoolConn) RecvPkg(option...PkgOption) ([]byte, error) {
|
||||
data, err := c.Conn.RecvPkg(option...)
|
||||
if err != nil {
|
||||
c.status = gCONN_STATUS_ERROR
|
||||
} else {
|
||||
@ -40,32 +40,36 @@ func (c *PoolConn) RecvPkg(retry...Retry) ([]byte, error) {
|
||||
}
|
||||
|
||||
// 简单协议: (方法覆盖)带超时时间的数据获取
|
||||
func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
c.SetRecvDeadline(time.Now().Add(timeout))
|
||||
func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option...PkgOption) ([]byte, error) {
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
return c.RecvPkg(retry...)
|
||||
return c.RecvPkg(option...)
|
||||
}
|
||||
|
||||
// 简单协议: (方法覆盖)带超时时间的数据发送
|
||||
func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
|
||||
c.SetSendDeadline(time.Now().Add(timeout))
|
||||
func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) error {
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
return c.SendPkg(data, retry...)
|
||||
return c.SendPkg(data, option...)
|
||||
}
|
||||
|
||||
// 简单协议: (方法覆盖)发送数据并等待接收返回数据
|
||||
func (c *PoolConn) SendRecvPkg(data []byte, retry...Retry) ([]byte, error) {
|
||||
if err := c.SendPkg(data, retry...); err == nil {
|
||||
return c.RecvPkg(retry...)
|
||||
func (c *PoolConn) SendRecvPkg(data []byte, option...PkgOption) ([]byte, error) {
|
||||
if err := c.SendPkg(data, option...); err == nil {
|
||||
return c.RecvPkg(option...)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// 简单协议: (方法覆盖)发送数据并等待接收返回数据(带返回超时等待时间)
|
||||
func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
if err := c.SendPkg(data, retry...); err == nil {
|
||||
return c.RecvPkgWithTimeout(timeout, retry...)
|
||||
func (c *PoolConn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option...PkgOption) ([]byte, error) {
|
||||
if err := c.SendPkg(data, option...); err == nil {
|
||||
return c.RecvPkgWithTimeout(timeout, option...)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -8,79 +8,132 @@
|
||||
package gtcp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"net"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_SERVER = "default"
|
||||
)
|
||||
|
||||
// tcp server结构体
|
||||
// TCP Server.
|
||||
type Server struct {
|
||||
listen net.Listener
|
||||
address string
|
||||
handler func (*Conn)
|
||||
tlsConfig *tls.Config
|
||||
}
|
||||
|
||||
// Server表,用以存储和检索名称与Server对象之间的关联关系
|
||||
// Map for name to server, for singleton purpose.
|
||||
var serverMapping = gmap.NewStrAnyMap()
|
||||
|
||||
// 获取/创建一个空配置的TCP Server
|
||||
// 单例模式,请保证name的唯一性
|
||||
// GetServer returns the TCP server with specified <name>,
|
||||
// or it returns a new normal TCP server named <name> if it does not exist.
|
||||
// The parameter <name> is used to specify the TCP server
|
||||
func GetServer(name...interface{}) *Server {
|
||||
serverName := gDEFAULT_SERVER
|
||||
if len(name) > 0 {
|
||||
serverName = gconv.String(name[0])
|
||||
}
|
||||
if s := serverMapping.Get(serverName); s != nil {
|
||||
return s.(*Server)
|
||||
}
|
||||
s := NewServer("", nil)
|
||||
serverMapping.Set(serverName, s)
|
||||
return s
|
||||
return serverMapping.GetOrSetFuncLock(serverName, func() interface{} {
|
||||
return NewServer("", nil)
|
||||
}).(*Server)
|
||||
}
|
||||
|
||||
// 创建一个tcp server对象,并且可以选择指定一个单例名字
|
||||
func NewServer(address string, handler func (*Conn), names...string) *Server {
|
||||
s := &Server{address, handler}
|
||||
if len(names) > 0 {
|
||||
serverMapping.Set(names[0], s)
|
||||
// NewServer creates and returns a new normal TCP server.
|
||||
// The param <name> is optional, which is used to specify the instance name of the server.
|
||||
func NewServer(address string, handler func (*Conn), name...string) *Server {
|
||||
s := &Server{
|
||||
address : address,
|
||||
handler : handler,
|
||||
}
|
||||
if len(name) > 0 {
|
||||
serverMapping.Set(name[0], s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 设置参数 - address
|
||||
func (s *Server) SetAddress (address string) {
|
||||
// NewServerTLS creates and returns a new TCP server with TLS support.
|
||||
// The param <name> is optional, which is used to specify the instance name of the server.
|
||||
func NewServerTLS(address string, tlsConfig *tls.Config, handler func (*Conn), name...string) *Server {
|
||||
s := NewServer(address, handler, name...)
|
||||
s.SetTLSConfig(tlsConfig)
|
||||
return s
|
||||
}
|
||||
|
||||
// NewServerKeyCrt creates and returns a new TCP server with TLS support.
|
||||
// The param <name> is optional, which is used to specify the instance name of the server.
|
||||
func NewServerKeyCrt(address, crtFile, keyFile string, handler func (*Conn), name...string) *Server {
|
||||
s := NewServer(address, handler, name...)
|
||||
if err := s.SetTLSKeyCrt(crtFile, keyFile); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SetAddress sets the listening address for server.
|
||||
func (s *Server) SetAddress(address string) {
|
||||
s.address = address
|
||||
}
|
||||
|
||||
// 设置参数 - handler
|
||||
func (s *Server) SetHandler (handler func (*Conn)) {
|
||||
// SetHandler sets the connection handler for server.
|
||||
func (s *Server) SetHandler(handler func (*Conn)) {
|
||||
s.handler = handler
|
||||
}
|
||||
|
||||
// 执行监听
|
||||
func (s *Server) Run() error {
|
||||
// SetTlsKeyCrt sets the certificate and key file for TLS configuration of server.
|
||||
func (s *Server) SetTLSKeyCrt(crtFile, keyFile string) error {
|
||||
tlsConfig, err := LoadKeyCrt(crtFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.tlsConfig = tlsConfig
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTlsConfig sets the TLS configuration of server.
|
||||
func (s *Server) SetTLSConfig(tlsConfig *tls.Config) {
|
||||
s.tlsConfig = tlsConfig
|
||||
}
|
||||
|
||||
// Close closes the listener and shutdowns the server.
|
||||
func (s *Server) Close() error {
|
||||
return s.listen.Close()
|
||||
}
|
||||
|
||||
// Run starts running the TCP Server.
|
||||
func (s *Server) Run() (err error) {
|
||||
if s.handler == nil {
|
||||
err := errors.New("start running failed: socket handler not defined")
|
||||
err = errors.New("start running failed: socket handler not defined")
|
||||
glog.Error(err)
|
||||
return err
|
||||
return
|
||||
}
|
||||
addr, err := net.ResolveTCPAddr("tcp", s.address)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
if s.tlsConfig != nil {
|
||||
// TLS Server
|
||||
s.listen, err = tls.Listen("tcp", s.address, s.tlsConfig)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Normal Server
|
||||
addr, err := net.ResolveTCPAddr("tcp", s.address)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
s.listen, err = net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
listen, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
for {
|
||||
if conn, err := listen.Accept(); err != nil {
|
||||
for {
|
||||
if conn, err := s.listen.Accept(); err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
} else if conn != nil {
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// 封装的链接对象
|
||||
// 封装的UDP链接对象
|
||||
type Conn struct {
|
||||
conn *net.UDPConn // 底层链接对象
|
||||
raddr *net.UDPAddr // 远程地址
|
||||
@ -53,15 +53,12 @@ func NewConnByNetConn(udp *net.UDPConn) *Conn {
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
func (c *Conn) Send(data []byte, retry...Retry) error {
|
||||
var err error
|
||||
var size int
|
||||
var length int
|
||||
func (c *Conn) Send(data []byte, retry...Retry) (err error) {
|
||||
for {
|
||||
if c.raddr != nil {
|
||||
size, err = c.conn.WriteToUDP(data, c.raddr)
|
||||
_, err = c.conn.WriteToUDP(data, c.raddr)
|
||||
} else {
|
||||
size, err = c.conn.Write(data)
|
||||
_, err = c.conn.Write(data)
|
||||
}
|
||||
if err != nil {
|
||||
// 链接已关闭
|
||||
@ -80,16 +77,16 @@ func (c *Conn) Send(data []byte, retry...Retry) error {
|
||||
time.Sleep(time.Duration(retry[0].Interval) * time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
length += size
|
||||
if length == len(data) {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 接收数据.
|
||||
// 注意:UDP协议存在消息边界,因此使用 length<=0 可以获取缓冲区所有消息包数据,即一个完整包。
|
||||
// 接收UDP协议数据.
|
||||
//
|
||||
// 注意事项:
|
||||
// 1、UDP协议存在消息边界,因此使用 length < 0 可以获取缓冲区所有消息包数据,即一个完整包;
|
||||
// 2、当length = 0时,表示获取当前的缓冲区数据,获取一次后立即返回;
|
||||
func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
var err error // 读取错误
|
||||
var size int // 读取长度
|
||||
@ -105,9 +102,11 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
}
|
||||
|
||||
for {
|
||||
if length <= 0 && index > 0 {
|
||||
if length < 0 && index > 0 {
|
||||
bufferWait = true
|
||||
c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait))
|
||||
if err = c.conn.SetReadDeadline(time.Now().Add(c.recvBufferWait)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
size, raddr, err = c.conn.ReadFromUDP(buffer[index:])
|
||||
if err == nil {
|
||||
@ -139,7 +138,9 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
}
|
||||
// 判断数据是否全部读取完毕(由于超时机制的存在,获取的数据完整性不可靠)
|
||||
if bufferWait && isTimeout(err) {
|
||||
c.conn.SetReadDeadline(c.recvDeadline)
|
||||
if err = c.conn.SetReadDeadline(c.recvDeadline); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
@ -157,6 +158,10 @@ func (c *Conn) Recv(length int, retry...Retry) ([]byte, error) {
|
||||
}
|
||||
break
|
||||
}
|
||||
// 只获取一次数据
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer[:index], err
|
||||
}
|
||||
@ -172,14 +177,18 @@ func (c *Conn) SendRecv(data []byte, receive int, retry...Retry) ([]byte, error)
|
||||
|
||||
// 带超时时间的数据获取
|
||||
func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry...Retry) ([]byte, error) {
|
||||
c.SetRecvDeadline(time.Now().Add(timeout))
|
||||
if err := c.SetRecvDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer c.SetRecvDeadline(time.Time{})
|
||||
return c.Recv(length, retry...)
|
||||
}
|
||||
|
||||
// 带超时时间的数据发送
|
||||
func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry...Retry) error {
|
||||
c.SetSendDeadline(time.Now().Add(timeout))
|
||||
if err := c.SetSendDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.SetSendDeadline(time.Time{})
|
||||
return c.Send(data, retry...)
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// Deprecated.
|
||||
// 常见的二进制数据校验方式,生成校验结果
|
||||
func Checksum(buffer []byte) uint32 {
|
||||
var checksum uint32
|
||||
@ -22,18 +23,18 @@ func Checksum(buffer []byte) uint32 {
|
||||
// 创建标准库UDP链接操作对象
|
||||
func NewNetConn(raddr string, laddr...string) (*net.UDPConn, error) {
|
||||
var err error
|
||||
var rudpaddr, ludpaddr *net.UDPAddr
|
||||
rudpaddr, err = net.ResolveUDPAddr("udp", raddr)
|
||||
var remoteAddr, localAddr *net.UDPAddr
|
||||
remoteAddr, err = net.ResolveUDPAddr("udp", raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(laddr) > 0 {
|
||||
ludpaddr, err = net.ResolveUDPAddr("udp", laddr[0])
|
||||
localAddr, err = net.ResolveUDPAddr("udp", laddr[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
conn, err := net.DialUDP("udp", ludpaddr, rudpaddr)
|
||||
conn, err := net.DialUDP("udp", localAddr, remoteAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -21,8 +21,9 @@ const (
|
||||
|
||||
// tcp server结构体
|
||||
type Server struct {
|
||||
address string
|
||||
handler func (*Conn)
|
||||
conn *Conn // UDP server connection object.
|
||||
address string // Listening address.
|
||||
handler func (*Conn)
|
||||
}
|
||||
|
||||
// Server表,用以存储和检索名称与Server对象之间的关联关系
|
||||
@ -30,7 +31,7 @@ var serverMapping = gmap.NewStrAnyMap()
|
||||
|
||||
// 获取/创建一个空配置的UDP Server
|
||||
// 单例模式,请保证name的唯一性
|
||||
func GetServer(name...interface{}) (*Server) {
|
||||
func GetServer(name...interface{}) *Server {
|
||||
serverName := gDEFAULT_SERVER
|
||||
if len(name) > 0 {
|
||||
serverName = gconv.String(name[0])
|
||||
@ -44,8 +45,11 @@ func GetServer(name...interface{}) (*Server) {
|
||||
}
|
||||
|
||||
// 创建一个tcp server对象,并且可以选择指定一个单例名字
|
||||
func NewServer (address string, handler func (*Conn), names...string) *Server {
|
||||
s := &Server{address, handler}
|
||||
func NewServer(address string, handler func (*Conn), names...string) *Server {
|
||||
s := &Server{
|
||||
address : address,
|
||||
handler : handler,
|
||||
}
|
||||
if len(names) > 0 {
|
||||
serverMapping.Set(names[0], s)
|
||||
}
|
||||
@ -58,10 +62,16 @@ func (s *Server) SetAddress (address string) {
|
||||
}
|
||||
|
||||
// 设置参数 - handler
|
||||
func (s *Server) SetHandler (handler func (*Conn)) {
|
||||
func (s *Server) SetHandler(handler func (*Conn)) {
|
||||
s.handler = handler
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
// It will make server shutdowns immediately.
|
||||
func (s *Server) Close() error {
|
||||
return s.conn.Close()
|
||||
}
|
||||
|
||||
// 执行监听
|
||||
func (s *Server) Run() error {
|
||||
if s.handler == nil {
|
||||
@ -79,7 +89,7 @@ func (s *Server) Run() error {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
for {
|
||||
s.handler(NewConnByNetConn(conn))
|
||||
}
|
||||
s.conn = NewConnByNetConn(conn)
|
||||
s.handler(s.conn)
|
||||
return nil
|
||||
}
|
||||
|
@ -55,20 +55,20 @@ func New(file...string) *Config {
|
||||
// Customized dir path from env/cmd.
|
||||
if envPath := cmdenv.Get("gf.gcfg.path").String(); envPath != "" {
|
||||
if gfile.Exists(envPath) {
|
||||
c.SetPath(envPath)
|
||||
_ = c.SetPath(envPath)
|
||||
} else {
|
||||
glog.Errorfln("Configuration directory path does not exist: %s", envPath)
|
||||
glog.Errorf("Configuration directory path does not exist: %s", envPath)
|
||||
}
|
||||
} else {
|
||||
// Dir path of working dir.
|
||||
c.SetPath(gfile.Pwd())
|
||||
_ = c.SetPath(gfile.Pwd())
|
||||
// Dir path of binary.
|
||||
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
|
||||
c.AddPath(selfPath)
|
||||
_ = c.AddPath(selfPath)
|
||||
}
|
||||
// Dir path of main package.
|
||||
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
|
||||
c.AddPath(mainPath)
|
||||
_ = c.AddPath(mainPath)
|
||||
}
|
||||
}
|
||||
return c
|
||||
@ -276,9 +276,9 @@ func (c *Config) getJson(file...string) *gjson.Json {
|
||||
return j
|
||||
} else {
|
||||
if filePath != "" {
|
||||
glog.Criticalfln(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
|
||||
glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
|
||||
} else {
|
||||
glog.Criticalfln(`[gcfg] Load configuration failed: %s`, err.Error())
|
||||
glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -115,7 +115,7 @@ func (entry *Entry) check() {
|
||||
return
|
||||
|
||||
case STATUS_CLOSED:
|
||||
glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s removed", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s removed", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
entry.Close()
|
||||
|
||||
case STATUS_READY: fallthrough
|
||||
@ -130,12 +130,12 @@ func (entry *Entry) check() {
|
||||
if times < 2000000000 && times > 1000000000 {
|
||||
entry.times.Set(gDEFAULT_TIMES)
|
||||
}
|
||||
glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
glog.Path(path).Level(level).Errorfln("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err)
|
||||
glog.Path(path).Level(level).Errorf("[gcron] %s(%s) %s end with error: %v", entry.Name, entry.schedule.pattern, entry.jobName, err)
|
||||
} else {
|
||||
glog.Path(path).Level(level).Debugfln("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s end", entry.Name, entry.schedule.pattern, entry.jobName)
|
||||
}
|
||||
if entry.entry.Status() == STATUS_CLOSED {
|
||||
entry.Close()
|
||||
|
@ -426,7 +426,7 @@ func MainPkgPath() string {
|
||||
}
|
||||
// separator of <file> '/' will be converted to Separator.
|
||||
path = Dir(file)
|
||||
for path != "/" && gstr.Contains(path, Separator) {
|
||||
for path[len(path) - 1] != os.PathSeparator {
|
||||
files, _ := ScanDir(path, "*.go")
|
||||
for _, v := range files {
|
||||
if gregex.IsMatchString(`package\s+main`, GetContents(v)) {
|
||||
|
@ -243,6 +243,13 @@ func (l *Logger) print(std io.Writer, lead string, value...interface{}) {
|
||||
if len(timeFormat) > 0 {
|
||||
buffer.WriteString(time.Now().Format(timeFormat))
|
||||
}
|
||||
// Lead string.
|
||||
if len(lead) > 0 {
|
||||
buffer.WriteString(lead)
|
||||
if len(value) > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
// Caller path.
|
||||
callerPath := ""
|
||||
if l.flags & F_FILE_LONG > 0 {
|
||||
@ -259,12 +266,6 @@ func (l *Logger) print(std io.Writer, lead string, value...interface{}) {
|
||||
buffer.WriteString(l.prefix + " ")
|
||||
}
|
||||
}
|
||||
if len(lead) > 0 {
|
||||
buffer.WriteString(lead)
|
||||
if len(value) > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
for k, v := range value {
|
||||
if k > 0 {
|
||||
buffer.WriteByte(' ')
|
||||
|
@ -4,43 +4,92 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmlock implements a thread-safe memory locker.
|
||||
//
|
||||
// 内存锁.
|
||||
// Package gmlock implements a concurrent-safe memory-based locker.
|
||||
package gmlock
|
||||
|
||||
import "time"
|
||||
|
||||
var (
|
||||
// Default locker.
|
||||
locker = New()
|
||||
)
|
||||
|
||||
// 内存写锁,如果锁成功返回true,失败则返回false;过期时间单位为秒,默认为0表示不过期
|
||||
// TryLock tries locking the <key> with write lock,
|
||||
// it returns true if success, or if there's a write/read lock the <key>,
|
||||
// it returns false. The parameter <expire> specifies the max duration it locks.
|
||||
func TryLock(key string, expire...time.Duration) bool {
|
||||
return locker.TryLock(key, expire...)
|
||||
}
|
||||
|
||||
// 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有其他写锁存在;过期时间单位为秒,默认为0表示不过期
|
||||
// Lock locks the <key> with write lock.
|
||||
// If there's a write/read lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func Lock(key string, expire...time.Duration) {
|
||||
locker.Lock(key, expire...)
|
||||
}
|
||||
|
||||
// 解除基于内存锁的写锁
|
||||
// Unlock unlocks the write lock of the <key>.
|
||||
func Unlock(key string) {
|
||||
locker.Unlock(key)
|
||||
}
|
||||
|
||||
// 内存读锁,如果锁成功返回true,失败则返回false; 过期时间单位为秒,默认为0表示不过期
|
||||
// TryRLock tries locking the <key> with read lock.
|
||||
// It returns true if success, or if there's a write lock on <key>, it returns false.
|
||||
func TryRLock(key string) bool {
|
||||
return locker.TryRLock(key)
|
||||
}
|
||||
|
||||
// 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有写锁存在; 过期时间单位为秒,默认为0表示不过期
|
||||
// RLock locks the <key> with read lock.
|
||||
// If there's a write lock on <key>,
|
||||
// it will blocks until the write lock is released.
|
||||
func RLock(key string) {
|
||||
locker.RLock(key)
|
||||
}
|
||||
|
||||
// 解除基于内存锁的读锁
|
||||
// RUnlock unlocks the read lock of the <key>.
|
||||
func RUnlock(key string) {
|
||||
locker.RUnlock(key)
|
||||
}
|
||||
|
||||
// TryLockFunc locks the <key> with write lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write/read lock the <key>, it return false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func TryLockFunc(key string, f func(), expire...time.Duration) bool {
|
||||
return locker.TryLockFunc(key, f, expire...)
|
||||
}
|
||||
|
||||
// TryRLockFunc locks the <key> with read lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write lock the <key>, it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func TryRLockFunc(key string, f func()) bool {
|
||||
return locker.TryRLockFunc(key, f)
|
||||
}
|
||||
|
||||
// LockFunc locks the <key> with write lock and callback function <f>.
|
||||
// If there's a write/read lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func LockFunc(key string, f func(), expire...time.Duration) {
|
||||
locker.LockFunc(key, f, expire...)
|
||||
}
|
||||
|
||||
// RLockFunc locks the <key> with read lock and callback function <f>.
|
||||
// If there's a write lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func RLockFunc(key string, f func()) {
|
||||
locker.RLockFunc(key, f)
|
||||
}
|
@ -12,53 +12,119 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// 内存锁管理对象
|
||||
// Memory locker.
|
||||
type Locker struct {
|
||||
m *gmap.StrAnyMap
|
||||
}
|
||||
|
||||
// 创建一把内存锁, 底层使用的是Mutex
|
||||
// New creates and returns a new memory locker.
|
||||
// A memory locker can lock/unlock with dynamic string key.
|
||||
func New() *Locker {
|
||||
return &Locker{
|
||||
m : gmap.NewStrAnyMap(),
|
||||
}
|
||||
}
|
||||
|
||||
// 内存写锁,如果锁成功返回true,失败则返回false; 过期时间默认为0表示不过期
|
||||
// TryLock tries locking the <key> with write lock,
|
||||
// it returns true if success, or if there's a write/read lock the <key>,
|
||||
// it returns false. The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) TryLock(key string, expire...time.Duration) bool {
|
||||
return l.doLock(key, l.getExpire(expire...), true)
|
||||
}
|
||||
|
||||
// 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有其他写锁存在;过期时间默认为0表示不过期
|
||||
// Lock locks the <key> with write lock.
|
||||
// If there's a write/read lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) Lock(key string, expire...time.Duration) {
|
||||
l.doLock(key, l.getExpire(expire...), false)
|
||||
}
|
||||
|
||||
// 解除基于内存锁的写锁
|
||||
// Unlock unlocks the write lock of the <key>.
|
||||
func (l *Locker) Unlock(key string) {
|
||||
if v := l.m.Get(key); v != nil {
|
||||
v.(*Mutex).Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// 内存读锁,如果锁成功返回true,失败则返回false; 过期时间单位为秒,默认为0表示不过期
|
||||
// TryRLock tries locking the <key> with read lock.
|
||||
// It returns true if success, or if there's a write lock on <key>, it returns false.
|
||||
func (l *Locker) TryRLock(key string) bool {
|
||||
return l.doRLock(key, true)
|
||||
}
|
||||
|
||||
// 内存写锁,锁成功返回true,失败时阻塞,当失败时表示有写锁存在; 过期时间单位为秒,默认为0表示不过期
|
||||
// RLock locks the <key> with read lock.
|
||||
// If there's a write lock on <key>,
|
||||
// it will blocks until the write lock is released.
|
||||
func (l *Locker) RLock(key string) {
|
||||
l.doRLock(key, false)
|
||||
}
|
||||
|
||||
// 解除基于内存锁的读锁
|
||||
// RUnlock unlocks the read lock of the <key>.
|
||||
func (l *Locker) RUnlock(key string) {
|
||||
if v := l.m.Get(key); v != nil {
|
||||
v.(*Mutex).RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
// 获得过期时间,没有设置时默认为0不过期
|
||||
// TryLockFunc locks the <key> with write lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write/read lock the <key>, it return false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) TryLockFunc(key string, f func(), expire...time.Duration) bool {
|
||||
if l.TryLock(key, expire...) {
|
||||
defer l.Unlock(key)
|
||||
f()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TryRLockFunc locks the <key> with read lock and callback function <f>.
|
||||
// It returns true if success, or else if there's a write lock the <key>, it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) TryRLockFunc(key string, f func()) bool {
|
||||
if l.TryRLock(key) {
|
||||
defer l.RUnlock(key)
|
||||
f()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LockFunc locks the <key> with write lock and callback function <f>.
|
||||
// If there's a write/read lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) LockFunc(key string, f func(), expire...time.Duration) {
|
||||
l.Lock(key, expire...)
|
||||
defer l.Unlock(key)
|
||||
f()
|
||||
}
|
||||
|
||||
// RLockFunc locks the <key> with read lock and callback function <f>.
|
||||
// If there's a write lock the <key>,
|
||||
// it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) RLockFunc(key string, f func()) {
|
||||
l.RLock(key)
|
||||
defer l.RUnlock(key)
|
||||
f()
|
||||
}
|
||||
|
||||
// getExpire returns the duration object passed.
|
||||
// If <expire> is not passed, it returns a default duration object.
|
||||
func (l *Locker) getExpire(expire...time.Duration) time.Duration {
|
||||
e := time.Duration(0)
|
||||
if len(expire) > 0 {
|
||||
@ -67,7 +133,14 @@ func (l *Locker) getExpire(expire...time.Duration) time.Duration {
|
||||
return e
|
||||
}
|
||||
|
||||
// 内存写锁,当try==true时,如果锁成功返回true,失败则返回false;try==false时,成功时立即返回,否则阻塞等待
|
||||
// doLock locks writing on <key>.
|
||||
// It returns true if success, or else returns false.
|
||||
//
|
||||
// The parameter <try> is true,
|
||||
// it returns false immediately if it fails getting the write lock.
|
||||
// If <true> is false, it blocks until it gets the write lock.
|
||||
//
|
||||
// The parameter <expire> specifies the max duration it locks.
|
||||
func (l *Locker) doLock(key string, expire time.Duration, try bool) bool {
|
||||
mu := l.getOrNewMutex(key)
|
||||
ok := true
|
||||
@ -77,7 +150,6 @@ func (l *Locker) doLock(key string, expire time.Duration, try bool) bool {
|
||||
mu.Lock()
|
||||
}
|
||||
if ok && expire > 0 {
|
||||
// 异步goroutine计时处理
|
||||
wid := mu.wid.Val()
|
||||
gtimer.AddOnce(expire, func() {
|
||||
if wid == mu.wid.Val() {
|
||||
@ -88,7 +160,12 @@ func (l *Locker) doLock(key string, expire time.Duration, try bool) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// 内存读锁,当try==true时,如果锁成功返回true,失败则返回false;try==false时,成功时立即返回,否则阻塞等待
|
||||
// doRLock locks reading on <key>.
|
||||
// It returns true if success, or else returns false.
|
||||
//
|
||||
// The parameter <try> is true,
|
||||
// it returns false immediately if it fails getting the read lock.
|
||||
// If <true> is false, it blocks until it gets the read lock.
|
||||
func (l *Locker) doRLock(key string, try bool) bool {
|
||||
mu := l.getOrNewMutex(key)
|
||||
ok := true
|
||||
@ -100,8 +177,9 @@ func (l *Locker) doRLock(key string, try bool) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// 根据指定key查询或者创建新的Mutex
|
||||
func (l *Locker) getOrNewMutex(key string) (*Mutex) {
|
||||
// getOrNewMutex returns the mutex of given <key> if it exists,
|
||||
// or else creates and returns a new one.
|
||||
func (l *Locker) getOrNewMutex(key string) *Mutex {
|
||||
return l.m.GetOrSetFuncLock(key, func() interface{} {
|
||||
return NewMutex()
|
||||
}).(*Mutex)
|
||||
|
@ -7,19 +7,20 @@
|
||||
package gmlock
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"sync"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 互斥锁对象
|
||||
// The high level Mutex.
|
||||
// It wraps the sync.RWMutex to implements more rich features.
|
||||
type Mutex struct {
|
||||
mu sync.RWMutex
|
||||
wid *gtype.Int64 // 当前Lock产生的唯一id(主要用于计时Unlock的校验)
|
||||
rcount *gtype.Int // RLock次数
|
||||
wcount *gtype.Int // Lock次数
|
||||
wid *gtype.Int64 // Unique id, used for multiple safely Unlock.
|
||||
rcount *gtype.Int // Reading locks count.
|
||||
wcount *gtype.Int // Writing locks count.
|
||||
}
|
||||
|
||||
// 创建一把内存锁使用的底层RWMutex
|
||||
// NewMutex creates and returns a new mutex.
|
||||
func NewMutex() *Mutex {
|
||||
return &Mutex{
|
||||
wid : gtype.NewInt64(),
|
||||
@ -28,65 +29,123 @@ func NewMutex() *Mutex {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Mutex) Lock() {
|
||||
l.wcount.Add(1)
|
||||
l.mu.Lock()
|
||||
l.wid.Add(1)
|
||||
// Lock locks mutex for writing.
|
||||
// If the lock is already locked for reading or writing,
|
||||
// Lock blocks until the lock is available.
|
||||
func (m *Mutex) Lock() {
|
||||
m.wcount.Add(1)
|
||||
m.mu.Lock()
|
||||
m.wid.Add(1)
|
||||
}
|
||||
|
||||
// 安全的Unlock
|
||||
func (l *Mutex) Unlock() {
|
||||
if l.wcount.Val() > 0 {
|
||||
if l.wcount.Add(-1) >= 0 {
|
||||
l.mu.Unlock()
|
||||
// Unlock unlocks the write lock.
|
||||
// It is safe to be called multiple times.
|
||||
func (m *Mutex) Unlock() {
|
||||
if m.wcount.Val() > 0 {
|
||||
if m.wcount.Add(-1) >= 0 {
|
||||
m.mu.Unlock()
|
||||
} else {
|
||||
// 标准库这里会panic
|
||||
l.wcount.Add(1)
|
||||
m.wcount.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Mutex) RLock() {
|
||||
l.rcount.Add(1)
|
||||
l.mu.RLock()
|
||||
// RLock locks mutex for reading.
|
||||
// If the mutex is already locked for writing,
|
||||
// It blocks until the lock is available.
|
||||
func (m *Mutex) RLock() {
|
||||
m.rcount.Add(1)
|
||||
m.mu.RLock()
|
||||
}
|
||||
|
||||
// 安全的RUnlock
|
||||
func (l *Mutex) RUnlock() {
|
||||
if l.rcount.Val() > 0 {
|
||||
if l.rcount.Add(-1) >= 0 {
|
||||
l.mu.RUnlock()
|
||||
// RUnlock undoes a single RLock call;
|
||||
// it does not affect other simultaneous readers.
|
||||
// It is a run-time error if mutex is not locked for reading
|
||||
// on entry to RUnlock.
|
||||
// It is safe to be called multiple times.
|
||||
func (m *Mutex) RUnlock() {
|
||||
if m.rcount.Val() > 0 {
|
||||
if m.rcount.Add(-1) >= 0 {
|
||||
m.mu.RUnlock()
|
||||
} else {
|
||||
// 标准库这里会panic
|
||||
l.rcount.Add(1)
|
||||
m.rcount.Add(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 不阻塞Lock
|
||||
func (l *Mutex) TryLock() bool {
|
||||
// 初步读写次数检查, 但无法保证原子性
|
||||
if l.wcount.Val() == 0 && l.rcount.Val() == 0 {
|
||||
// 第二次检查, 保证原子操作
|
||||
if l.wcount.Add(1) == 1 {
|
||||
l.mu.Lock()
|
||||
l.wid.Add(1)
|
||||
// TryLock tries locking the mutex for writing.
|
||||
// It returns true if success, or if there's a write/read lock on the mutex,
|
||||
// it returns false.
|
||||
func (m *Mutex) TryLock() bool {
|
||||
// The first check, but it cannot ensure the atomicity.
|
||||
if m.wcount.Val() == 0 && m.rcount.Val() == 0 {
|
||||
// The second check, it ensures the atomicity with atomic Add.
|
||||
if m.wcount.Add(1) == 1 {
|
||||
m.mu.Lock()
|
||||
m.wid.Add(1)
|
||||
return true
|
||||
} else {
|
||||
l.wcount.Add(-1)
|
||||
m.wcount.Add(-1)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 不阻塞RLock
|
||||
func (l *Mutex) TryRLock() bool {
|
||||
// 只要不存在写锁
|
||||
if l.wcount.Val() == 0 {
|
||||
l.rcount.Add(1)
|
||||
l.mu.RLock()
|
||||
// TryRLock tries locking the mutex for reading.
|
||||
// It returns true if success, or if there's a write lock on the mutex, it returns false.
|
||||
func (m *Mutex) TryRLock() bool {
|
||||
// There must be no write lock on mutex.
|
||||
if m.wcount.Val() == 0 {
|
||||
m.rcount.Add(1)
|
||||
m.mu.RLock()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TryLockFunc tries locking the mutex for writing with given callback function <f>.
|
||||
// it returns true if success, or if there's a write/read lock on the mutex,
|
||||
// it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) TryLockFunc(f func()) bool {
|
||||
if m.TryLock() {
|
||||
defer m.Unlock()
|
||||
f()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TryRLockFunc tries locking the mutex for reading with given callback function <f>.
|
||||
// It returns true if success, or if there's a write lock on the mutex, it returns false.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) TryRLockFunc(f func()) bool {
|
||||
if m.TryRLock() {
|
||||
defer m.RUnlock()
|
||||
f()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LockFunc locks the mutex for writing with given callback function <f>.
|
||||
// If there's a write/read lock the mutex, it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) LockFunc(f func()) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
f()
|
||||
}
|
||||
|
||||
// RLockFunc locks the mutex for reading with given callback function <f>.
|
||||
// If there's a write lock the mutex, it will blocks until the lock is released.
|
||||
//
|
||||
// It releases the lock after <f> is executed.
|
||||
func (m *Mutex) RLockFunc(f func()) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
f()
|
||||
}
|
||||
|
@ -14,10 +14,10 @@ import (
|
||||
|
||||
// Goroutine Pool
|
||||
type Pool struct {
|
||||
limit int // Max goroutine count limit.
|
||||
count *gtype.Int // Current running goroutine count.
|
||||
list *glist.List // Job list.
|
||||
closed *gtype.Bool // Is pool closed or not.
|
||||
limit int // Max goroutine count limit.
|
||||
count *gtype.Int // Current running goroutine count.
|
||||
list *glist.List // Job list for asynchronous job adding purpose.
|
||||
closed *gtype.Bool // Is pool closed or not.
|
||||
}
|
||||
|
||||
// Default goroutine pool.
|
||||
@ -33,7 +33,7 @@ func New(limit...int) *Pool {
|
||||
list : glist.New(),
|
||||
closed : gtype.NewBool(),
|
||||
}
|
||||
if len(limit) > 0 {
|
||||
if len(limit) > 0 && limit[0] > 0 {
|
||||
p.limit = limit[0]
|
||||
}
|
||||
return p
|
||||
@ -72,6 +72,7 @@ func (p *Pool) Add(f func()) {
|
||||
p.fork()
|
||||
}
|
||||
|
||||
|
||||
// Size returns current goroutine count of the pool.
|
||||
func (p *Pool) Size() int {
|
||||
return p.count.Val()
|
||||
|
@ -5,8 +5,6 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gtime provides functionality for measuring and displaying time.
|
||||
//
|
||||
// 时间管理.
|
||||
package gtime
|
||||
|
||||
import (
|
||||
|
@ -69,7 +69,7 @@ func New(path...string) *View {
|
||||
if gfile.Exists(envPath) {
|
||||
view.SetPath(envPath)
|
||||
} else {
|
||||
glog.Errorfln("Template directory path does not exist: %s", envPath)
|
||||
glog.Errorf("Template directory path does not exist: %s", envPath)
|
||||
}
|
||||
} else {
|
||||
// Dir path of working dir.
|
||||
|
@ -15,11 +15,17 @@ import (
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/gfsnotify"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/os/gmlock"
|
||||
"github.com/gogf/gf/g/os/gspath"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const (
|
||||
// Template name for content parsing.
|
||||
gCONTENT_TEMPLATE_NAME = "template content"
|
||||
)
|
||||
|
||||
var (
|
||||
// Templates cache map for template folder.
|
||||
templates = gmap.NewStrAnyMap()
|
||||
@ -40,7 +46,7 @@ func (view *View) getTemplate(path string, pattern string) (tpl *template.Templa
|
||||
if tpl, err = tpl.ParseFiles(files...); err != nil {
|
||||
return nil
|
||||
}
|
||||
gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
_, _ = gfsnotify.Add(path, func(event *gfsnotify.Event) {
|
||||
templates.Remove(path)
|
||||
gfsnotify.Exit()
|
||||
})
|
||||
@ -102,7 +108,10 @@ func (view *View) Parse(file string, params...Params) (parsed string, err error)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tpl, err = tpl.Parse(gfcache.GetContents(path))
|
||||
// Using memory lock to ensure concurrent safety for template parsing.
|
||||
gmlock.LockFunc("gview-parsing:" + folder, func() {
|
||||
tpl, err = tpl.Parse(gfcache.GetContents(path))
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -146,8 +155,14 @@ func (view *View) Parse(file string, params...Params) (parsed string, err error)
|
||||
func (view *View) ParseContent(content string, params...Params) (string, error) {
|
||||
view.mu.RLock()
|
||||
defer view.mu.RUnlock()
|
||||
tpl := template.New("template content").Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcMap)
|
||||
tpl, err := tpl.Parse(content)
|
||||
err := (error)(nil)
|
||||
tpl := templates.GetOrSetFuncLock(gCONTENT_TEMPLATE_NAME, func() interface {} {
|
||||
return template.New(gCONTENT_TEMPLATE_NAME).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcMap)
|
||||
}).(*template.Template)
|
||||
// Using memory lock to ensure concurrent safety for content parsing.
|
||||
gmlock.LockFunc("gview-parsing:content", func() {
|
||||
tpl, err = tpl.Parse(content)
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -10,51 +10,29 @@ package gregex_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"testing"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var pattern = `(.+):(\d+)`
|
||||
var src = "johng.cn:80"
|
||||
var replace = "johng.cn"
|
||||
var pattern = `(\w+).+\-\-\s*(.+)`
|
||||
var src = `GF is best! -- John`
|
||||
|
||||
func BenchmarkValidate(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.Validate(pattern)
|
||||
}
|
||||
func Benchmark_GF(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.IsMatchString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsMatch(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.IsMatch(pattern, []byte(src))
|
||||
}
|
||||
func Benchmark_Compile(b *testing.B) {
|
||||
var wcdRegexp = regexp.MustCompile(pattern)
|
||||
for i := 0; i < b.N; i++ {
|
||||
wcdRegexp.MatchString(src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsMatchString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.IsMatchString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatchString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.MatchString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMatchAllString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.MatchAllString(pattern, src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReplace(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.Replace(pattern, []byte(replace), []byte(src))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReplaceString(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gregex.ReplaceString(pattern, replace, src)
|
||||
}
|
||||
func Benchmark_Compile_Actual(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
wcdRegexp := regexp.MustCompile(pattern)
|
||||
wcdRegexp.MatchString(src)
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,11 @@ type apiString interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Type assert api for Error().
|
||||
type apiError interface {
|
||||
Error() string
|
||||
}
|
||||
|
||||
var (
|
||||
// Empty strings.
|
||||
emptyStringMap = map[string]struct{}{
|
||||
@ -143,6 +148,10 @@ func String(i interface{}) string {
|
||||
// If the variable implements the String() interface,
|
||||
// then use that interface to perform the conversion
|
||||
return f.String()
|
||||
} else if f, ok := value.(apiError); ok {
|
||||
// If the variable implements the Error() interface,
|
||||
// then use that interface to perform the conversion
|
||||
return f.Error()
|
||||
} else {
|
||||
// Finally we use json.Marshal to convert.
|
||||
jsonContent, _ := json.Marshal(value)
|
||||
|
@ -46,6 +46,7 @@ func Struct(params interface{}, pointer interface{}, mapping...map[string]string
|
||||
if kind := rv.Kind(); kind != reflect.Ptr {
|
||||
return fmt.Errorf("object pointer should be type of: %v", kind)
|
||||
}
|
||||
// Using IsNil on reflect.Ptr variable is OK.
|
||||
if !rv.IsValid() || rv.IsNil() {
|
||||
return errors.New("object pointer cannot be nil")
|
||||
}
|
||||
@ -156,7 +157,9 @@ func StructDeep(params interface{}, pointer interface{}, mapping...map[string]st
|
||||
trv := rv.Field(i)
|
||||
switch trv.Kind() {
|
||||
case reflect.Struct:
|
||||
StructDeep(params, trv, mapping...)
|
||||
if err := StructDeep(params, trv, mapping...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,7 +241,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
switch structFieldValue.Kind() {
|
||||
// 属性为结构体
|
||||
case reflect.Struct:
|
||||
Struct(value, structFieldValue)
|
||||
if err := Struct(value, structFieldValue); err != nil {
|
||||
structFieldValue.Set(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
// 属性为数组类型
|
||||
case reflect.Slice: fallthrough
|
||||
@ -252,11 +257,15 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
Struct(v.Index(i).Interface(), e)
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()))
|
||||
}
|
||||
a.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
Struct(v.Index(i).Interface(), e)
|
||||
if err := Struct(v.Index(i).Interface(), e); err != nil {
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()))
|
||||
}
|
||||
a.Index(i).Set(e)
|
||||
}
|
||||
}
|
||||
@ -266,11 +275,15 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
t := a.Index(0).Type()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
Struct(value, e)
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
}
|
||||
a.Index(0).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
Struct(value, e)
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
}
|
||||
a.Index(0).Set(e)
|
||||
}
|
||||
}
|
||||
@ -279,7 +292,9 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) er
|
||||
// 属性为指针类型
|
||||
case reflect.Ptr:
|
||||
e := reflect.New(structFieldValue.Type().Elem()).Elem()
|
||||
Struct(value, e)
|
||||
if err := Struct(value, e); err != nil {
|
||||
e.Set(reflect.ValueOf(value))
|
||||
}
|
||||
structFieldValue.Set(e.Addr())
|
||||
|
||||
default:
|
||||
|
@ -7,12 +7,12 @@
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"testing"
|
||||
"time"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Struct_Basic1(t *testing.T) {
|
||||
@ -104,6 +104,26 @@ func Test_Struct_Basic2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// 带有指针的基础类型属性
|
||||
func Test_Struct_Basic3(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name *string
|
||||
}
|
||||
user := new(User)
|
||||
params := g.Map {
|
||||
"uid" : 1,
|
||||
"Name" : "john",
|
||||
}
|
||||
if err := gconv.Struct(params, user); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.Uid, 1)
|
||||
gtest.Assert(*user.Name, "john")
|
||||
})
|
||||
}
|
||||
|
||||
// slice类型属性的赋值
|
||||
func Test_Struct_Attr_Slice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
|
@ -69,11 +69,17 @@ func Throw(exception interface{}) {
|
||||
// TryCatch implements try...catch... logistics.
|
||||
func TryCatch(try func(), catch ... func(exception interface{})) {
|
||||
if len(catch) > 0 {
|
||||
// If <catch> is given, it's used to handle the exception.
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
catch[0](e)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
// If no <catch> function passed, it filters the exception.
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
}
|
||||
try()
|
||||
}
|
||||
|
@ -3,14 +3,13 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/os/gproc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("pid:", gproc.Pid())
|
||||
r.Response.Write("hello world")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
|
37
geg/net/ghttp/server/websocket/echo/main-group.go
Normal file
37
geg/net/ghttp/server/websocket/echo/main-group.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
)
|
||||
|
||||
|
||||
func ws(r *ghttp.Request) {
|
||||
ws, err := r.WebSocket()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
r.Exit()
|
||||
}
|
||||
for {
|
||||
msgType, msg, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = ws.WriteMessage(msgType, msg); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group().Bind([]ghttp.GroupItem{
|
||||
{"ALL", "/ws", ws},
|
||||
})
|
||||
|
||||
s.SetServerRoot(gfile.MainPkgPath())
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
data, err := gtcp.SendRecv("www.baidu.com:80", []byte("GET / HTTP/1.1\n\n"), -1)
|
||||
data, err := gtcp.SendRecv("www.baidu.com:80", []byte("HEAD / HTTP/1.1\n\n"), -1)
|
||||
if len(data) > 0 {
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
|
@ -40,21 +40,21 @@ func main() {
|
||||
case "doexit": onServerDoExit(conn, msg)
|
||||
case "heartbeat": onServerHeartBeat(conn, msg)
|
||||
default:
|
||||
glog.Errorfln("invalid message: %v", msg)
|
||||
glog.Errorf("invalid message: %v", msg)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onServerHello(conn *gtcp.Conn, msg *types.Msg) {
|
||||
glog.Printfln("hello response message from [%s]: %s", conn.RemoteAddr().String(), msg.Data)
|
||||
glog.Printf("hello response message from [%s]: %s", conn.RemoteAddr().String(), msg.Data)
|
||||
}
|
||||
|
||||
func onServerHeartBeat(conn *gtcp.Conn, msg *types.Msg) {
|
||||
glog.Printfln("heartbeat from [%s]", conn.RemoteAddr().String())
|
||||
glog.Printf("heartbeat from [%s]", conn.RemoteAddr().String())
|
||||
}
|
||||
|
||||
func onServerDoExit(conn *gtcp.Conn, msg *types.Msg) {
|
||||
glog.Printfln("exit command from [%s]", conn.RemoteAddr().String())
|
||||
glog.Printf("exit command from [%s]", conn.RemoteAddr().String())
|
||||
conn.Close()
|
||||
}
|
@ -36,10 +36,10 @@ func main() {
|
||||
}
|
||||
|
||||
func onClientHello(conn *gtcp.Conn, msg *types.Msg) {
|
||||
glog.Printfln("hello message from [%s]: %s", conn.RemoteAddr().String(), msg.Data)
|
||||
glog.Printf("hello message from [%s]: %s", conn.RemoteAddr().String(), msg.Data)
|
||||
funcs.SendPkg(conn, msg.Act, "Nice to meet you!")
|
||||
}
|
||||
|
||||
func onClientHeartBeat(conn *gtcp.Conn, msg *types.Msg) {
|
||||
glog.Printfln("heartbeat from [%s]", conn.RemoteAddr().String())
|
||||
glog.Printf("heartbeat from [%s]", conn.RemoteAddr().String())
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func main() {
|
||||
defer conn.Close()
|
||||
for i := 0; i < 10000; i++ {
|
||||
if err := conn.SendPkg([]byte(gconv.String(i))); err != nil {
|
||||
glog.Error(err)
|
||||
glog.Error(err.Error())
|
||||
}
|
||||
time.Sleep(1*time.Second)
|
||||
}
|
39
geg/net/gtcp/pkg_operations/gtcp_pkg_option.go
Normal file
39
geg/net/gtcp/pkg_operations/gtcp_pkg_option.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/net/gtcp"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Server
|
||||
go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
|
||||
defer conn.Close()
|
||||
for {
|
||||
data, err := conn.RecvPkg(gtcp.PkgOption{MaxSize : 1})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
break
|
||||
}
|
||||
fmt.Println("RecvPkg:", string(data))
|
||||
}
|
||||
}).Run()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Client
|
||||
conn, err := gtcp.NewConn("127.0.0.1:8999")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
for i := 0; i < 10000; i++ {
|
||||
if err := conn.SendPkg([]byte(gconv.String(i))); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
time.Sleep(1*time.Second)
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ func main() {
|
||||
}
|
||||
info := &types.NodeInfo{}
|
||||
if err := json.Unmarshal(data, info); err != nil {
|
||||
glog.Errorfln("invalid package structure: %s", err.Error())
|
||||
glog.Errorf("invalid package structure: %s", err.Error())
|
||||
} else {
|
||||
glog.Println(info)
|
||||
conn.SendPkg([]byte("ok"))
|
||||
|
58
geg/net/gtcp/tls/gtcp_server_client.go
Normal file
58
geg/net/gtcp/tls/gtcp_server_client.go
Normal file
@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/net/gtcp"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
address := "127.0.0.1:8999"
|
||||
crtFile := "server.crt"
|
||||
keyFile := "server.key"
|
||||
// TLS Server
|
||||
go gtcp.NewServerKeyCrt(address, crtFile, keyFile, func(conn *gtcp.Conn) {
|
||||
defer conn.Close()
|
||||
for {
|
||||
data, err := conn.Recv(-1)
|
||||
if len(data) > 0 {
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
if err != nil {
|
||||
// if client closes, err will be: EOF
|
||||
glog.Error(err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}).Run()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Client
|
||||
tlsConfig, err := gtcp.LoadKeyCrt(crtFile, keyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
|
||||
conn, err := gtcp.NewConnTLS(address, tlsConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
for i := 0; i < 10; i++ {
|
||||
if err := conn.Send([]byte(gconv.String(i))); err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
if i == 5 {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// exit after 5 seconds
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
23
geg/net/gtcp/tls/server.crt
Normal file
23
geg/net/gtcp/tls/server.crt
Normal file
@ -0,0 +1,23 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDzzCCAregAwIBAgIJAJYpWLkC2lEXMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV
|
||||
BAYTAkNIMRAwDgYDVQQIDAdTaUNodWFuMRAwDgYDVQQHDAdDaGVuZ2R1MRAwDgYD
|
||||
VQQKDAdKb2huLmNuMQwwCgYDVQQLDANEZXYxDTALBgNVBAMMBEpvaG4xHDAaBgkq
|
||||
hkiG9w0BCQEWDWpvaG5Aam9obmcuY24wHhcNMTgwNDIzMTMyNjA4WhcNMTkwNDIz
|
||||
MTMyNjA4WjB+MQswCQYDVQQGEwJDSDEQMA4GA1UECAwHU2lDaHVhbjEQMA4GA1UE
|
||||
BwwHQ2hlbmdkdTEQMA4GA1UECgwHSm9obi5jbjEMMAoGA1UECwwDRGV2MQ0wCwYD
|
||||
VQQDDARKb2huMRwwGgYJKoZIhvcNAQkBFg1qb2huQGpvaG5nLmNuMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6cngPUrDgBhiNfn+7MMHPzOoO+oVavlS
|
||||
F/tCPyKINhsePGqHkR4ILkHu9IuoBiPYR1JgrMz5goQ6mkrvq/LMfo4dCuA29ZRg
|
||||
+Vps/RimBpiz+RU3FDGyqc7d+fk74dElGk6NhJJ6XO3qHqgIg1yc6d5DiZfEnlMz
|
||||
CRKoZ2dQ+98o5LwES+XJBVWfZiC1pEfyppIh+ci7fXajxkRPJ+5qYWaS5cIHmJIN
|
||||
DIp5Ypszg1cPs0gIr5EgPeGwZzOeqMMzsbLLE8kjSw59Pt1/+Jkdm1e0GhO18qIO
|
||||
NcqaHeGaTUVjzX9XwRj8cw+q3kRoqD5aWMjUzAg9+IDrMqvo6VZQ5QIDAQABo1Aw
|
||||
TjAdBgNVHQ4EFgQU1/tUQpOK0xEwLLlYDiNrckqPlDowHwYDVR0jBBgwFoAU1/tU
|
||||
QpOK0xEwLLlYDiNrckqPlDowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEA5MbG2xU3s/GDU1MV4f0wKhWCNhXfrLaYSwNYGT/eb8ZG2iHSTO0dvl0+pjO2
|
||||
EK63PDMvMhUtL1Zlyvl+OqssYcDhVfDzdFoYX6TZNbYxFwSzcx78mO6boAADk9ro
|
||||
GEQWN+VHsl984SzBRZRJbtNbiw5iVuPruofeKHrrk4dLMiCsStyUaz9lUZxjo2Fi
|
||||
vVJOY+mRNOBqz1HgU2+RilFTl04zWadCWPJMugQSgJcUPgxRXQ96PkC8uYevEnmR
|
||||
2DUReSRULIOYEjHw0DZ6yGlqUkJcUGge3XAQEx3LlCpJasOC8Xpsh5i6WBnDPbMh
|
||||
kPBjRRTooSrJOQJC5v3QW+0Kgw==
|
||||
-----END CERTIFICATE-----
|
27
geg/net/gtcp/tls/server.key
Normal file
27
geg/net/gtcp/tls/server.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA6cngPUrDgBhiNfn+7MMHPzOoO+oVavlSF/tCPyKINhsePGqH
|
||||
kR4ILkHu9IuoBiPYR1JgrMz5goQ6mkrvq/LMfo4dCuA29ZRg+Vps/RimBpiz+RU3
|
||||
FDGyqc7d+fk74dElGk6NhJJ6XO3qHqgIg1yc6d5DiZfEnlMzCRKoZ2dQ+98o5LwE
|
||||
S+XJBVWfZiC1pEfyppIh+ci7fXajxkRPJ+5qYWaS5cIHmJINDIp5Ypszg1cPs0gI
|
||||
r5EgPeGwZzOeqMMzsbLLE8kjSw59Pt1/+Jkdm1e0GhO18qIONcqaHeGaTUVjzX9X
|
||||
wRj8cw+q3kRoqD5aWMjUzAg9+IDrMqvo6VZQ5QIDAQABAoIBAHF7cMHPvL49F88j
|
||||
nr7GnIntRUhwBB19EIBbknibBotc9nxVKaEjds0dbCSAdfslAyL7tbmrdaIJFXk3
|
||||
zsckgGceDLLuyz7B26CuaCEjCdRB43qQ9b9zsEoFBHMGrC6dGul+H+uuPn9FbVOc
|
||||
NSWumuxa22W6qdJAiJFq4RvwZrsbVnYs5V29Y4Y20IlVUj3siJpAny//UUHequW9
|
||||
A/U7RvVssDsEEbbKvCpfcS7STNJKU7GlgV5l5hMKN2xLs1bVG5OKiZN82Zh9r7e1
|
||||
m2irxu/ehu6rENxZN0gsfPE4vqoQpbRMNAJlCfq9a3k0PH0TOy5oOVJXPGTIDQab
|
||||
E3PeAwECgYEA9wh4+bPgMuO04hsAqsoO0DJ9Cwa+BzoDPYOvENobDzmcMErSDLKb
|
||||
ekl1ej+fBTHRHVaBkuOf/9neLjhjMLad1B+I5gLksqwoMh87odDRCCpkO/B20ln8
|
||||
IN6RFiMiNjOaZqjPCCUobgzjbaIz3I69lCQQnMNPwjllSgZs9Lh/PjUCgYEA8kZU
|
||||
hhUN6ctHIo8ocnmqa4AUPbt2l4qOoBGHCMmhjthyft6g8y6cQlACVJzbco37MhjY
|
||||
uCOhhOClyUS1tyfds3NXdzAxXPl8SwQJGvl3zqkDQG7/GhCh6AzvHhZR8u7UaweC
|
||||
kVnAG87Ck6Qqo5ZNbjhMIUm0ujm2cdVd3vyV3fECgYEAmJSMHDck8GnCzLE+/T5m
|
||||
XeQBZfEZKF+FptYSKId+lS3RMebUzHD5JVQAEqz/LHczoTpQOAkORzorSEMdyPXS
|
||||
kDWWGfOJjG5XOXYfH/hZVADS/k6tJYnc9/RgitrSg8XlxSjZDz/cM/UT+CBqhf1I
|
||||
TRrlg94DAoTu8gT8AT9/oE0CgYB5CSPO/JO/2jtGi6iUUC4QmKMEGDRuDt2kID2K
|
||||
6ViaCY5hzY0xEHcmNdyEMvz7JO16oKkcjUhzHtwUSgxSXUtIDHaE6AGxRj6PJ4v4
|
||||
+uqcxxkFxq4Rcn/Acz2+lT4JlMFwWwci4Gi2O7w/kENxCHTUfLGj67OrWYvJIORN
|
||||
s3iXsQKBgD1I+v+simBvKZKmozzv99EgGfxkRxmrUQsclg1V8a1VTNfE5X9oNaE5
|
||||
kjp+dTnwbtmFl3SHVdFUzX/L6FvQIQ9FIwWI2bsszPm4rw8FBeOvH+8lXwVhCwPs
|
||||
y9him/PhdjBPX0zydDI+h+fmrxH/XbmryZcq1rNmEtFRHBsUs5jg
|
||||
-----END RSA PRIVATE KEY-----
|
27
geg/net/gtcp/tls/server.key.public
Normal file
27
geg/net/gtcp/tls/server.key.public
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA6cngPUrDgBhiNfn+7MMHPzOoO+oVavlSF/tCPyKINhsePGqH
|
||||
kR4ILkHu9IuoBiPYR1JgrMz5goQ6mkrvq/LMfo4dCuA29ZRg+Vps/RimBpiz+RU3
|
||||
FDGyqc7d+fk74dElGk6NhJJ6XO3qHqgIg1yc6d5DiZfEnlMzCRKoZ2dQ+98o5LwE
|
||||
S+XJBVWfZiC1pEfyppIh+ci7fXajxkRPJ+5qYWaS5cIHmJINDIp5Ypszg1cPs0gI
|
||||
r5EgPeGwZzOeqMMzsbLLE8kjSw59Pt1/+Jkdm1e0GhO18qIONcqaHeGaTUVjzX9X
|
||||
wRj8cw+q3kRoqD5aWMjUzAg9+IDrMqvo6VZQ5QIDAQABAoIBAHF7cMHPvL49F88j
|
||||
nr7GnIntRUhwBB19EIBbknibBotc9nxVKaEjds0dbCSAdfslAyL7tbmrdaIJFXk3
|
||||
zsckgGceDLLuyz7B26CuaCEjCdRB43qQ9b9zsEoFBHMGrC6dGul+H+uuPn9FbVOc
|
||||
NSWumuxa22W6qdJAiJFq4RvwZrsbVnYs5V29Y4Y20IlVUj3siJpAny//UUHequW9
|
||||
A/U7RvVssDsEEbbKvCpfcS7STNJKU7GlgV5l5hMKN2xLs1bVG5OKiZN82Zh9r7e1
|
||||
m2irxu/ehu6rENxZN0gsfPE4vqoQpbRMNAJlCfq9a3k0PH0TOy5oOVJXPGTIDQab
|
||||
E3PeAwECgYEA9wh4+bPgMuO04hsAqsoO0DJ9Cwa+BzoDPYOvENobDzmcMErSDLKb
|
||||
ekl1ej+fBTHRHVaBkuOf/9neLjhjMLad1B+I5gLksqwoMh87odDRCCpkO/B20ln8
|
||||
IN6RFiMiNjOaZqjPCCUobgzjbaIz3I69lCQQnMNPwjllSgZs9Lh/PjUCgYEA8kZU
|
||||
hhUN6ctHIo8ocnmqa4AUPbt2l4qOoBGHCMmhjthyft6g8y6cQlACVJzbco37MhjY
|
||||
uCOhhOClyUS1tyfds3NXdzAxXPl8SwQJGvl3zqkDQG7/GhCh6AzvHhZR8u7UaweC
|
||||
kVnAG87Ck6Qqo5ZNbjhMIUm0ujm2cdVd3vyV3fECgYEAmJSMHDck8GnCzLE+/T5m
|
||||
XeQBZfEZKF+FptYSKId+lS3RMebUzHD5JVQAEqz/LHczoTpQOAkORzorSEMdyPXS
|
||||
kDWWGfOJjG5XOXYfH/hZVADS/k6tJYnc9/RgitrSg8XlxSjZDz/cM/UT+CBqhf1I
|
||||
TRrlg94DAoTu8gT8AT9/oE0CgYB5CSPO/JO/2jtGi6iUUC4QmKMEGDRuDt2kID2K
|
||||
6ViaCY5hzY0xEHcmNdyEMvz7JO16oKkcjUhzHtwUSgxSXUtIDHaE6AGxRj6PJ4v4
|
||||
+uqcxxkFxq4Rcn/Acz2+lT4JlMFwWwci4Gi2O7w/kENxCHTUfLGj67OrWYvJIORN
|
||||
s3iXsQKBgD1I+v+simBvKZKmozzv99EgGfxkRxmrUQsclg1V8a1VTNfE5X9oNaE5
|
||||
kjp+dTnwbtmFl3SHVdFUzX/L6FvQIQ9FIwWI2bsszPm4rw8FBeOvH+8lXwVhCwPs
|
||||
y9him/PhdjBPX0zydDI+h+fmrxH/XbmryZcq1rNmEtFRHBsUs5jg
|
||||
-----END RSA PRIVATE KEY-----
|
@ -5,6 +5,6 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
glog.Line().Println("this is the short file name with its line number")
|
||||
glog.Line(true).Println("lone file name with line number")
|
||||
glog.Line().Debug("this is the short file name with its line number")
|
||||
glog.Line(true).Debug("lone file name with line number")
|
||||
}
|
||||
|
@ -1,20 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int `json:"id" gconv:"i_d"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
user := User{100}
|
||||
jsonBytes, _ := json.Marshal(user)
|
||||
fmt.Println(string(jsonBytes))
|
||||
|
||||
b, _ := gparser.VarToJson(user)
|
||||
fmt.Println(string(b))
|
||||
fmt.Println(gtime.Now().Format("U"))
|
||||
fmt.Println(gtime.Second())
|
||||
}
|
@ -7,9 +7,15 @@ import (
|
||||
|
||||
func main() {
|
||||
g.TryCatch(func() {
|
||||
glog.Printfln("hello")
|
||||
glog.Println("hello")
|
||||
g.Throw("exception")
|
||||
glog.Printfln("world")
|
||||
glog.Println("world")
|
||||
})
|
||||
|
||||
g.TryCatch(func() {
|
||||
glog.Println("hello")
|
||||
g.Throw("exception")
|
||||
glog.Println("world")
|
||||
}, func(exception interface{}) {
|
||||
glog.Error(exception)
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.6.16"
|
||||
const VERSION = "v1.7.0"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
Loading…
Reference in New Issue
Block a user