用c#和go做帧同步游戏服务器程序

用c#和go做帧同步游戏服务器程序

在高并发这方面,我对设备的驾驭能力已经达到了个人服务器程序开发的极限,能把一台1核1G的洋垃圾驾驭的如此轻松的,估计也没谁了。我这次实践再次证明,内存和性能时相对立的。

二话不说直接上代码

package main

import (
	"encoding/binary"
	"fmt"
	"math"
	"net"
	"os"
	//"reflect"
	"bytes"
	"container/list"
	"time"
)

var isbncc int32 = 0


var sj_car =make([][]byte,600)//
var sj_car_able =make([]bool,600)//

var car_xue [600]int16//
var car_passenger =make([][]int32,600)//
var car_isnpc [600]bool//
var pid_driving [600]int32//以pid为索引

var xuelian = make([]int32,600) //血量数组
var xuelian_log=make([]int32,600)

var area = make([][]list.List, 1003)  //各方块区域,根据区域同于储存各sj的指针
var sj = make([][]byte, 600)        //各pid的sj
var lep [600]*list.Element        //储存pid所在区域(area)内sj内容的指针
var logp = make([][2]int32, 600)   //二维数组,切片
var heart = make([]int32, 600)    //check heart心跳检测
var heartlog = make([]int32, 600) //check heart
var cn = list.New()               //have been leave's isbn离开pid列表
var iplist [600]*net.UDPAddr     //储存对应pid的ip

// 限制goroutine数量
var limitChan = make(chan bool, 6)
	
func bytetofloat(bytes []byte) float32 {
	bits := binary.LittleEndian.Uint32(bytes)
	return math.Float32frombits(bits)
}

func shorttobyte(isbn int16) []byte {
	result := make([]byte, 2)
	arcresult := make([]byte, 2)
	binary.BigEndian.PutUint16(result, uint16(isbn))
	arcresult[0] = result[1]
	arcresult[1] = result[0]
	return arcresult
}

func inttobyte(x int32) []byte {
	bytesBuffer := bytes.NewBuffer([]byte{})
	binary.Write(bytesBuffer, binary.BigEndian, x)
	s := bytesBuffer.Bytes()
	arcs := make([]byte, 4)
	arcs[0] = s[3]
	arcs[1] = s[2]
	arcs[2] = s[1]
	arcs[3] = s[0]
	return arcs
}
func bytetoint(data []byte) int32 {
	arcdata := make([]byte, 4)
	arcdata[0] = data[3]
	arcdata[1] = data[2]
	arcdata[2] = data[1]
	arcdata[3] = data[0]

	return int32(binary.BigEndian.Uint32(arcdata))
}

func bytetoshort(data []byte) int16 {
	arcdata := make([]byte, 4)
	arcdata[0] = data[1]
	arcdata[1] = data[0]

	return int16(binary.BigEndian.Uint16(arcdata))
}

