Go语言的网络编程

Go基础:一文掌握Go语言网络编程 Go语言凭借强大的标准库和原生并发支持,非常适合网络编程开发。文章介绍了Go语言在网络编程中的核心概念和应用场景,包括TCP、UDP和HTTP等协议的具体实现方法。主要内容包括:1)网络编程核心概念(Listener、Conn、Addr等);2)TCP服务器/客户端的实现流程与示例代码;3)UDP无连接通信的实现方式;4)利用net/http包构建HTTP服务。文章通过详细的代码示例展示了如何利用Go的Goroutine和Channel特性高效处理网络连接,并强调了连接管理的最佳实践。 阅读详情

一、Socket编程

很多游戏服务都是采用Socket来编写服务器端的,因为HTTP协议相对而言比较耗费性能。

对于底层网络应用开发者而言,几乎所有网络编程都是Socket,因为大部分底层网络的编程都离不开Socket编程。HTTP编程、Web开发、IM通信、视频流传输的底层都是Socket编程。

1、什么是Socket

Socket编程主要是面向OSI模型的第三层和第四层协议。

Socket类型只有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。

流式Socket是一种面向连接的Socket,针对面向连接的TCP服务应用。

数据报式Socket是一种无连接的Socket,对应无连接的UDP服务应用。

1)Socket如何通信

网络层的“IP地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。

利用三大要素(IP地址、协议、端口)就可以标识网络的进程了,网络中需要互相通信的进程,就可以利用这个标志在它们之间进行交互。

Socket有两种:TCP Socket和UDP Socket。

2)Go语言支持的IP类型

在Go语言的net包中定义了很多类型、函数和方法用于网络编程,其中IP的定义如下:

type IP []byte

代码如下:

package main

import (
	"fmt"
	"net"
	"os"
)

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "用法: %s IP地址
", os.Args[0])
		os.Exit(1)
	}
	name := os.Args[1]
	addr := net.ParseIP(name)
	if addr == nil {
		fmt.Println("无效的IP地址")
	} else {
		fmt.Println("IP地址是", addr.String())
	}
	os.Exit(0)
}

运行结果如下:

用法: /var/folders/3x/txs8lfzs6s13njkkrvjmnz2c0000gn/T/go-build3894546141/b001/exe/main IP地址
exit status 1

2、Dial()函数

Dial()函数的原型如下:

func Dial(net, addr string)(Conn, error)

其中net参数是网络协议的名字,addr参数是IP地址或域名,而端口号以“:”的形式跟随在地址或域名的后面,端口号可选。如果连接成功,返回连接对象,否则返回error。

在成功建立连接后,就可以进行数据的发送和接收。在发送数据时,使用conn的Write()成员方法,在接收数据时使用Read()方法。

3、ICMP示例

代码如下:

package main

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
)

func checkSum(msg []byte) uint16 {
	sum := 0
	for n := 1; n < len(msg)-1; n += 2 {
		sum += int(msg[n])*256 + int(msg[n+1])
	}
	sum = (sum >> 16) + (sum & 0xffff)
	sum += (sum >> 16)
	var answer uint16 = uint16(^sum)
	return answer
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "错误:%s", err.Error())
		os.Exit(1)
	}
}

func readFully(conn net.Conn) ([]byte, error) {
	defer conn.Close()
	result := bytes.NewBuffer(nil)
	var buf [512]byte
	for {
		n, err := conn.Read(buf[0:])
		result.Write(buf[0:n])
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
	}
	return result.Bytes(), nil
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("用法:", os.Args[0], "hostname")
		os.Exit(1)
	}
	service := os.Args[1]

	conn, err := net.Dial("ip4:icmp", service)
	checkError(err)

	var msg [512]byte
	msg[0] = 8
	msg[1] = 0
	msg[2] = 0
	msg[3] = 0
	msg[4] = 0
	msg[5] = 13
	msg[6] = 0
	msg[7] = 37
	len := 8
	check := checkSum(msg[0:len])
	msg[2] = byte(check >> 8)
	msg[3] = byte(check & 255)

	_, err = conn.Write(msg[0:len])
	checkError(err)

	_, err = conn.Read(msg[0:])
	checkError(err)

	fmt.Println("取得响应")
	if msg[5] == 13 {
		fmt.Println("Identifier匹配")
	}
	if msg[7] == 37 {
		fmt.Println("Sequence匹配")
	}
	os.Exit(0)
}

