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

《QMT量化实战系列》支持自定义筛选与排序的多因子策略,实测年化收益超100%

【QMT量化实战系列】推出《支持自定义筛选与排序的多因子策略》,通过Tushare获取38个核心因子数据,实现股票多维筛选(剔除ST股、次新股及特定板块)。策略创新性地采用多因子加权排名机制,通过(股票数-排名+1)/股票数*100的算法计算各因子得分,支持自定义权重组合。该策略提供完整代码及详细注释,涵盖数据获取、因子筛选、权重排序等核心环节,帮助用户构建个性化量化模型。系列内容每月更新1-2期,由浅入深讲解QMT平台实战应用。

qq_46262068的博客 1255

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

STM32测测速---编码电机读取速度的计算

速度计算:轮胎每转一圈的脉冲数取决于编码器的分辨率,可由下面公式进行计算:PPR是电机的线数最后可以计算轮胎每转一圈的脉冲数为:轮胎每转一圈的脉冲数 = 9.6 × 11 × 4 = 422.4最后的速度公式简化为:速度 = (0.01 秒内产生的脉冲数 ∗ 0.055 ∗ π )/(0.01 ∗422.4​。

weixin_69414084的博客 3811

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

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

AI天才研究院 1235

火山引擎之火山方舟知识库+OpenWebUI,搭建独属于自己的AI智能客服

创建接入点,接入点名称可按个人需求而定,比如Doubao-1.5-pro-32k,在添加模型选择Doubao-1.5-pro-32k,点击确定(PS:有些模型不支持知识库,若发现后续无法选择,请重新创建推理接入点。选择应用广场,创建零代码应用,在创建好的应用设置内编辑相关信息,并选择推理接入点,即先前建好的推理接入点;此处的模型名称是火山的模型ID名称,可在OpenWebUI设置-模型 中修改名称。至此所需的apikey和模型ID和请求的api地址(可选配置,非本文重点参数)。进入火山官网,注册账号。

AmaZInGvCH的博客 2652

AIPex:AI + 自然语言驱动的浏览器自动化扩展

AIPex是一款革命性的Chrome扩展,通过AI技术实现自然语言驱动的浏览器自动化。它支持30多种自动化工具,包括智能标签页管理(分组、切换、批量操作)、页面内容提取(文本、链接、元数据)、交互元素操作(点击、滚动)以及表单智能填写等功能。用户只需用自然语言下达指令,即可快速完成复杂的浏览器操作,大幅提升工作效率。AIPex特别适合需要处理大量重复性网页操作的用户,如数据分析师、产品经理等。

blues_C的博客 1127

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

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

gitblog_01173的博客 458

YOLO11改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总

YOLOv11改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总

魔改工程师的博客 4523

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

LeRobot 入门教程(一)软件环境、部署流程及摄像头配置

🤖 LeRobot:开源机器人学习平台 摘要:LeRobot是一个基于PyTorch的开源机器人学习平台,提供模型、数据集和工具,旨在降低机器人技术门槛。平台包含经过验证的模仿学习和强化学习方法,并提供预训练模型、人类演示数据集及模拟环境。安装需从源码获取,支持Python 3.10环境,可选扩展包括仿真环境(如gym-aloha)、电机控制(Dynamixel/Feetech SDK)和实验跟踪(Weights & Biases)。该平台托管在HuggingFace,并设有社区Discord频道

2506_90492529的博客 3417

RK3566 Android11默认客户Launcher修改

【代码】RK3566 Android11默认客户Launcher修改。

Android系统开发笔记 1723

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

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

weixin_32216275的博客 407

【网络基础2】路由器的 “两扇门”:二层接口和三层接口到底有啥不一样?

家里的路由器背后总有几个网线接口,有的标着 “LAN”,有的标着 “WAN”;公司机房的路由器上,接口更是五花八门,有的插着网线,有的连着光纤,旁边还贴着 “GE0/0/1”“Eth-Trunk1” 这样的标签。你可能会想:不就是插网线的地方吗?有啥区别?其实,这些接口分 “二层接口” 和 “三层接口”,它们的作用差得远了 —— 就像你家的门,有的是 “小区内的单元门”(只通小区内部),有的是 “小区的大门”(能通往外面的世界)。接下来,咱们用大白话 + 生活例子,一点点把这两种接口讲透。

jundao1997的专栏 565

Go语言网络编程:HTTP与WebSocket

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

AI天才研究院 1246

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: 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、付费专栏及课程。

余额充值