【go语言 socket编程系列】Conn接口类型及简单服务器实现Read Write 和Close方法

【Conn接口类型】

Conn是一种通用的面向流的网络连接,多个Goroutine可以同时调用Conn上的方法。

主要通过Read(b []byte)读取数据,Write(b [byte]) 写数据 及Close() 关闭连接。

其源码定义在net.go中

type Conn interface {        
        Read(b []byte) (n int, err error)      
        Write(b []byte) (n int, err error)      
        Close() error      
        LocalAddr() Addr       
        RemoteAddr() Addr
        SetDeadline(t time.Time) error     
        SetReadDeadline(t time.Time) error
        SetWriteDeadline(t time.Time) error
}

【conn结构体】

conn结构体为一个 *netFD的网络文件描述符号,Conn接口方法都会作用在conn对象上。

type conn struct {
        fd *netFD
}

Read(b []byte)

Read 方法用于从conn对象中读取字节流并写入[]byte类型的b对象中。 

func (c *conn) Read(b []byte) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
        n, err := c.fd.Read(b)
        if err != nil && err != io.EOF {
                err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
        }
        return n, err
}

Write(b [byte])

Write()方法用于把[]byte类型的切片中的数据写入到conn对象中

func (c *conn) Write(b []byte) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
        n, err := c.fd.Write(b)
        if err != nil {
                err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
        }
        return n, err
}

Close()

Close()方法用于关闭conn连接。实现代码中会优先调用 ok()方法。

func (c *conn) Close() error {
        if !c.ok() {
                return syscall.EINVAL
        }
        err := c.fd.Close()
        if err != nil {
                err = &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
        }
        return err
}


func (c *conn) ok() bool { return c != nil && c.fd != nil }

【一个简单的服务器实现】

工作流程:通过net.ListenTCP()生成一个mylistener对象,通过mylistener.Accept()方法接受客户端请求

handleRequest自定义函数中,预先申请[]byte类型的变量 mybuff,用于存放 从conn对象读取的数据。并通过Write()方法再写入到conn对象中。

package main

import (
	"fmt"
	"net"
	"os"
)

func main() {
	service := ":2001"
	tcpAddr, err := net.ResolveTCPAddr("tcp", service)
	checkError(err)

	mylistener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)

	for {
		conn, err := mylistener.Accept()
		if err != nil {
			continue
		}
		handleRequest(conn)
		conn.Close()

	}

}
func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error :", err.Error())
		os.Exit(1)
	}
}

func handleRequest(conn net.Conn) {
	var mybuff [512]byte
	for {
		n, err := conn.Read(mybuff[0:])
		if err != nil {
			return
		}
		fmt.Println(string(mybuff[0:]))
		fmt.Println("localaddr is:", conn.LocalAddr())
		fmt.Println("remoteaddr is:", conn.RemoteAddr())
		fmt.Println("##########")

		_, err2 := conn.Write(mybuff[0:n])
		if err2 != nil {
			return
		}
	}

}

func checckError(err error) {
	if err != nil {
		fmt.Println("Fatal err:", err.Error())
		os.Exit(1)
	}
}

客户端通过 telnet命令简单实现,输入内容后回车,会直接打印到屏幕。

c$telnet localhost 2001
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
the first server
the first server
second
second

服务端的显示如下

the first server

localaddr is: 127.0.0.1:2001
remoteaddr is: 127.0.0.1:50120
##########
second
t server

localaddr is: 127.0.0.1:2001
remoteaddr is: 127.0.0.1:50120
##########

从服务端的打印信息看, 有个“t server”的输出,其原因是 handleRequest函数中 定义的 mybuff是  [512]byte ,并通过mybuff[0:n]来写入数据。不同长度显示的会不一样。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是一个简单的 Linux 下使用 C 语言实现 socket 编程的服务器端示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8888 #define MAX_PENDING 5 #define MAX_LINE 2048 int main(int argc, char *argv[]) { int listen_fd, conn_fd; struct sockaddr_in servaddr; char buf[MAX_LINE]; int n; // 创建套接字 if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket error"); exit(1); } // 设置套接字地址 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); // 绑定套接字 if (bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { perror("bind error"); exit(1); } // 监听套接字 if (listen(listen_fd, MAX_PENDING) == -1) { perror("listen error"); exit(1); } printf("Server started.\n"); // 接受客户端连接并处理请求 while (1) { if ((conn_fd = accept(listen_fd, (struct sockaddr *)NULL, NULL)) == -1) { perror("accept error"); continue; } printf("Client connected.\n"); while ((n = read(conn_fd, buf, MAX_LINE)) > 0) { printf("Received: %s", buf); if (write(conn_fd, buf, n) == -1) { perror("write error"); exit(1); } } printf("Client disconnected.\n"); close(conn_fd); } close(listen_fd); return 0; } ``` 该示例代码中,服务器端创建了一个 TCP 套接字,绑定了一个固定的端口号,并监听客户端连接。服务器端会接受客户端的连接请求,并处理来自客户端的请求。当接受到客户端的请求后,服务器端会将请求内容原样返回给客户端,并在控制台输出接收到的请求内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值