运行结果如下:

用法: /var/folders/3x/txs8lfzs6s13njkkrvjmnz2c0000gn/T/go-build3151093604/b001/exe/main hostname
exit status 1

4、TCP Socket

当知道如何通过网络端口访问一个服务时能够做什么呢?作为客户端来说,可以通过向远端某台机器的某个网络端口发送一个请求,然后得到在机器的此端口上监听的服务反馈的信息。作为服务器端,需要把服务绑定到某个指定端口,并且在此端口上监听,当有客户端来访问时能够读取信息并且写入反馈信息。

在Go语言的net包中有一个类型TCPConn,这个类型可以用来作为客户端和服务端交互的通道,它有两个主要的函数:

func (c *TCPConn) Write(b []byte) (n int, err os.Error)

func (c *TCPConn) Read(b []byte) (n int, err os.Error)

TCPConn可以用在客户端和服务器端读写数据。

需要知道一个TCPAddr类型,它表示一个TCP的地址信息,它的定义如下:

type TCPAddr struct{

        IP IP

        Port int

Zone string

}

在Go语言中通过ResolveTCPAddr获取一个TCPAddr:

func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error)

net参数是TCP4、TCP6、TCP中的任意一个,分别表示TCP(IPv4-only),TCP(IPv6-only)或者TCP(IPv4,IPv6的任意一个)。

addr表示域名或者IP地址。

1)TCP客户端

建立连接的函数定义如下:

func DialTCP(net string, laddr, raddr *TCPAddr)(c *TCPConn, err os.Error)

net参数是TCP4、TCP6、TCP中的任意一个。

laddr表示本机地址,一般设置为nil。

raddr表示远程服务地址

代码如下:

package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
)

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "错误: %s", err.Error())
		os.Exit(1)
	}
}
func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "用法: %s host:port", os.Args[0])
		os.Exit(1)
	}
	service := os.Args[1]
	tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
	checkError(err)
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	checkError(err)
	_, err = conn.Write([]byte("HEAD / HTTP/1.0

"))
	checkError(err)
	result, err := ioutil.ReadAll(conn)
	checkError(err)
	fmt.Println(string(result))
	os.Exit(0)
}

运行结果如下:

用法: /var/folders/3x/txs8lfzs6s13njkkrvjmnz2c0000gn/T/go-build3298507250/b001/exe/main host:portexit status 1

2)TCP服务器端

函数定义如下:

func ListenTCP(net string, laddr *TCPAddr)(1 *TCPListener, err os.Error)
func (1 *TCPListener) Accept() (c Conn, err os.Error)

代码如下:

package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

func echo(conn *net.TCPConn) {
	tick := time.Tick(5 * time.Second)
	for now := range tick {
		n, err := conn.Write([]byte(now.String()))
		if err != nil {
			log.Println(err)
			conn.Close()
			return
		}
		fmt.Printf("send %d bytes to %s
", n, conn.RemoteAddr())
	}
}
func main() {
	address := net.TCPAddr{
		IP:   net.ParseIP("127.0.0.1"),
		Port: 8000,
	}
	listener, err := net.ListenTCP("tcp4", &address)
	if err != nil {
		log.Fatal(err)
	}
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("远程地址:", conn.RemoteAddr())
		go echo(conn)
	}
}

运行结果如下:(出不来结果)

3)控制TCP连接

func DialTimeout(net, addr string, timeout time.Duration) (Conn, error)

设置建立连接的超时时间,客户端和服务器端都适用,当超过设置时间后,连接自动关闭:

func (c *TCPConn) SetReadDeadline(t time.Time) error

func (c *TCPConn) SetWriteDeadline(t time.Time) error

用来设置写入/读取一个连接的超时时间。当超过设置时间后,连接自动关闭:

func (c *TCPConn) SetKeepAlive(keepalive bool) os.Error

5、UDP Socket

func ResolveUDPAddr(net, addr string) (*UDPAddr, os.Error)

func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err os.Error)

func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err os.Error)

func (c *UDPConn) ReadFormUDP(b []byte) (n int, addr *UDPAddr, err os.Error)

func (c *UDPConn) WriteToUDP (b []byte, addr *UDPAddr) (n int, err os.Error)

一个UDP的客户端,代码如下:

package main

import (
	"fmt"
	"net"
	"os"
)

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "错误", err.Error())
		os.Exit(1)
	}
}

