时间:2021-07-01 10:21:17 帮助过:4人阅读
package main
 
import (
"net"
"fmt"
)
 
var (
    maxRead = 1100
    msgStop   = []byte("cmdStop")
    msgStart  = []byte("cmdContinue")
    )
func main() {
    hostAndPort := "localhost:54321"
    listener := initServer(hostAndPort)
 
    for {
        conn, err := listener.Accept()
        checkError(err, "Accept: ")
        go connectionHandler(conn)
    }
}
 
func initServer(hostAndPort string) *net.TCPListener {
    serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
    checkError(err, "Resolving address:port failed: ‘" + hostAndPort + "‘")
    listener, err := net.ListenTCP("tcp", serverAddr)
    checkError(err, "ListenTCP: ")
    println("Listening to: ", listener.Addr().String())
    return listener
}
 
func connectionHandler(conn net.Conn) {
    connFrom := conn.RemoteAddr().String()
    println("Connection from: ", connFrom)
    talktoclients(conn)
    for {
        var ibuf []byte = make([]byte, maxRead + 1)
        length, err := conn.Read(ibuf[0:maxRead])
        ibuf[maxRead] = 0 // to prevent overflow
      switch err {
        case nil:
            handleMsg(length, err, ibuf)
 
        default:
            goto DISCONNECT
      }
    }
    DISCONNECT:
    err := conn.Close()
    println("Closed connection:" , connFrom)
    checkError(err, "Close:" )
}
 
func talktoclients(to net.Conn) {
    wrote, err := to.Write(msgStart)
    checkError(err, "Write: wrote " + string(wrote) + " bytes.")
}
 
func handleMsg(length int, err error, msg []byte) {
    if length > 0 {
         
        for i := 0; ; i++ {
            if msg[i] == 0 {
                break
            }
        }
        fmt.Printf("Received data: %v", string(msg[0:length]))
        fmt.Println("   length:",length)
    }
 
}
 
func checkError(error error, info string) {
    if error != nil {
    panic("ERROR: " + info + " " + error.Error()) // terminate program
  }
}
例2 、
下面先创建一个简单的Server端:
package main
import (
    "net"
    "fmt"
)
func main() {
    if ln, err := net.Listen("tcp", ":8080"); err == nil {
        defer ln.Close()
        for{
            ln.Accept()
            fmt.Println("Receive a Message")
        }
    }   
}
和脚本一样简单的写法。
先net.Listen(), 第一个参数是协议,tcp还是udp,第二个参数是ip地址,这里可以不填写IP只填写端口就是
使用defer 来Close,将close写在listen之后是个好习惯
然后再循环中使用Accept()接受消息
下面是一个可以进行压力测试的客户端程序
package main
import (
    "net"
    "fmt"
)
func main() {
    currency := 20 //并发数,记住,一个连接数是打开一个端口号,window和linux的端口号都是有限制的
    count := 10 //每条连接发送多少次连接
    for i:=0;i<currency;i++ {
        go func(){
            for j:=0;j<count;j++ {
                sendMessage()
            }
        }()
    }
    select{}
}
func sendMessage() {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")
    if(err != nil) {
        panic("error")
    }
    header := "GET / HTTP/1.0\r\n\r\n"
    fmt.Fprintf(conn, header)
}
首先安装mysql的go语言驱动
go get github.com/ziutek/mymysql/godrv
演示代码
package users
  
import (
    "database/sql"
    "fmt"
    _ "github.com/ziutek/mymysql/godrv"
)
  
const (
    DB_NAME = "mysql_database_name"
    DB_USER = "mysql_user"
    DB_PASS = "mysql_password"
)
  
type User struct {
    Id      int    `json:"id"`
    Name string `json:"name"`
    Alias   string `json:"alias"`
}
  
func OpenDB() *sql.DB {
    db, err := sql.Open("mymysql", fmt.Sprintf("%s/%s/%s", DB_NAME, DB_USER, DB_PASS))
    if err != nil {
        panic(err)
    }
    return db
}
  