// UDP goroutine 实现并发读取UDP数据
func udpProcess(conn *net.UDPConn) {
	sendbuff := make([]byte, 600*21)
	data := make([]byte, 64)
	for ;1==1; {
	n, remoteAddr, err := conn.ReadFromUDP(data)
	if err != nil {
		fmt.Println("Failed To Read UDP Msg, Error: " + err.Error())
	}

	//str := string(data[:n]) //n is the end and the front is the began
	//fmt.Println("Reveive From Client, Data: " + str)
    if n == 2 {//复活的消息
     ls_hited:=bytetoshort(data[:2])
     if ls_hited>0 {
     xuelian[ls_hited]=100
	 }else{//延迟测试
     conn.WriteToUDP(data[:2], remoteAddr)
	 }
	}
	if n == 4 {

		if cn.Front() == nil {
			conn.WriteToUDP(inttobyte(isbncc), remoteAddr) //send
			fmt.Println("isbn is null:", bytetoint(inttobyte(isbncc)), "# ", remoteAddr)
			iplist[isbncc] = remoteAddr
			isbncc++

		} else {
			lsbbk := cn.Front()
			lskisbn := lsbbk.Value.(int32)
			conn.WriteToUDP(inttobyte(lskisbn), remoteAddr) //send
			fmt.Println("isbn is:", bytetoint(inttobyte(lskisbn)), "# ", remoteAddr)
			iplist[lskisbn] = remoteAddr
			cn.Remove(cn.Front())

		}
	}

	if n==5	{//返回信息:cid type(-1为失败,成功为位置0为驾驶位) 字符("a"为驾驶或乘坐 "d"为下车) //乘坐、驾驶、下车 数据格式:cid type(0驾驶 1乘坐 2下车) null(len 1)
		cid_s:=bytetoshort(data[:2])
        cid:=int32(cid_s)
		rem_pid:=-1//取远程客户端的pid
		for i:=0;i<600;i++{
			comp1:=(*iplist[i]).IP
            comp2:=(*remoteAddr).IP
			if net.IP(comp1).String()==net.IP(comp2).String(){
				if (*iplist[i]).Port==(*remoteAddr).Port{
				rem_pid=i
				}
			}
		}
		if rem_pid<600&&rem_pid>=0{
		if sj_car_able[cid]{
			d_type:=bytetoshort(data[2:4])
			if d_type==0||d_type==1{
			if d_type==0{
				ls_able_notcon:=true
			   for i:=0;i<600;i++{
				  if car_passenger[i][0]==int32(rem_pid){
					ls_able_notcon=false
				  }
			   }
			   if ls_able_notcon{
				   
                     if car_passenger[cid][0]==-1{
						 car_passenger[cid][0]=int32(rem_pid)
						 var send_buf=make([]byte,5)
						 send_buf[0]=data[0]
						 send_buf[1]=data[1]
						 copy(send_buf[0:],shorttobyte(int16(0)))
                         send_buf[4]=byte(97)
						 pid_driving[rem_pid]=cid
						 conn.WriteToUDP(send_buf, remoteAddr)
					 }else{//座位上有人
						var send_buf=make([]byte,5)
						send_buf[0]=data[0]
						send_buf[1]=data[1]
						copy(send_buf[0:],shorttobyte(int16(-1)))
						send_buf[4]=byte(97)
						conn.WriteToUDP(send_buf, remoteAddr)
					 }
				  

				   }
			   }
			   if d_type==1{
                   var wz int16
				   wz=-1
				   for i:=1;i<4;i++{
					 if  car_passenger[rem_pid][i]==-1{
						car_passenger[rem_pid][i]=int32(rem_pid)
						wz=int16(i)
					 }
				   }
				   if wz==-1{//乘客位已满
					var send_buf=make([]byte,5)
					send_buf[0]=data[0]
					send_buf[1]=data[1]
					copy(send_buf[0:],shorttobyte(int16(-1)))
					send_buf[4]=byte(97)
					conn.WriteToUDP(send_buf, remoteAddr)
				   }else{
					var send_buf=make([]byte,5)
					send_buf[0]=data[0]
					send_buf[1]=data[1]
					copy(send_buf[0:],shorttobyte(int16(wz)))
					send_buf[4]=byte(97)
					pid_driving[rem_pid]=cid
					car_passenger[cid][int32(wz)]=int32(rem_pid)
					conn.WriteToUDP(send_buf, remoteAddr)
				   }

			   }
			}else{//下车
				ls_able_con:=false
				ls_psid:=-1
			   for i:=0;i<4;i++{
				  if car_passenger[cid][i]==int32(rem_pid){
					ls_able_con=true
					ls_psid=i
				  }
			   }
			   if ls_able_con{
				car_passenger[cid][ls_psid]=-1
				var send_buf=make([]byte,5)
					send_buf[0]=data[0]
					send_buf[1]=data[1]
					copy(send_buf[0:],shorttobyte(int16(ls_psid)))
					send_buf[4]=byte(100)
					pid_driving[rem_pid]=-1
					conn.WriteToUDP(send_buf, remoteAddr)
			   }
			}
			}
		}
    }
	


    if n == 7{//攻击人 数据格式:bhit_pid(short) num(int) 填充(byte)
       if data[6] ==1 {
         ls_short_hited:=bytetoshort(data[:2])
         if ls_short_hited>=0 && ls_short_hited<600 {
           xuelian[ls_short_hited] = xuelian[ls_short_hited] - bytetoint(data[2:6])
		 }
	   }else{//回复收到血量确定 数据格式:bhit_pid(short) xue(int) 填充(byt)  收到或未已发出命令后出现非法操作,即可对其进行违规处理
         if data[6]==2{
			ls_short_hited2:=bytetoshort(data[:2])
            if ls_short_hited2>=0 && ls_short_hited2<600 {
			   if xuelian[ls_short_hited2] == bytetoint(data[2:6]){
                    xuelian_log[ls_short_hited2]=xuelian[ls_short_hited2]
			   }
			}
		 }
	   }
	}

	var isbnl int16

	if n == 21 {
		isbnbytes := data[:2]
		isbnl = bytetoshort(isbnbytes)
		copy(sj[isbnl], data[:21]) //updata self sj

		heart[isbnl]++
		if iplist[isbnl] != nil {//if have been lose and no people come,then will send lose 0,but now we do not send back
			if (*remoteAddr).Port == (*iplist[isbnl]).Port {//if someone come and comeback,then will not send anything

               if xuelian[isbnl] != xuelian_log[isbnl]{//血量同步
				conn.WriteToUDP(inttobyte(xuelian[isbnl]), remoteAddr)
			   }


				lsbx := data[2:6]
				lsbz := data[10:14]

				//sendbuff := make([]byte, 600*21)

				icc := 0

				var px int32 = int32(bytetofloat(lsbx) / 500)
				var pz int32 = int32(bytetofloat(lsbz) / 500)

				if lep[isbnl] != nil {
					if logp[isbnl][0]!=px + 501 || logp[isbnl][1] != pz + 501{
					area[logp[isbnl][0]][logp[isbnl][1]].Remove(lep[isbnl])
					lep[isbnl] = area[px+501][pz+501].PushBack(&sj[isbnl]) //updata address
					}
                }else{
                lep[isbnl] = area[px+501][pz+501].PushBack(&sj[isbnl]) //address
				}
				logp[isbnl][0] = px + 501
				logp[isbnl][1] = pz + 501
				//putself

				//put send

				for i := area[px+501][pz+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px+1+501][pz+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px-1+501][pz+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px+501][pz+1+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px+501][pz-1+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px+1+501][pz+1+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px-1+501][pz-1+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px+1+501][pz-1+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++
				}
				for i := area[px-1+501][pz+1+501].Front(); i != nil; i = i.Next() {
					xh := i.Value.(*[]byte)
					copy(sendbuff[icc*21:], *xh)

					icc++ //icc is the count of all 21
				}
				conn.WriteToUDP(sendbuff[:icc*21], remoteAddr) //send
			}
		}else
		{
			fmt.Println("ex ip", iplist[isbnl], " @ ", remoteAddr)
			//conn.WriteToUDP(inttobyte(int32(0)), remoteAddr) //send the cf login respon
		}
	}
    if n == 26 {//创建载具请求
		var car_id int16
		for car_id=0;car_id<600;car_id++{
			if !sj_car_able[car_id]{//!able表示未创建
             var ls_car_sj=make([]byte,30)
			 copy(ls_car_sj[0:],shorttobyte(car_id))
			 for i:=0;i<26;i++{
				 ls_car_sj[i+2]=data[i]
			 }
			 copy(ls_car_sj[28:],shorttobyte(int16(0)))
			 copy(sj_car[int32(car_id)][0:],ls_car_sj[:30])
			 car_xue[car_id]=int16(10000)
			 sj_car_able[car_id]=true
			 break;
			}
		}
	}
	if n == 30{//载具控制
		var isbnfc int16
		isbnfc=bytetoshort(data[0:2])

		if isbnfc>=0&&isbnfc<600{
			if sj_car_able[isbnfc]{
				copy(sj_car[isbnfc][0:],data[:30])
			}
		}
	}

	}

	<-limitChan
}