func main() {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "用法:%s host:port", os.Args[0])
		os.Exit(1)
	}
	service := os.Args[1]
	udpAddr, err := net.ResolveUDPAddr("udp4", service)
	checkError(err)
	conn, err := net.DialUDP("udp", nil, udpAddr)
	checkError(err)
	_, err = conn.Write([]byte("anything"))
	checkError(err)
	var buf [512]byte
	n, err := conn.Read(buf[0:])
	checkError(err)
	fmt.Println(string(buf[0:n]))
	os.Exit(0)
}

运行结果 如下:

用法:C:UsersA-XIAO~1AppDataLocalTempgo-build3311049071001exemain.exe host:portexit status 1

UDP服务端,代码如下:

package main

import (
	"fmt"
	"net"
	"os"
	"time"
)

func handleClient(conn *net.UDPConn) {
	var buf [512]byte
	_, addr, err := conn.ReadFromUDP(buf[0:])
	if err != nil {
		return
	}
	daytime := time.Now().String()
	conn.WriteToUDP([]byte(daytime), addr)
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "错误", err.Error())
		os.Exit(1)
	}
}

func main() {
	service := ":1200"
	udpAddr, err := net.ResolveUDPAddr("udp4", service)
	checkError(err)
	conn, err := net.ListenUDP("udp", udpAddr)
	checkError(err)
	for {
		handleClient(conn)
	}
}

运行结果 如下:

错误%!(EXTRA string=listen udp :1200: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.)exit status 1

二、HTTP编程

Go语言标准库内建提供了net/http包,涵盖了HTTP客户端和服务器端的具体实现。

1、HTTP客户端

1)基本用法

func (c *Client) Get(url string) (r *Response, err error)

func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)

func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)

func (c *Client) Head(url string) (r *Response, err error)

func (c *Client) Do(req *Request) (resp *Response, err error)

2)封装

自定义http.Client

自定义http.Transport

2、HTTP服务器端

处理HTTP请求

func ListenAndServe(addr string, handler Handler) error

第一个参数addr即监听地址;第二个参数表示服务器端处理程序 ,通常为空。

处理HTTPS请求

func ListenAndServeTLS(addr string, certFile string, keyFile string, hander Handler) error

cerFile对应SSL证书文件存放路径,keyFile对应证书私钥文件路径。

三、RPC编程

RPC就是想实现函数调用模式的网络化。

运行时,一次客户机对服务器的RPC调用,其内部操作大致有如下 10步。

(1)调用客户端句柄,执行传送参数。

(2)调用本地系统内核发送网络消息。

(3)消息传送到远程主机。

(4)服务器句柄得到消息并取得参数。

(5)执行远程过程。

(6)执行的过程将结果返回服务器句柄。

(7)服务器句柄返回结果,调用远程系统内核。

(8)消息传回本地主机。

(9)客户句柄由内核接收消息。

(10)客户接收句柄返回的数据。

1、Go RPC

Go RPC的函数只有符合下面的条件才能被 远程访问,不然会被 忽略,详细的要求如下:

函数必须是导出的(首字母大写)。

必须有两个导出类型的参数。

第一个参数是接收的参数,第二个参数是返回给客户端的参数,第二个参数是必须 是指针类型。

函数还要有一个返回值error。

func (t *T) MethodName(argType T1, replyType *T2) error

2、HTTP RPC

代码如下:

package main

import (
	"error"
	"fmt"
	"net/http"
	"net/rpc"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return error.New("除数不能为零。")
	}
	quo.Quo = args.A / args.A
	quo.Rem = args.A % args.B
	return nil
}

func main() {
	arith := new(Arith)
	rpc.Register(arith)
	rpc.HandleHTTP()

	err := http.ListenAndServe(":1234", nil)
	if err != nil {
		fmt.Println(err.Error())
	}
}

运行结果如下:

CreateFile main1.go: The system cannot find the file specified.

