javaer to go之TCP Socket与Goroutine

简介: <div class="markdown_views"><h1 id="1前言"> 1、前言</h1><p>其实我前面一篇笔记的例子就是socket的一个例子,但是由于大部分的笔记说明都是在整理基础的东西,所以socket的笔记单独列在这里。</p><p>server.go</p><pre class="prettyprint"><code class=" h

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的线程,太优雅了。

目录
相关文章
|
2月前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
2月前
|
安全 Java Go
Go语言中的并发编程:掌握goroutine与通道的艺术####
本文深入探讨了Go语言中的核心特性——并发编程,通过实例解析goroutine和通道的高效使用技巧,旨在帮助开发者提升多线程程序的性能与可靠性。 ####
|
2月前
|
缓存 监控 前端开发
Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信
本文探讨了在 Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信。首先介绍了 WebSocket 和 Socket.IO 的基本概念及其优势,接着详细讲解了 Go 语言中 WebSocket 的实现方法,以及二者集成的重要意义和具体步骤。文章还讨论了集成过程中需要注意的问题,如协议兼容性、消息格式、并发处理等,并提供了实时聊天、数据监控和在线协作工具等应用案例,最后提出了性能优化策略,包括数据压缩、缓存策略和连接管理优化。旨在帮助开发者更好地理解并应用这些技术。
79 3
|
2月前
|
安全 Go 调度
探索Go语言的并发模型:goroutine与channel
在这个快节奏的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你深入了解Go语言的goroutine和channel,这两个核心特性如何协同工作,以实现高效、简洁的并发编程。
|
2月前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。
|
2月前
|
安全 Go 调度
解密Go语言并发模型:CSP与goroutine的魔法
在本文中,我们将深入探讨Go语言的并发模型,特别是CSP(Communicating Sequential Processes)理论及其在Go中的实现——goroutine。我们将分析CSP如何为并发编程提供了一种清晰、简洁的方法,并通过goroutine展示Go语言在处理高并发场景下的独特优势。
|
3月前
|
安全 Go 调度
探索Go语言的并发之美:goroutine与channel
在这个快节奏的技术时代,Go语言以其简洁的语法和强大的并发能力脱颖而出。本文将带你深入Go语言的并发机制,探索goroutine的轻量级特性和channel的同步通信能力,让你在高并发场景下也能游刃有余。
|
3月前
|
存储 安全 Go
探索Go语言的并发模型:Goroutine与Channel
在Go语言的多核处理器时代,传统并发模型已无法满足高效、低延迟的需求。本文深入探讨Go语言的并发处理机制,包括Goroutine的轻量级线程模型和Channel的通信机制,揭示它们如何共同构建出高效、简洁的并发程序。
|
3月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
185 1
|
3月前
|
存储 Go 调度
深入理解Go语言的并发模型:goroutine与channel
在这个快速变化的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你穿越Go语言的并发世界,探索goroutine的轻量级特性和channel的同步机制。摘要部分,我们将用一段对话来揭示Go并发模型的魔力,而不是传统的介绍性文字。