分享
  1. 首页
  2. 文章

javaer to go之TCP Socket与Goroutine

p_3er · · 3284 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

1、前言

其实我前面一篇笔记的例子就是socket的一个例子,但是由于大部分的笔记说明都是在整理基础的东西,所以socket的笔记单独列在这里。

server.go

package socket
import (
 "fmt"
 "net"
)
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) {
 defer conn.Close()
 for {
 buffer := make([]byte, 1024)
 length, _:= conn.Read(buffer)
 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())
 }
}

2、TCP Socket Server

2.1 导包

和java一样,golang提供了比较强大的网络编程的基础库net。

当然我们想使用golang的网络相关的库,先导入net包。而fmt包,则是为了测试一些结果,作为终端输出的工具包。

import (
 "fmt"
 "net"
)

2.2 声明地址

通过net包的ResolveTCPAddr函数声明一个TCPAddr。

// TCPAddr represents the address of a TCP end point.
type TCPAddr struct {
 IP IP
 Port int
 Zone string // IPv6 scoped addressing zone
}
func ResolveTCPAddr(net, addr string) (*TCPAddr, error)
  • net参数是"tcp4"、"tcp6"、"tcp"中的任意一个,分别表示TCPv4、TCPv6或者任意。查看ResolveTCPAddr的源码后,net参数甚至可以是一个空字符串,估计是为了兼容之前的Go版本的原因。
 switch net {
 case "tcp", "tcp4", "tcp6":
 case "": // a hint wildcard for Go 1.0 undocumented behavior
 net = "tcp"
 default:
 return nil, UnknownNetworkError(net)
 }
  • addr表示域名或者IP地址,例如"www.baidu.com:80" 或者"127.0.0.1:22"。

从源码上看,ip是可以缺省的,但端口不能缺省。ip缺省的话,默认使用的是本地地址。ip和端口使用英文的冒号(:)分开,net包是通过冒号分隔字符串的方式来处理ResolveTCPAddr的net参数的。

ResolveTCPAddr可供服务器及客户端共用的,不像java那样区分Socket和ServerSocket。所以缺省ip的逻辑很好理解。

我们这里写的是服务器端的逻辑,只输入端口号。

// SplitHostPort splits a network address of the form "host:port",
// "[host]:port" or "[ipv6-host%zone]:port" into host or
// ipv6-host%zone and port. A literal address or host name for IPv6
// must be enclosed in square brackets, as in "[::1]:80",
// "[ipv6-host]:http" or "[ipv6-host%zone]:80".
func SplitHostPort(hostport string) (host, port string, err error) {
 j, k := 0, 0
  // The port starts after the last colon.
 i := last(hostport, ':')
 if i < 0 {
 goto missingPort
 }
 if hostport[0] == '[' {
  // Expect the first ']' just before the last ':'.
 end := byteIndex(hostport, ']')
 if end < 0 {
 err = &AddrError{Err: "missing ']' in address", Addr: hostport}
 return
 }
 switch end + 1 {
 case len(hostport):
  // There can't be a ':' behind the ']' now.
 goto missingPort
 case i:
  // The expected result.
 default:
  // Either ']' isn't followed by a colon, or it is
  // followed by a colon that is not the last one.
 if hostport[end+1] == ':' {
 goto tooManyColons
 }
 goto missingPort
 }
 host = hostport[1:end]
 j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions
 } else {
 host = hostport[:i]
 if byteIndex(host, ':') >= 0 {
 goto tooManyColons
 }
 if byteIndex(host, '%') >= 0 {
 goto missingBrackets
 }
 }
 if byteIndex(hostport[j:], '[') >= 0 {
 err = &AddrError{Err: "unexpected '[' in address", Addr: hostport}
 return
 }
 if byteIndex(hostport[k:], ']') >= 0 {
 err = &AddrError{Err: "unexpected ']' in address", Addr: hostport}
 return
 }
 port = hostport[i+1:]
 return
missingPort:
 err = &AddrError{Err: "missing port in address", Addr: hostport}
 return
tooManyColons:
 err = &AddrError{Err: "too many colons in address", Addr: hostport}
 return
missingBrackets:
 err = &AddrError{Err: "missing brackets in address", Addr: hostport}
 return
}

2.3 启动监听器

  • 通过net包的ListenTCP之前的TCPAddr启动一个监听器。
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) 

这里的net参数也是支付"tcp", "tcp4", "tcp6"。但不支持空字符串。

  • 通过TCPListener的Accept方法阻塞式接收客户端的连接
func (l *TCPListener) Accept() (Conn, error) 

整个过程和java socket server的思想基本是一至的。

2.4 Conn

type Conn interface {
 // Read reads data from the connection.
 // Read can be made to time out and return a Error with Timeout() == true
 // after a fixed time limit; see SetDeadline and SetReadDeadline.
 Read(b []byte) (n int, err error)
 // Write writes data to the connection.
 // Write can be made to time out and return a Error with Timeout() == true
 // after a fixed time limit; see SetDeadline and SetWriteDeadline.
 Write(b []byte) (n int, err error)
 // Close closes the connection.
 // Any blocked Read or Write operations will be unblocked and return errors.
 Close() error
 // LocalAddr returns the local network address.
 LocalAddr() Addr
 // RemoteAddr returns the remote network address.
 RemoteAddr() Addr
 // SetDeadline sets the read and write deadlines associated
 // with the connection. It is equivalent to calling both
 // SetReadDeadline and SetWriteDeadline.
 //
 // A deadline is an absolute time after which I/O operations
 // fail with a timeout (see type Error) instead of
 // blocking. The deadline applies to all future I/O, not just
 // the immediately following call to Read or Write.
 //
 // An idle timeout can be implemented by repeatedly extending
 // the deadline after successful Read or Write calls.
 //
 // A zero value for t means I/O operations will not time out.
 SetDeadline(t time.Time) error
 // SetReadDeadline sets the deadline for future Read calls.
 // A zero value for t means Read will not time out.
 SetReadDeadline(t time.Time) error
 // SetWriteDeadline sets the deadline for future Write calls.
 // Even if write times out, it may return n > 0, indicating that
 // some of the data was successfully written.
 // A zero value for t means Write will not time out.
 SetWriteDeadline(t time.Time) error
}
  • Conn接口的Read和Write方法进行IO操作。
  • 通过SetDeadline、SetReadDeadline、SetWriteDeadline方法设置IO超时时间
  • RemoteAddr方法可以获取到客户端的ip及端口信息。
fmt.Println("新连接:", conn.RemoteAddr().String())

注意:Conn接口的Read也是阻塞的。

2.5 defer

func handleConn(conn net.Conn) {
 defer conn.Close()
 for {
 ...
 }
}

关键字 defer 的用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块,它一般用于释放某些已分配的资源,比如常见的关闭文件、释放缓存。

这里是操作完写操作后,就把conn连接关掉。

3、Goroutine

在Go中,每个并发处理的部分被称作 goroutines(协程),它可以进行更有效的并发运算。在协程和操作系统线程之间并无一对一的关系:协程是根据一个或多个线程的可用性,映射(多路复用,执行于)在他们之上的;协程调度器在 Go 运行时很好的完成了这个工作。

golang想使用协程的方式很简单,只要在需要使用协程的方法调用的前面加上go关键字就可以了。
如:

go handleConn(conn)

这种方式相比java的线程,太优雅了。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:p_3er

查看原文:javaer to go之TCP Socket与Goroutine

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3284 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