我是一个javaer,最近空闲时间在学习golang。
度娘后,安装好Go环境和LiteIDE后,一开始我也没从基础开始看,而是想把现有的java项目改成是golang版本的。
原项目内容:
golang相比java,有很多很方便的特性。特别是并发与网络方面更是golang的卖点。所以我就直接找了个socket的例子开始模拟着实现项目的socket模块
server.go :
package socket
import (
"fmt"
"net"
"strings"
)
func StartServer() {
service := ":3338"
tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
for {
conn, err := listener.Accept()
if err != nil {
continue
}
fmt.Println("新连接:", conn.RemoteAddr().String())
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
for {
buffer := make([]byte, 1024)
length, err := conn.Read(buffer)
if err != nil {
conn.Close()
}
if length > 12 {
data := buffer[:length]
switch data[11] & 0xff {
case 0x80:
//桌子
fmt.Println("桌子")
case 0x90:
//椅子
case 0xA0:
//台灯
default:
//其它
}
//写数据
// conn.Write(data)
}
}
}
func isProtocol(data []byte) bool {
if (data[0]&0xff) == 0xC0 && (data[len(data)-1]&0xff) == 0xC1 {
return true
}
return false
}
func checkError(err error) {
if err != nil {
fmt.Println(err.Error())
}
}
因为是第一篇笔记,也简单地说一下golang的基础语法。
对于一个javaer来说,或者一个有计算机语言基础的朋友来说,golang的语法看起来不会太困难。
像java一样,一开始我们为程序声明一个包路径
package socket
和java不一样的是,golang的包不是层叠式的。所以为了方便识别我们也可以为我们所有的项目放到一个父包下。
类似与java,我这里使用了一个letus.xyz的命名作为父包(文件夹)。这样其它的程序就能比较方便地找到server.go程序来调用。
如果想要构建一个程序,则包和包内的文件都必须以正确的顺序进行编译。包的依赖关系决定了其构建顺序。
属于同一个包的源文件必须全部被一起编译,一个包即是编译时的一个单元,因此根据惯例,每个目录都只包含一个包。
如果对一个包进行更改或重新编译,所有引用了这个包的客户端程序都必须全部重新编译。
import ( "fmt" "net" "strings" )
和java相比,golang使用的这种方式进行导包和库看起来优雅多了。
当然,你也可以像java一样,一个一个地import
import "fmt"
import "net"
import "strings"
注意事项:
如果你导入了一个包却没有使用它,则会在构建程序时引发错误,如 imported and not used: os,这正是遵循了 Go 的格言:“没有不必要的代码!“。
当你导入多个包时,导入的顺序会按照字母排序。
如果包名不是以 . 或 / 开头,如 “fmt” 或者 “container/list”,则 Go 会在全局文件进行查找;如果包名以 ./ 开头,则 Go 会在相对目录中查找;如果包名以 / 开头(在 Windows 下也可以这样使用),则会在系统的绝对路径中查找。
导入包即等同于包含了这个包的所有的代码对象。
除了符号 _,包中所有代码对象的标识符必须是唯一的,以避免名称冲突。但是相同的标识符可以在不同的包中使用,因为可以使用包名来区分它们。
导入包和库之后,就是我们的程序主体了。当然,我们写程序的时候肯定是package之后就直接写程序主体,而包与库是到用到这个包内容的时候再导。
golang和c一样,是面向过程的函数式编程,而不是java那样的面向对象。
func StartServer() {
}
func isProtocol(data []byte) bool {
return false
}
func checkError(err error) {
}
可见性规则:
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。
函数的基本结构:
func functionName(parameter_list) (return_value_list) {
…
}
其中:
golang的方法比java有意思的是它允许返回多个值。而它的参数表示形式与java也不一样,名字是放在类型的前面。
service := ":3338"
:=是简短声明语法 ,表示声明并赋值。
或者你也可以用var来声明:
var service = ":3338"
或
var service string
service = ":3338"
简短声明法看起来更加优雅些。
const ( Unknown = 0 Female = 1 Male = 2 )
我们第一个程序没用到常量。常量是通过const来定义的。
常量的定义格式:
const identifier [type] = value
package main
import ( "letus.xyz/socket" ) func main() { socket.StartServer() }
这里我们通过绝对路径letus.xyz主包找到socket包来调用程序。当然我们也可以相对目录导入。很明显,相对路径的方式不利于包的复用。
所以个人建议使用绝对路径来导包,毕竟思想上和java相似。
import ( "../socket" )
golang的运算符与控制结构语句的使用基本和java的一样,但值得一提的是,golang的switch语法支持字符串的匹配。这是作为一个javaer经常想java也提供的一个特性。
switch field.Type().String() {
case "time.Time":
v, _ := time.Parse("2006-01-02 15:04:05", s)
field.Set(reflect.ValueOf(v))
}