文档章节

golang socket服务器

liaojie
 liaojie
发布于 2014/03/19 12:07
字数 929
阅读 9127
收藏 19

一、main.go该代码从项目中分离出来,自行修改后再运行)

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
)

var (
	Port           = flag.String("i", ":12345", "IP port to listen on")
	logFileName    = flag.String("log", "cServer.log", "Log file name")
	configFileName = flag.String("configfile", "config.ini", "General configuration file")
)
var (
	configFile = flag.String("configfile", "config.ini", "General configuration file")
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	flag.Parse()

	//set logfile Stdout
	logFile, logErr := os.OpenFile(*logFileName, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	if logErr != nil {
		fmt.Println("Fail to find", *logFile, "cServer start Failed")
		os.Exit(1)
	}
	log.SetOutput(logFile)
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
	//set logfile Stdout End

	//start listen
	listenErr := StartListen(*Port)
	if listenErr != nil {
		log.Fatalf("Server abort! Cause:%v \n", listenErr)
	}
}

二、listener.go该代码从项目中分离出来,自行修改后再运行)

package main

import (
	"code.google.com/p/go-uuid/uuid"
	"errors"
	"fmt"
	"goCS/consistent"
	"log"
	"net"
	"sync"
	"time"
)

const (
	ConnectionMax = 100 //CS max connect
)

//conn Pool info
var (
	poolLock sync.RWMutex
	poolCli  [ConnectionMax]*CliInfo
)

//Web user info
type UserInfo struct {
	WS_ID       int
	WS_Name     string
	ServiceName string
}

//Cli info
type CliInfo struct {
	AssignID   int            //cs assign ID
	Conn       net.Conn       // The TCP/IP connectin to the player.
	ConnTime   time.Time      //连接时间
	VerifyKey  string         //连接验证KEY
	ConnVerify bool           //是否验证
	ServerType int32          //服务器类型(1DB服务器,2WEB服务器)
	NodeStat   int32          //服务器当前状态(0、宕机;1、正常;2、数据导入中;3、准备中;4、数据迁出中
	Address    string         //服务地址
	Port       int            //服务端口
	BackupSer  map[string]int //备份服务器列表map(ip:port)
	sync.RWMutex
}

type hashPool struct {
	Version  int
	Circle   map[uint32]string //hash圈节点分布
	Replicas map[string]int    //hash圈节点范围
}

var SerHashPool *consistent.Consistent = consistent.New()

// Client disconnect
func (cli *CliInfo) disconnect(clientID int) {
	poolLock.Lock()
	defer poolLock.Unlock()
	cli.Conn.Close()
	log.Printf("Client: %s quit\n", cli.VerifyKey)
	if cli.ServerType == 1 {
		//掉线处理
		if ok := cli.removeDBS(); ok {
			poolCli[clientID] = nil
		}

	} else {

	}

}

//listen handle
func (cli *CliInfo) listenHandle(clientID int) {
	headBuff := make([]byte, 12) // set read stream size
	defer cli.Conn.Close()

	//send verify Key:
	b := []byte(cli.VerifyKey)
	cli.Conn.Write(b)
	// fmt.Println("cli-IP:", cli.Conn.RemoteAddr().String())

	//await 10 second verify
	cli.Conn.SetDeadline(time.Now().Add(time.Duration(10) * time.Second))

	forControl := true
	for forControl {
		var headNum int
		for headNum < cap(headBuff) {
			readHeadNum, readHeadErr := cli.Conn.Read(headBuff[headNum:])
			if readHeadErr != nil {
				log.Println("errHead:", readHeadErr)
				forControl = false
				break
			}
			headNum += readHeadNum
		}
		if headNum == cap(headBuff) {
			//pack head Handle
			packHead := packHeadAnalysis(headBuff)
			bodyBuff := make([]byte, packHead.PackLen)
			var bodyNum int
			for bodyNum < cap(bodyBuff) {
				readBodyNum, readBodyErr := cli.Conn.Read(bodyBuff[bodyNum:])
				if readBodyErr != nil {
					log.Println("errBody:", readBodyErr)
					forControl = false
					break
				}
				bodyNum += readBodyNum
			}
			if bodyNum == int(packHead.PackLen) {
				//pack body Handle
				cli.packBodyAnalysis(clientID, packHead, bodyBuff)
				// fmt.Printf("packType:%d;packOther:%d;packLen:%d\n", packHead.PackType, packHead.PackOther, packHead.PackLen)
			}
		}
	}
	cli.disconnect(clientID)
}

//Check or assign new conn
func NewConnection_CS(conn net.Conn) (ok bool, index int, info *CliInfo) {
	poolLock.Lock()
	defer poolLock.Unlock()

	//Assign ID for client
	var i int
	for i = 0; i < ConnectionMax; i++ {
		if poolCli[i] == nil {
			break
		}
	}

	//Too many connections
	if i > ConnectionMax {
		log.Printf("Too many connections! Active Denial %s\n", conn.RemoteAddr().String())
		conn.Close()
		return false, 0, nil
	}

	//Create client base info
	Cli := new(CliInfo)
	Cli.Conn = conn
	Cli.ConnTime = time.Now()
	Cli.VerifyKey = uuid.New()
	Cli.BackupSer = make(map[string]int)

	//Update Pool info
	poolCli[i] = Cli
	log.Println("Cli ID assign ok:", i)
	return true, i, Cli
}