代码如下:

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("用法:", os.Args[0], "server")
		os.Exit(1)
	}
	serverAddress := os.Args[1]

	client, err := rpc.DialHTTP("tcp", serverAddress+":1234")
	if err != nil {
		log.Fatal("Dial错误:", err)
	}

	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith错误:", err)
	}
	fmt.Printf("Arith:%d*%d=%d
", args.A, Args.B, reply)

	var quot Quotient
	err = client.Call("Arith.Divide", args, &quot)
	if err != nil {
		log.Fatal("arith错误:", err)
	}
	fmt.Printf("Arith:%d/%d=%d 余 %d
", args.A, Args.B, quot.Quo, quot.Rem)
}

运行结果如下:

用法: C:UsersA-XIAO~1AppDataLocalTempgo-build1502604559001exemain.exe server
exit status 1

书上说:把上面的服务器端和客户端的代码分别编译,先把服务器端开启,再开户客户端,输入代码,就会输出如下信息:

$ ./http_c localhost

Arith:17*8=136

Arith:17/8=2 余 1

3、TCP RPC

代码如下:

package main

import (
	"errors"
	"fmt"
	"net"
	"net/rpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("除数不能为零")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func main() {
	arith := new(Arith)
	rpc.Register(arith)
	tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
	checkError(err)
	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		rpc.ServeConn(conn)
	}
}
func checkError(err error) {
	if err != nil {
		fmt.Println("错误:", err.Error())
		os.Exit(1)
	}
}

运行结果如下:

代码如下:

package main

import (
	"fmt"
	"log"
	"net/rpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("用法:", os.Args[0], "server:port")
		os.Exit(1)
	}
	service := os.Args[1]
	client, err := rpc.Dial("tcp", service)
	if err != nil {
		log.Fatal("Dial错误:", err)
	}
	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith错误:", err)
	}
	fmt.Printf("Arith:%d*%d=%d\n", args.A, args.B, reply)

	var quot Quotient
	err = client.Call("Arith.Divide", args, &quot)
	if err != nil {
		log.Fatal("arith错误:", err)
	}
	fmt.Printf("Arith:%d/%d=%d 余 %d\n", args.A, args.B, quot.Quo, quot.Rem)
}

运行结果如下:

用法: C:\Users\A-XIAO~1\AppData\Local\Temp\go-build3660339729\b001\exe\main.exe server:port
exit status 1

4、JSON RPC

服务器端代码如下:

package main

import (
	"errors"
	"fmt"
	"net"
	"net/rpc"
	"net/rpc/jsonrpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

type Arith int

func (t *Arith) Multiply(args *Args, reply *int) error {
	*reply = args.A * args.B
	return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
	if args.B == 0 {
		return errors.New("除数不能为零")
	}
	quo.Quo = args.A / args.B
	quo.Rem = args.A % args.B
	return nil
}

func main() {
	arith := new(Arith)
	rpc.Register(arith)
	tcpAddr, err := net.ResolveTCPAddr("tcp", ":1234")
	checkError(err)

	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		jsonrpc.ServeConn(conn)
	}
}

func checkError(err error) {
	if err != nil {
		fmt.Println("错误:", err.Error())
		os.Exit(1)
	}
}

运行结果如下:

客户端的代码如下:

package main

import (
	"fmt"
	"log"
	"net/rpc/jsonrpc"
	"os"
)

type Args struct {
	A, B int
}

type Quotient struct {
	Quo, Rem int
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("用法:", os.Args[0], "server:port")
		log.Fatal(1)
	}
	service := os.Args[1]
	client, err := jsonrpc.Dial("tcp", service)
	if err != nil {
		log.Fatal("Dial错误:", err)
	}
	args := Args{17, 8}
	var reply int
	err = client.Call("Arith.Multiply", args, &reply)
	if err != nil {
		log.Fatal("arith错误:", err)
	}
	fmt.Printf("Arith:%d*%d=%d\n", args.A, args.B, reply)
	var quot Quotient
	err = client.Call("Arith.Divide", args, &quot)
	if err != nil {
		log.Fatal("arith错误:", err)
	}
	fmt.Printf("Arith:%d/%d=%d 余 %d\n", args.A, args.B, quot.Quo, quot.Rem)
}

运行结果如下:

用法: /var/folders/3x/txs8lfzs6s13njkkrvjmnz2c0000gn/T/go-build1075837643/b001/exe/main server:port
2022/03/02 20:11:23 1
exit status 1

5、RPC接口

type ClientCodec interface{

        WriteRequest(*Request, interface{}) error

        ReadResponseHeader(*Response) error

        ReadResponseBody(interface{}) error

        Close() error

}

type ServerCodec interface{

        ReadRequestHeader(*Request) error

        ReadRequestBody(interface{}) error

        WriteResponse(*Response, interface{}) error

        Close() error

}

