一、概述

1.socket解读

2.socket与HTTP关系

3.socket与TCP/UDP关系

4.HTTP与TCP/UDP关系

二、第一步:绑定端口
//创建socket文件描述符,绑定ip:port,改变socket状态为监听状态
netListen, err := net.Listen("tcp", "192.168.123.27:9800")
//defer延迟关闭(方法返回的时候关闭)资源,以免引起内存泄漏
defer netListen.Close()

这里的net.Listen方法为net包dial.go类中的一个方法。

第一个参数是制定网络IO类型,比如有TCP/IP, UDP, 域名解析, Unix domain socket

第二个参数是host:port 就是ip和端口号的组合。

第一个返回的的是当前连接socket状态监听器。

第二个返回是错误,我们可以判断,如果错误不为空就返回。

第一个返回的netListen是net.go类中的一个接口:

// A Listener is a generic network listener for stream-oriented protocols.
//
// Multiple goroutines may invoke methods on a Listener simultaneously.
type Listener interface {
    // Accept waits for and returns the next connection to the listener.
    //从socket recive队列里获取一个建立好的连接
    Accept() (Conn, error)

    // Close closes the listener. 
    //关闭监听器
    // Any blocked Accept operations will be unblocked and return errors.
    Close() error

    // Addr returns the listener's network address.
    //返回链接的传输类型和ip地址
    Addr() Addr
}

这个接口里的三个方法实现了对打开socket的三种基本操作,并在很多类中实现了这个接口,具体请看:

这里写图片描述

不同的具体链接都会实现这个接口,方便我们对socket链接做基本操作。

三、第二步:建立连接
conn, err := netListen.Accept()  //第二步:获取连接

第一个返回 conn是当前socket链接。

第二个返回是错误,我们可以判断,如果错误不为空就返回。

第一个返回conn也是net.go类中的一个接口,源代码如下:

// Conn is a generic stream-oriented network connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
type Conn interface {
    // Read reads data from the connection.
    // Read can be made to time out and return an 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 an 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 and pending
    // I/O, not just the immediately following call to Read or
    // Write. After a deadline has been exceeded, the connection
    // can be refreshed by setting a deadline in the future.
    //
    // 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
    // and any currently-blocked Read call.
    // A zero value for t means Read will not time out.
    SetReadDeadline(t time.Time) error

    // SetWriteDeadline sets the deadline for future Write calls
    // and any currently-blocked Write call.
    // 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
}

我们可以看到里面有一系列的读写和关闭操作,就如上面说到的Listener 接口,也肯定是在很多具体的socket连接中被实现了,验证如下:
这里写图片描述

我们这里用的肯定是TCPConn了。

四、第三步:读写数据

上面我们也看到了,既然我们用的是TCPConn,也就是基于TCP的Socket链接。
里面的读写操作都已经实现了,我们直接调用就行了。
下面是socket服务端和客户端的交互过程:
这里写图片描述

其实里面的不管是客户端还是服务端,只要socket长链接建立成功了,我们读写都随便的,可先可后,具体看你的业务需求。
我们使用一个无限for循环来不断读写:

buffer := make([]byte, 2048)

    for {  //无限循环

        n, err := conn.Read(buffer) //第三步:读取从该端口传来的内容
        //words := "ok" //向链接中写数据,向链接既可以先读也可以先写,看自己的需要
        words := "golang socket server : " + strconv.Itoa(rand.Intn(100)) //向链接中写数据
        conn.Write([]byte(words))
        if err != nil {
            Log(conn.RemoteAddr().String(), " connection error: ", err)
            return //出错后返回
        }

        Log(conn.RemoteAddr().String(), "receive data string:\n", string(buffer[:n]))

    }
五、服务端完整代码

package main

import (
    "fmt"
    "net"
    "log"
    "os"
    "math/rand"
    "strconv"
)

func main() {

    //建立socket,监听端口  第一步:绑定端口
    //netListen, err := net.Listen("tcp", "localhost:1024")
    netListen, err := net.Listen("tcp", "192.168.123.27:9800")
    CheckError(err)
    //defer延迟关闭改资源,以免引起内存泄漏
    defer netListen.Close()

    Log("Waiting for clients")
    for {
        conn, err := netListen.Accept()  //第二步:获取连接
        if err != nil {
            continue  //出错退出当前一次循环
        }

        Log(conn.RemoteAddr().String(), " tcp connect success")
        //handleConnection(conn)  //正常连接就处理
        //这句代码的前面加上一个 go,就可以让服务器并发处理不同的Client发来的请求
        go handleConnection(conn) //使用goroutine来处理用户的请求
    }
}
//处理连接
func handleConnection(conn net.Conn) {

    buffer := make([]byte, 2048)

    for {  //无限循环

        n, err := conn.Read(buffer) //第三步:读取从该端口传来的内容
        //words := "ok" //向链接中写数据,向链接既可以先读也可以先写,看自己的需要
        words := "golang socket server : " + strconv.Itoa(rand.Intn(100)) //向链接中写数据
        conn.Write([]byte(words))
        if err != nil {
            Log(conn.RemoteAddr().String(), " connection error: ", err)
            return //出错后返回
        }

        Log(conn.RemoteAddr().String(), "receive data string:\n", string(buffer[:n]))

    }
}

//log输出
func Log(v ...interface{}) {
    log.Println(v...)
}

//处理error
func CheckError(err error) {
    if err != nil {
        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
        os.Exit(1)
    }
}
六、服务端完整代码