//start listens
func StartListen(addr string) error {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	// if Errors accept arrive 100 .listener stop.
	for failures := 0; failures < 100; {
		conn, listenErr := listener.Accept()
		if listenErr != nil {
			log.Printf("number:%d,failed listening:%v\n", failures, listenErr)
			failures++
		}
		if ok, index, Cli := NewConnection_CS(conn); ok {
			// A new connection is established. Spawn a new gorouting to handle that Client.
			go Cli.listenHandle(index)
		}
	}
	return errors.New("Too many listener.Accept() errors,listener stop")
}

三、原理

一个新的连接建立。产生一个新的gorouting来处理客户端。

    一个客户端进来首先分配一个唯一ID,并初始化该客户端的基本信息(见:NewConnection_CS方法),产生一个新的gorouting来处理客户端。

    如果服务器达到设定的连接上限,将抛弃该客户端。

    客户端连接(分配ID)正常后,将等待10秒来给客户端进行验证超期将断开该客户端连接(见:listenHandle中的cli.Conn.SetDeadline)。

    验证成功后,开接与用户数据进行分析处理:接收原理为:前4字节为包类型,4-12字节为包长,首先接收够12字节后进行包头解析(如不够12字节将进行等待直到够12字节),解析出4-12字节来得到整个包体的长度进行读取(如不够将等待直到够包体长度)

    整包读取完后,根据0-4字节判断包的类型进行包的处理。

四、服务器连接出错达到100条后将终止运行

© 著作权归作者所有

liaojie
粉丝 29
博文 76
码字总数 12108
作品 0
朝阳
程序员
私信 提问
加载中

评论(2)

s
smile_2015
有完整打包代码么?
s
smile_2015
很好
使用 Go 语言实现优雅的服务器重启

Go被设计为一种后台语言,它通常也被用于后端程序中。服务端程序是GO语言最常见的软件产品。在这我要解决的问题是:如何干净利落地升级正在运行的服务端程序。 目标: 不关闭现有连接:例如我...

oschina
2014/12/20
14.1K
30
Golang Web学习(13)—— 搭建简单的Web服务器

本文为转载,原文:Golang Web学习(13)—— 搭建简单的Web服务器 Golang 1、Web工作方式 我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要 浏览的内容。...

ChainZhang
2018/01/14
0
0
go语言文件汇总

归并排序及go语言实现 堆排序算法及go语言实现 Go语言基础学习(一)变量 【Leetcode】:Counting Bits问题 in Go语言 基于go语言的心跳响应 【Leetcode】:Single Number III问题 in Go语言 ...

d_watson
2016/04/15
137
2
Golang开发支持平滑升级(优雅重启)的HTTP服务

前段时间用Golang在做一个HTTP的接口,因编译型语言的特性,修改了代码需要重新编译可执行文件,关闭正在运行的老程序,并启动新程序。对于访问量较大的面向用户的产品,关闭、重启的过程中势...

一曲
2016/12/15
610
0
go实现的简易TCP的客户端和服务器

今天介绍golang版本的通信基础:基于TCP的客户端和服务器实现,参考书籍:The Way To Go 那时学习java的时候也是做过通信的,当时是socket编程,服务器监听某一个端口,然后客户机去连接,简...

徐学良
2015/08/28
544
0

没有更多内容

加载失败,请刷新页面

加载更多

二叉查找树的第 K 个结点

private TreeNode ret;private int cnt = 0;public TreeNode KthNode(TreeNode pRoot, int k) { inOrder(pRoot, k); return ret;}private void inOrder(TreeNode root......

Garphy
52分钟前
4
0
windo8 weblogic

需要的软件包 现在安装jdk 则先进入你电脑自带jdk \bin目录下 然后java -jar 执行你的jar包就可以了 欢迎界面直接点击下一步,跳到更新界面,直接选择跳过 然后选择安装目录(注意:目录不要有...

恩多
59分钟前
8
0
Activiti 批注

Activiti添加批注(comment)信息 在每次提交任务的时候需要描述一些批注信息,例如:请假流程提交的时候要描述信息为什么请假,如果领导驳回可以批注驳回原因等  1、添加批注 // 由于流程...

奔跑的android
今天
4
0
centos7命令行和图形界面的相互切换

最近安装了centos7,发现在命令行和图形界面的相互切换命令上,与centos以往版本有很大不同。 1,centos7默认安装后,跟其他版本一样,启动默认进入图形界面; 2,在图形化桌面,右击鼠标,选...

无名氏的程序员
今天
6
0
快速失败 (fail-fast) 和安全失败 (fail-safe) 的区别是什么

一:快速失败(fail—fast) 在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception。 原理:迭代器在...

Bb进阶
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部