四、数据库

http://go-database.sql.org

1、database/sql接口

1)sql.Register

2)driver.Driver

3) driver.Conn

4) driver.Stmt

5) driver.Tx

6) driver.Execer

7) driver.Result

8) driver.Rows

9) driver.RowsAffected

10) driver.Value

11) driver.ValueConverter

12) driver.Valuer

2、使用MySQL数据库

https://github.com/go-sql-driver/mysql 支持database/sql

https://github.com/ziutek/mymysql 支持database/sql

https://github.com/Philio/GoMySQL 不支持database/sql

CREATE TABLE 'userinfor' (

        'uid' INT(10) NOT NULL AUTO_INCREMENT,

        'username' VARCHAR(64) NULL DEFAULT NULL,

        'departname' VARCHAR(64) NULL DEFAULT NULL,

        ’created‘ DATE NULL DEFAULT NULL,

        PRIMARY KEY ('uid')

);

CREATE TABLE 'userdetail' (

        'uid' INT(10) NOT NULL DEFAULT '0',

        'intro' TEXT NULL,

        'profile' TEXT NULL,

        PRIMARY KEY ('uid')

)

代码如下:

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)



func main() {
	db,err:=sql.Open("mysql","zuolan:zuolan@/test?charset=utf8")
	checkErr(err)

    //插入数据
	stm,err:=db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
	checkErr(err)

	res,err:=stmt.Exec("张三","研发部门","2017-09-09")
	checkErr(err)

	id,err:=res.LastInsertId()
	checkErr(err)

	fmt.Println(id)
    //更数数据
	stmt,err=db.Prepare("update userinfo set username=? where uid=?")
	checkErr(err)

	res,err=stmt.Exec("zuolanupdate",id)
	checkErr(err)

	affect,err:=res.RowsAffected()
	checkErr(err)

	fmt.Println(affect)

    //查询数据
	rows,err:=db.Query("SELECT*FROM userinfo")
	checkErr(err)

	for rows.Next(){
		var uid int
		var username string
		var department string
		var created string
		err=rows.Scan(&uid,&usernae,&department,&created)
		checkErr(err)
		fmt.Println(uid)
		fmt.Println(username)
		fmt.Println(department)
		fmt.Println(created)
	}

    //删除数据
	stmt,err=db.Prepare("delete from userinfo where uid=?")
	checkErr(err)

	res,err=stmt.Exec(id)
	checkErr(err)

	affect,err=res.RowsAffected()
	checkErr(err)

	fmt.Println(affect)

	db.Close()
}

func checkErr(err error){
	if err!=nil{
		panic(err)
	}
}

运行结果如下:

2022/03/05 20:03:02 open test/test2/: no such file or directory
exit status 1

sql.Open()函数用来打开一个注册过的数据库驱动,go-sql-driver中注册了MySQL这个数据库驱动,第二个参数是DNS(Data Source Name),它是go-sql-driver定义的一些数据库连接和配置信息。它支持如下格式:

user@unix(/path/to/socket)/dbname?charset=utf8

user:password@tcp(localhost:5555)/dbname?charset=utf8

user:password@/dbname

user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname

db.Prepare()函数用来返回准备要执行的SQL操作,然后返回准备完毕的执行状态。

db.Query()函数用来直接执行SQL返回Rows结果。

stmt.Exec()函数用来执行stmt准备好的SQL语句。

3、使用SQLite数据库

https://github.com/mattn/go-sqlite3        支持database/sql接口,基于cgo

https://github.com/feyeleanor/gosqlite3        不支持database/sql接口,基于cgo

https://github.com/phf/go-sqlite3        不支持database/sql接口,基于cgo

示例的数据库表结构相应的建表SQL如下:

CREATE TABLE 'userinfo' (

        'uid' INTEGER PRIMARY KEY AUTOINCREMENT,

        'username' VARCHAR(64) NULL,

        'departname' VARCHAR(64) NULL,

        ’created‘ DATE NULL,

        PRIMARY KEY ('uid')

);

CREATE TABLE 'userdeatail' (

        'uid' INT(10) NULL,

        'intro' TEXT NULL,

        'profile' TEXT NULL,

        PRIMARY KEY ('uid')

)

代码如下:

package main

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	db, err := sql.Open("sqlite3", "./demo.db")
	checkErr(err)

	//插入数据
	stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) values(?,?,?)")
	checkErr(err)

	res, err := stmt.Exec("张三", "研发部门", "2017-09-09")
	checkErr(err)

	id, err := res.LastInsertId()
	checkErr(err)

	fmt.Println(id)
	//更新数据
	stmt,err = db.Prepare("update userinfo set username=? where uid=?")
	checkErr(err)

	res, err = stmt.Exec("zuolanupdate", id)
	checkErr(err)

	affect, err := res.RowsAffected()
	checkErr(err)

	fmt.Println(affect)

	//查询数据
	rows, err := db.Query("SELECT*FROM userinfo")
	checkErr(err)

	for rows.Next() {
		var uid int
		var username string
		var department string
		var created time.Time
		err = rows.Scan(&uid, &usernae, &department, &created)
		checkErr(err)
		fmt.Println(uid)
		fmt.Println(username)
		fmt.Println(department)
		fmt.Println(created)
	}

	//删除数据
	stmt, err = db.Prepare("delete from userinfo where uid=?")
	checkErr(err)

	res, err = stmt.Exec(id)
	checkErr(err)

	affect, err = res.RowsAffected()
	checkErr(err)

	fmt.Println(affect)

	db.Close()
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

运行结果如下:

main.go:8:2: cannot find package "github.com/mattn/go-sqlite3" in any of:
	/usr/local/go/src/github.com/mattn/go-sqlite3 (from $GOROOT)
	/Users/douxiaobo/go/src/github.com/mattn/go-sqlite3 (from $GOPATH)

4、使用PostgreSQL数据库存

github.com/lib/pq

github.com/jbarham/gopgsqldriver

github.com/lxn/go-pgsql

CREATE TABLE userinfo 

(

        uid serial NOT NULL,

        username character varying(100) NOT NULL,

        departname character varying(500) NOT NULL,

        Created date,

        CONSTRAINT userinfo_pkey PRIMARY KEY (uid)

)

WITH (OIDS=FALSE);

CREATE TABLE 'userdeatail'

(

        uid integer,

        intro character varying(180),

        profile character varying(180)

)

WITH (OIDS=FALSE);

代码如下:

package main

import (
	"database/sql"
	"fmt"

	_ "github.com/lib/pq"
)

func main() {
	db, err := sql.Open("postgres", "user=zuolan password=zuolan dbname=test sslmode=disable")
	checkErr(err)

	//插入数据
	stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) values($1,$2,$3) RETURNING uid")
	checkErr(err)

	res, err := stmt.Exec("张三", "研发部门", "2017-09-09")
	checkErr(err)

	//pg不支持这个函数,因为它没有类似MySQL的自增ID
	//id, err := res.LastInsertId()
	//checkErr(err)
	//fmt.Println(id)

	var lastInsertId int
	err = db.QueryRow("INSERT INTO userinfo(username,departname,created) values($1,$2,$3) returning uid;", "zuolan", "研发部门", "2017-09-09").Scan(&lastInsertId)
	checkErr(err)
	fmt.println("最后插入id =", lastInsertId)

	//更新数据
	stmt, err = db.Prepare("update userinfo set username=$1 where uid=$2")
	checkErr(err)

	res, err = stmt.Exec("zuolanupdate", id)
	checkErr(err)

	affect, err := res.RowsAffected()
	checkErr(err)

	fmt.Println(affect)

	//查询数据
	rows, err := db.Query("SELECT * FROM userinfo")
	checkErr(err)

	for rows.Next() {
		var uid int
		var username string
		var department string
		var created string
		err = rows.Scan(&uid, &username, &department, &created)
		checkErr(err)
		fmt.Println(uid)
		fmt.Println(username)
		fmt.Println(department)
		fmt.Println(created)
	}

	//删除数据
	stmt, err = db.Prepare("delete from userinfo where uid=$1")
	checkErr(err)

	res, err = stmt.Exec(1)
	checkErr(err)

	affect, err = res.RowsAffected()
	checkErr(err)

	fmt.Println(affect)

	db.Close()
}

func checkErr(err error) {
	if err != nil {
		panic(err)
	}
}

运行结果如下:

main.go:7:2: cannot find package "github.com/lib/pq" in any of:
	/usr/local/go/src/github.com/lib/pq (from $GOROOT)
	/Users/douxiaobo/go/src/github.com/lib/pq (from $GOPATH)

5、NoSQL数据库操作

1)Redis