func udpServer(address string) {
	udpAddr, err := net.ResolveUDPAddr("udp", address)
	conn, errr := net.ListenUDP("udp", udpAddr)
	if conn != nil {
		defer conn.Close()
	}
	if err != nil {
		fmt.Println("Read From Connect Failed, Err :" + err.Error())
		os.Exit(1)
	}
	if errr != nil {
		fmt.Println("Read From Connect Failed, Err :" + errr.Error())
		os.Exit(1)
	}

	for {
		limitChan <- true
		go udpProcess(conn)
	}

}


func main() {
	for i:=0;i<600;i++{
		sj_car[i]=make([]byte,30)
	}
	for i:=0;i<600;i++{
		sj_car_able[i]=false
	}
	for i:=0;i<600;i++{
		car_xue[i]=5000
	}
	for i:=0;i<600;i++{
		car_passenger[i]=make([]int32,4)
		car_passenger[i][0]=-1
		car_passenger[i][1]=-1
		car_passenger[i][2]=-1
		car_passenger[i][3]=-1
	}
	for i:=0;i<600;i++{
		car_isnpc[i]=false
	}
	for i:=0;i<600;i++{
		pid_driving[i]=-1
	}
    for i := 0; i < 600; i++ {
		xuelian[i] = 100
	}
	for i := 0; i < 600; i++ {
		xuelian_log[i] = 100
	}
	for i := 0; i < 600; i++ {
		iplist[i] = nil
	}

	for i := 0; i < len(sj); i++ {
		sj[i] = make([]byte, 21)//初始化二维数组,[600][21]
	}
	for i := 0; i < len(area); i++ {
		area[i] = make([]list.List, 1003)
	}
	var ipp string = ""
	fmt.Scanln(&ipp)
	address := ipp
	fmt.Println("success connect" + ipp)
	go func() {
		udpServer(address)
	}()

	ticker := time.NewTicker(time.Millisecond * 10)
	go func() {
		var icount int32 = 0
		for _ = range ticker.C {
			if heart[icount] == heartlog[icount] {
				//is disconnected but it may come back  we need ip to tick it out

				if iplist[icount] != nil {

                	//如果存在于载具内,将其踢出车外
	                if pid_driving[icount]>=0 && pid_driving[icount]<600{
						for i:=0;i<4;i++{
						   if car_passenger[pid_driving[icount]][i]==icount{
							car_passenger[pid_driving[icount]][i]=-1
						   }
						}
						pid_driving[icount]=-1
					}

					area[logp[icount][0]][logp[icount][1]].Remove(lep[icount]) //clean the sj
					fmt.Println("ip ", iplist[icount], " leave")
					iplist[icount] = nil
					cn.PushBack(icount)

				
				}

			}
			heartlog[icount] = heart[icount]

			if icount == 599 {
				icount = -1
			}
			icount++
		}
	}()

	fmt.Println("R ")
	var t string = ""
	fmt.Scanln(&t)
	fmt.Println("t ")
	os.Exit(1)

}

此程序在初始化基本数据结构的时候就会占用20到30M的内存,启动监听后,第一个玩家以50帧/s进入游戏,内存会扩充至原来的2倍,也就是50多M。随着玩家的数量增加临时变量所产生的Gc会以2M/人渐变增长。和c#对比,go使用的是指针,在索引取数据的时候性能比c#高,但是基本的数据结构会带来更多的内存占用。而且go的Gc比c#的Gc更难清理。这个帧同步脚本支持600人同时在线,脚本仅有位置移动同步功能,其他功能未完善。

你可能感兴趣的:(golang,asp.net,架构)