func UserById(id int) User {
    db := OpenDB()
    defer db.Close()
    row := db.QueryRow("SELECT `id`, `name`,`alias` FROM `users` WHERE id=?", id)
    user := new(User)
    row.Scan(&user.Id, &user.Name, &user.Alias)
    return user
}
例2:
go的sql包是在pkg/database中,里面的两个包sql和sql/driver可以一起看。建议看这个两个包之前可以先看看sql文件夹下的doc.txt。这个文档说了几点比较重要的:
1 这两个包是真正Go风格的包。
2 这使用这两个包就不需要关于并发处理了,也不需要维护自己的数据库连接池了,一旦建立了一个连接,这个连接是可以在各个goroutine之间共用的。
3 sql/driver提供的是数据库的接口,具体的实现还需要自己实现。
一、先看database/driver包
第一个用到的方法是Register
这个方法将实现了driver.Driver的驱动注册到变量drivers中,当写完一个驱动之后,就需要将驱动注册到sql中才能使用sql包中的这些接口。这个实现了driver.Driver的必须实现了Open方法.
driver.Open返回的是driver.Conn,它的三个方法
Prepare : 参数绑定
Close : 关闭连接
Begin : 支持事务
先看Prepare,这个和php mysql pdo一样的用法
Conn.Prepare("select * from test where a=?")
返回的Stmt结构:
Close : 关闭这个statement
NumInput : 返回有多少个可以绑定的参数
Exec : Insert或者update等无返回的使用
Query :select等查询操作使用
Exec是绑定变量,然后返回Result结构
Query是绑定变量,然后返回Rows结果集
看Result里面的方法:
LastInsertId() : Insert操作之后获取到的主键id
RowsAffect() : 影响到的行数
Rows:
Columns() : 返回的数据有哪些列,其实就是返回的表列名
Close() : 关闭Rows,调用之后不能再进行任何操作
Next() : 将下一行的数据取到des[] Value中。这里的Value接口可以是int64,float64,bool,[]byte,string,time.Time
下面回到Begin,返回了Tx
开始事务之后除了查询之外,就两种行为:Commit和Rollback,这两种行为都是Tx接口的方法
drvier中的结构全是接口性质的,它需要你来实现并注册到Register中。
二、驱动的具体使用是在database/sql中
首先将几个sql的结构看一遍
首先sql中的结构都是对driver中的结构进行了一层封装,比如像Rows,有个内部属性是rowsi driver.Rows。
对sql的实际操作都是使用driver.Rows里面的接口进行操作的,实际也就是使用你自己实现的driver进行操作。
driver和sql就像插头和一个充满插头的汽车一样,你实现了driver,即实现了这些插头的配置,你就可使用sql这个汽车了。
Result : 和driver中的Result一致,即你如果实现了driver.Result,那你自然就实现了sql.Result。它是个接口,其实没有什么特殊的用处,如果sql包中的所有Result都换成driver.Result也是行的,估计是作者希望返回值不要引用到其他包去,就使用这种方法。
Rows : 基于了driver.Rows, 还在上面扩展了几个其他的方法。拥有方法:
Close
Cloumns
Err
Next
Scan
Stmt :基于driver.Stmt。拥有方法
Close
Exec
Query
QueryRow
Tx:基于driver.Tx。拥有方法:
Commit
Exec
Prepare
Query
QueryRow
Rollback
Stmt
从sql.Open开始
返回了sql.DB结构,这个结构是实现了driver.Conn结构,除了Conn已有的Prepare,Begin,Close之外,还直接多了几个查询方法:
Driver() : 返回当前驱动
Exec() : 直接进行操作
Query() : 进行查询,返回Rows
QueryRow() : 预期返回单行,返回Row
不管Rows还是Row都有个很好用的方法Scan来将数据放到制定的变量中去。
比如下面就是典型的Rows用法
rows, err := db.Query("SELECT ...")
...
 
for rows.Next() {
    var id int
    var name string
     
    err = rows.Scan(&id, &name)
    ...
}
Prepare返回Stmt结构
Exec返回Result结构
至于这几个结构也有各自的方法。
go-mysql
标签: