golang笔记(2)写一个简单的socket服务端

来源:互联网 发布:以色列 巴基斯坦 知乎 编辑:程序博客网 时间:2024/06/10 19:06

windows开发环境搭建http://blog.csdn.net/u010072711/article/details/72859731

一、概述

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 mainimport (    "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...)}//处理errorfunc CheckError(err error) {    if err != nil {        fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())        os.Exit(1)    }}

六、服务端完整代码

Github完整代码:https://github.com/Dawish/GoStudy/blob/master/src/main/SocketServer.go

原创粉丝点击