tcp编程socket编程
Posted 关灯吃面
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tcp编程socket编程相关的知识,希望对你有一定的参考价值。
tcp编程(需要建立连接,三次握手,四次挥手,然后发送信息流,数据包是有序的)
udp编程(知道IP、端口直接发送数据,数据包可能是无序的)
1、客户端和服务器客
socket编程
1.服务端的处理流程
a.监听端口
b.接收客户端的链接
c.创建goroutine,处理该链接
2.客户端的处理流程
a.建立与服务端的链接
b.进行数据收发
c.关闭链接
3.服务端代码
package main import ( "fmt" "net"//导入socket的包 ) func main() { fmt.Println("start server...") listen, err := net.Listen("tcp", "0.0.0.0:50000")//监听50000端口 if err != nil { fmt.Println("listen failed, err:", err) return } for { conn, err := listen.Accept()//等待客户端连接 if err != nil { fmt.Println("accept failed, err:", err) continue } go process(conn) } } func process(conn net.Conn) { defer conn.Close() for { buf := make([]byte, 512) _, err := conn.Read(buf)//读取客户端传输过来的数据 if err != nil { fmt.Println("read err:", err) return } fmt.Println("read: ", string(buf)) } }
4.客户端代码
package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { conn, err := net.Dial("tcp", "localhost:50000") //建立链接 if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() inputReader := bufio.NewReader(os.Stdin) //从终端读取数据 for { input, _ := inputReader.ReadString(\'\\n\') //从终端读取一行数据 trimmedInput := strings.Trim(input, "\\r\\n")//去掉字符串\\r\\n if trimmedInput == "Q" { return } _, err = conn.Write([]byte(trimmedInput))//将信息发给服务端 if err != nil { return } } }
5.发送http请求
package main
import (
"fmt"
"io"
"net"
)
func main() {
conn, err := net.Dial("tcp", "www.baidu.com:80")
if err != nil {
fmt.Println("Error dialing", err.Error())
return
}
defer conn.Close()
msg := "GET / HTTP/1.1\\r\\n"
msg += "Host: www.baidu.com\\r\\n"
msg += "Connection: close\\r\\n"
msg += "\\r\\n\\r\\n"
_, err = io.WriteString(conn, msg)
if err != nil {
fmt.Println("write string failed, ", err)
return
}
buf := make([]byte, 4096)
for {
count, err := conn.Read(buf)
if err != nil {
break
}
fmt.Println(string(buf[0:count]))
}
}
redis
redis是个开源的高性能的key-value的内存数据库,可以把它当成远程的数据结构。
支持的value类型 非常多, 比如string、list(链表)、set(集合)、hash表等等
redis性能非常高,单机能够达到15w qps,通常适合做缓存。
PV=page view 是指页面被浏览的次数,比如你打开一网页,那么这个网站的pv就算加了一次; TPS=transactions per second 是每秒内的事务数 QPS=queries per second 是指每秒内查询次数,比如执行了select操作,相应的qps会增加。
RPS=requests per second 是指每秒请求数
1、redis使用
安装使用第三方开源的redis库: github.com/garyburd/redigo/redis go get github.com/garyburd/redigo/redis import( “github.com/garyburd/redigo/redis" )
2、连接redis
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") //连接redis
if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() }
3、Set 接 口
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("Set", "abc", 100) //设置值 if err != nil { fmt.Println(err) return } r, err := redis.Int(c.Do("Get", "abc")) //获取设置的值 if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) }
4、Hash表
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("HSet", "books", "abc", 100) //设置hash表的名字books if err != nil { fmt.Println(err) return } r, err := redis.Int(c.Do("HGet", "books", "abc")) //因为读取出来是字符串,所以redis.Int转换成整数 if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) }
5、批量Set
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("MSet", "abc", 100, "efg", 300) //批量设置 if err != nil { fmt.Println(err) return } r, err := redis.Ints(c.Do("MGet", "abc", "efg")) //读取出来是一个切片 if err != nil { fmt.Println("get abc failed,", err) return } for _, v := range r { fmt.Println(v) } }
6、过期时间
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("expire", "abc", 10) //设置过期时间,abc为key值,10为超时时间,插入的时候不能设置超时时间,因为插入和设置超时时间是两个不同的接口 if err != nil { fmt.Println(err) return } }
7、队列操作
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("lpush", "book_list", "abc", "ceg", 300) //book_list队列的名字 if err != nil { fmt.Println(err) return } r, err := redis.String(c.Do("lpop", "book_list")) //从队列里面取值 if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) }
8、连接池
type Pool struct { //Dial 是创建链接的方法 Dial func() (Conn, error) //TestOnBorrow 是一个测试链接可用性的方法 TestOnBorrow func(c Conn, t time.Time) error // 最大的空闲连接数,表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态 MaxIdle int // 最大的激活连接数,表示同时最多有N个连接 ,为0事表示没有限制 MaxActive int //最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭 IdleTimeout time.Duration // 当链接数达到最大后是否阻塞,如果不的话,达到最大后返回错误 Wait bool }
package main import ( "encoding/json" "flag" "fmt" "time" "github.com/garyburd/redigo/redis" ) //声明一些全局变量 var ( pool *redis.Pool redisServer = flag.String("redisServer", ":6379", "") //flag对命令参数进行解析是常见的需求 //go run main.go -redisServer "8000",启动程序是给redisServer赋值,第3个参数是使用说明 //如果执行程序不带有-fredisServer 那么flag.Sring()的第2个参数则为默认值 redisPassword = flag.String("redisPassword", "123456", "") ) //初始化一个连接池pool func newPool(server, password string) *redis.Pool { return &redis.Pool{ MaxIdle: 64, //空闲连接数 MaxActive: 1000, //活跃连接数 IdleTimeout: 240 * time.Second, //240s超时时间,连接池没有使用,会关闭 Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", server) if err != nil { return nil, err } /* //密码权限验证 if _, err := c.Do("AUTH", password); err != nil { c.Close() return nil, err }*/ return c, err }, //从连接池获取一个链接,测试连接是否可用 TestOnBorrow: func(c redis.Conn, t time.Time) error { if time.Since(t) < time.Minute { return nil } _, err := c.Do("PING") return err }, } } type Student struct { Id int `json:"id"` Name string `json:"name"` Age int `json:"age"` Score float32 `json:"score"` } func main() { flag.Parse() pool = newPool(*redisServer, *redisPassword) conn := pool.Get() defer conn.Close() var stu Student = Student{ Id: 1000, Name: "abc", Age: 89, Score: 99.2, } data, _ := json.Marshal(stu) //redis操作结构体,需要转成json格式 v, err := conn.Do("SET", 1000, string(data)) if err != nil { fmt.Println(err) return } fmt.Println(v) ret, err := redis.String(conn.Do("GET", 1000)) if err != nil { fmt.Println(err) return } var stu01 Student json.Unmarshal([]byte(ret), &stu01) fmt.Printf("stu01:%#v\\n", stu01) }
socket练习:
client/main.go
package main import ( "bufio" "encoding/json" "fmt" "net" "os" "github.com/sherlockhua/goproject/day9/proto" ) var recvMsg chan interface{} func main() { conn, err := net.Dial("tcp", "192.168.14.200:18080") if err != nil { fmt.Printf("dial server failed, err:%v\\n", err) return } fmt.Fprintf recvMsg = make(chan interface{}, 1000) defer conn.Close() go read(conn) err = login(conn) if err != nil { fmt.Printf("login failed, err:%v\\n", err) return } msg := <-recvMsg loginResp, ok := msg.(*proto.LoginResponse) if !ok { fmt.Printf("unexpect msg:%T, %+v\\n", msg, msg) return } if loginResp.Errno != 0 { fmt.Printf("login failed, err:%v\\n", loginResp.Message) return } fmt.Printf("login succ\\n") for { var data string reader := bufio.NewReader(os.Stdin) //在shell输入 data, err := reader.ReadString(\'\\n\') //从shell读取一行数据 if err != nil { continue } err = sendMessage(conn, data) //开始发消息 if err != nil { fmt.Printf("send message failed, err:%v\\n", err) return } } } func sendMessage(conn net.Conn, data string) (err error) { var message proto.MessageRequest message.Message = data message.Username, _ = os.Hostname() //获取主机名 body, err := json.Marshal(message) if err != nil { fmt.Printf("marshal failed, err:%v\\n", err) return } err = proto.WritePacket(conn, proto.CmdSendMessageRequest, body) //将数据包发送给服务端 if err != nil { fmt.Printf("send to server failed, err:%v\\n", err) return } return } func login(conn net.Conn) (err error) { var loginReq proto.LoginRequest loginReq.Password = "admin" loginReq.Username = "admin" body, err := json.Marshal(loginReq) if err != nil { fmt.Printf("marshal failed, err:%v\\n", err) return } err = proto.WritePacket(conn, proto.CmdLoginRequest, body) if err != nil { fmt.Printf("send to server failed, err:%v\\n", err) return } return } func read(conn net.Conn) { for { body, cmd, err := proto.ReadPacket(conn) if err != nil { fmt.Printf("read from server failed, err:%v\\n", err) return } switch cmd { case proto.CmdLoginResponse: err = processLoginResponse(conn, body) case proto.CmdSendMessageResponse: err = processSendMsgResponse(conn, body) case proto.CmdBroadMessage: err = processBroadMessage(conn, body) default: fmt.Printf("unsupport cmd[%v]\\n", cmd) return } } } func processLoginResponse(conn net.Conn, body []byte) (err error) { //登录返回 var loginResponse proto.LoginResponse err = json.Unmarshal(body, &loginResponse) if err != nil { fmt.Printf("unmarshal failed, err:%v\\n", err) return } recvMsg <- &loginResponse return } func processSendMsgResponse(conn net.Conn, body []byte) (err error) { var messageResp proto.MessageResponse err = json.Unmarshal(body, &messageResp) if err != nil { fmt.Printf("unmarshal failed, err:%v\\n", err) return } if messageResp.Errno != 0 { fmt.Printf("消息发送失败:%v\\n", messageResp.Message) return } return } func processBroadMessage(conn net.Conn, body []byte) (err error) { var msg proto.BroadMessage err = json.Unmarshal(body, &msg) if err != nil { fmt.Printf("unmarshal failed, err:%v\\n", err) return } fmt.Printf("%s:\\n %s\\n\\n", msg.Username, msg.Message) return }
server/
package main import ( "fmt" "net" ) var ( clientMgr *ClientMgr ) func startServer(addr string) (l net.Listener, err error) { l, err = net.Listen("tcp", addr) //监听一个地址 if err != nil { fmt.Printf("listen addr:%s failed, err:%v\\n", addr, err) return } return } func main() { clientMgr = NewClientMgr(200) fmt.Printf("start server...\\n"以上是关于tcp编程socket编程的主要内容,如果未能解决你的问题,请参考以下文章