Go语言支持Redis的驱动如下:

https://github.com/garyburd/redigo

https://github.com/go-redis/redis

https://github.com/hoisie/redis

https://github.com/alphazero/Go-Redis

https://github.com/simonz05/godis

2)MongoDB

Go语言支持MongoDB最好的驱动就是mgo(http://labix.org/mgo)。安装mgo的命令为:

go get gopkg.in/mgo.v2

五、Go语言使用Cookie

1、设置Cookie

Go语言中通过net/http包中的SetCookie来设置Cookie:

http.SetCookie(w ResponseWriter, Cookie *Cookie)

2、读取Cookie

深入探索Go中的网络编程 作者:禅与计算机程序设计艺术 《深入探索Go中的网络编程》(网络编程 - 深入探索Go中的网络编程 - 深入探索Go中的网络编程) 1. 引言 1.1. 背景介绍 阅读详情

相关推荐

Go编程基础教程:网络编程入门

作者:禅与计算机程序设计艺术 1.背景介绍 一、什么是网络编程? 计算机网络通信是指两个或多个节点之间传输信息的过程及相关协议。计算机网络可以分为三层结构:物理层、数据链路层、网络层;而互联网则是一个连接在不同地点之间的通信网络。 网络

AI天才研究院 1060

Go 网络编程

在 sql.DB 和连接池里面都看到了一个过期时间的处理。在开发中,还有类似的场景,例如说本地缓存的过期时间。

weixin_48943299的博客 2322

【Golang】深入探索Go语言中的网络编程实现方式

Go语言作为一种简洁而强大的编程语言,在网络编程方面表现尤为出色。其内置的net包提供了丰富的网络I/O基础设施,支持TCP、UDP协议,以及DNS解析等功能。 TCP提供可靠的、面向连接的通信,适用于需要确保数据传输完整性的场景。而UDP提供无连接的快速数据传输,适用于实时性要求较高但对数据完整性要求不高的场景。 Go语言的并发模型基于轻量级的goroutines和channels。每个网络请求都可以在自己的goroutine中处理,实现高效的并发。Channels用于在goroutines之间安全地

景天科技苑 5万+

go语言网络编程

TOCTOC。

gopher9511的博客 1955

深入理解Go语言网络编程:golang-net项目解析

本文还有配套的精品资源,点击获取 简介:开源项目-golang-net.zip是一个关于HTTP/2协议实现的Go语言网络编程项目,由知名开发者Bradfitz开发。该项目让开发者可以深入了解Go语言在网络编程的应用,掌握高效的HTTP/2通信实现。文章内容将详细讲解Go语言基础、HTTP/2协议的核心特性、Go网络库的使用技巧、多路复用、二进制分帧、头部压缩以及Go并发模...

weixin_42437253的博客 1274

MySQL表名大小写敏感问题终极解决:lower_case_table_names=1不生效的3个隐藏坑

本文深入剖析了MySQL表名大小写敏感问题中,设置lower_case_table_names=1参数不生效的三个核心原因。关键在于理解该参数只能在数据库初始化前设置,并需排查配置文件优先级与正确停止服务的顺序。文章提供了从Windows迁移到Linux的完整避坑指南与验证方法,帮助DBA和开发者彻底解决这一跨平台兼容性难题。

weixin_29229055的博客 392

Go语言网络编程

具体地,可以使用http.ListenAndServe函数创建HTTP服务器,使用http.Get函数和http.Post函数访问HTTP服务器,并使用http.ResponseWriter和http.Request实现HTTP请求和响应的读写操作。在Go语言中,可以使用net包实现套接字编程,具体地,可以使用net.Dial函数和net.Listen函数创建TCP连接和TCP服务器,使用net.Conn接口实现TCP连接的读写操作。在Go语言中,可以使用标准库中的net包实现TCP/IP协议的网络编程。

Kali与编程 1385

群晖Video Station海报墙插件更新了!手把手教你用c5h12o5插件搞定豆瓣/TMDB搜刮

本文详细介绍了如何使用c5h12o5插件为群晖Video Station配置高效的海报墙系统。该插件支持豆瓣、TMDB等多数据源并行搜刮,智能优先级设置和本地化优化,解决TMDB API失效问题,提升影视库管理体验。包含安装教程、配置策略和高级使用技巧,适合影视爱好者优化NAS媒体库。

weixin_29250403的博客 265

Go语言网络编程:基础与实战应用

1.背景介绍 1. 背景介绍 Go语言,也被称为Golang,是一种开源的编程语言,由Google的Robert Griesemer、Rob Pike和Ken Thompson于2009年开发。Go语言的设计目标是简洁、高效、可扩展和易于使用。它具有弱类型、垃圾回收、并发处理等特点。 网络编程是计算机网络的一种应用,用于实现计算机之间的数据传输和通信。Go语言的网络编程能力卓越,它的标准库提...

AI天才研究院 1235

从TM1到TM9:手把手教你用Wireshark抓包分析LTE下行调度与传输模式切换

本文详细解析了如何使用Wireshark抓包分析LTE下行调度中的传输模式切换,从TM1到TM9的动态调整过程。通过实战案例,包括小区边缘移动、高速场景和多用户调度,揭示eNodeB如何基于信道质量指标(如RSRP、SINR)动态切换传输模式,并提供关键信令解析和优化建议,助力网络工程师提升LTE网络性能。

weixin_42651748的博客 315

FPGA时序约束:从时钟树到数据路径的深度解析与优化策略

本文深入解析FPGA时序约束的核心技术与优化策略,涵盖时钟树结构分析、时序报告解读和数据路径优化。重点探讨Vivado工具中的时序约束方法,包括时钟定义、异常约束和物理优化,帮助工程师掌握寄存器级时序分析和系统级协同设计,实现高性能FPGA设计。

u5v6w7的博客 652

Go语言网络编程:learning-golang分布式系统开发终极指南

在当今互联网时代,**分布式系统开发**已成为构建高性能应用的核心技术。Go语言凭借其卓越的并发模型和简洁的语法,成为**分布式系统开发**的理想选择。learning-golang项目为您提供了一套完整的Go语言网络编程学习方案,帮助您快速掌握构建分布式系统的关键技能。 ## 为什么选择Go语言进行分布式开发? Go语言在设计之初就充分考虑了现代网络应用的需求。其轻量级协程(goroutin

gitblog_01173的博客 458

Ambari 3.0+BigTop 3.3集群部署实战

Ambari 3.0+BigTop 3.3集群部署

docsz的博客 609

Go语言实战:Go的网络编程与TCP_UDP

1.背景介绍 1. 背景介绍 Go语言是一种现代的编程语言,由Google的Robert Griesemer、Rob Pike和Ken Thompson在2009年开发。Go语言旨在简化编程过程,提高开发效率,同时具有高性能和可靠性。Go语言的网络编程功能是其强大的特点之一,它提供了简单易用的API来处理TCP/UDP协议。 在本文中,我们将深入探讨Go语言的网络编程功能,涵盖TCP/UDP...

AI天才研究院 1170

FlashBootloader01-基于UDS的Flash Bootloader【架构篇】

文章目录1 Flash Bootloader的依赖项2 Flash Bootloader中使用的UDS的主要功能3 Flash Bootloader更新固件3.1 初始化MCU3.2 刷写流程 1 Flash Bootloader的依赖项 通常Flash Bootloader由以下四方面组成: UDS协议 上位机「编写Bootloader Sequence」 通信驱动(如CAN ) Flash Driver 2 Flash Bootloader中使用的UDS的主要功能 1. 10 02服务 客户端

3591

Qt之QMqtt 发送图片数据

MQTT是一个基于客户端-服务器的消息发布/订阅传输协议;MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛;在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT);其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用;

Ilson_的博客 1437

视频教程-桫哥-GOlang-10网络编程-Go语言

桫哥-GOlang-10网络编程 多年互联网从业经验; 有丰富的的企业网站、...

weixin_32216275的博客 407

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Go语言网络编程:HTTP与WebSocket

1.背景介绍 1. 背景介绍 Go语言(Golang)是Google开发的一种静态类型、编译式、多平台的编程语言。Go语言的设计目标是简洁、高效、可扩展和易于使用。它具有匿名函数、接口、闭包、垃圾回收等特性。Go语言的网络编程是其强大功能之一,可以轻松搭建高性能的网络应用。本文将从HTTP和WebSocket两个方面深入探讨Go语言网络编程的核心概念、算法原理、最佳实践和实际应用场景。 2....

AI天才研究院 1246

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

上一篇: Go语言的并发编程
下一篇: Go语言的I/O编程
DXB2021
博客等级 码龄5年 307粉丝 289原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值