1. 网络编程基本介绍

  Golang的主要设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端,程序必不可少也是至关重要的一部分。

网络编程有两种:

1)TCP socket编程,是网络编程的主流。之所以叫Tcp socket编程,是因为底层是基于Tcp/ip协议的,比如:QQ聊天【示意图】

2)b/s结构的http编程,我们使用浏览器去访问服务器时,使用的就是http协议,而http底层依旧是用tcp socket实现的。【示意图】  比如:京东商城【这属于 go web开发范畴】

2.协议(tcp/ip)

  TCP/IP(Transmission Control Protocol/Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是Internet最基本的协议、Internet国际互联网络的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的。

 3. OSI与Tcp/ip 参考模型(推荐tcp/ip协议3卷)

 4. ip地址

  概述:每个internet上的主机和路由器都有一个ip地址,它包括网络号和主机号,ip地址有ipv4(32位)或者ipv6(128位).可以通过ipconfig来查看

5. 端口(port)-介绍 

  我们这里所指的端口不是指物理意义上的端口,而是特指TCP/IP协议中的端口,是逻辑意义上的端口

  如果把IP地址比作一间房子,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口可以有65536个(即256 * 256)个之多。端口是通过端口号来标记的,端口号只有整数,范围是从0到65535(256 * 256 -1)

 6. 端口(port)-分类

0号是保留端口

1-1024是固定端口(程序员不要使用),又叫有名端口,即被某些程序固定使用,一般程序员不使用

22:SSH远程登录协议

23:telnet使用

21:ftp使用

25:smtp服务使用

80:iis使用

7:echo服务

1025——65535是动态端口,这些端口程序员可以使用

7.端口(port)-使用注意

1)在计算机(尤其是做服务器)要尽可能的少开端口

2)一个端口只能被一个程序监听

3)如果使用netstat  - an  可以查看本机有哪些端口在监听

4)可以使用netstat - anb  来查看监听端口的pid,在结合任务管理器关闭不安全的端口

8.tcp socket编程的客户端和服务器端

为了授课方便,我们将tcp socket编程,简称socket编程,下图为Golang socket编程中客户端和服务器的网络分布图:

 9. tcp socket 编程的快速入门——服务器的处理流程

1)监听端口8888

2)接收客户端的tcp链接,建立客户端和服务器端的链接

3)创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)

10.tcp socket 编程的快速入门——客户端的处理流程

1)建立与服务端的链接

2)发送请求数据【终端】,接收服务器端返回的结果数据

3)关闭链接

11. tcp socket 编程的快速入门——简单的程序示意图

12. 代码的实现 

程序框架图示意图

服务器端功能:

  编写一个服务器端程序,在8888端口监听

  可以和多个客户端创建链接

  链接成功后,客户端可以发送数据,服务器端接收数据,并显示在终端上

  先使用telnet来测试(可能电脑没有telnet服务器选项,暂没找到解决办法,建议直接使用客户端测试),然编写客户端程序来测试

服务器端代码:

package main
import (
	"fmt"
	"net" // 做网络socket开发时,net包含有我们需要所有的方法和函数
)
// 这里必须要有一个conn,否则啥也干不了
func process(conn net.Conn) {
	fmt.Println("执行了process")
	// 这里我们循环的接收客户端发送的数据
	defer conn.Close()
	for {
		// 每次创建一个新的切片
		// Read从连接中读取数据
    	// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
		buf := make([]byte, 1024)
		// 1. 等待客户端通过conn发送信息
		// 2. 如果客户端没有write[发送],那么协程就一直阻塞在这里,也有可能会超时
		// n代表读取的字节数
		// fmt.Printf("服务器在等待客户端%s 发送信息\n",conn.RemoteAddr().String())
		n, err := conn.Read(buf) // 从conn读取
		if err != nil {
			// 如果 err = io.EOF,则代表客户端退出
			// 如果等待出了问题,如:超时,客户端退出[链接断了]
			fmt.Println("服务器的Read err=",err)
			return
		}
		// 3. 显示客户端发送的内容到服务器的终端
		fmt.Println(string(buf[:n]))
	}
}
func main() {
	fmt.Println("服务器开始监听了...")
	// 127.0.0.1只能监听ipv4,0.0.0.0可以监听ipv4和ipv6
	// 1. tcp表示使用的网络协议是tcp
	// 2. 0.0.0.0:8888 表示在本地监听 8888端口
	listen, err := net.Listen("tcp", "127.0.0.1:8888")
	if err != nil {
		fmt.Printf("listen err=%v\n",err)
		return
	}
	// Close关闭该接口,并使任何阻塞的Accept操作都会不再阻塞并返回错误
	defer listen.Close()
	// 循环等待客户端来链接我
	for {
		// 等待客户端来链接我
		fmt.Println("等待客户端来链接")
		conn, err := listen.Accept()
		if err != nil {
			// 这个地方错了,不需要return
			// 因为A客户端错了,B客户端不一定错了
			fmt.Printf("Accept() err=%v\n",err)
		} else {
			fmt.Printf("Accept() suc conn=%v\n",conn)
			// RemoteAddr() Addr
			// Conn.RemoteAddr返回远端网络地址
			// Addr.String() 字符串格式的地址
			fmt.Printf("客户端的ip=%v\n",conn.RemoteAddr().String())
		}
		// 这里准备起一个协程,为客户端服务,这里不能写在主线程,使用协程,避免阻塞
		go process(conn)
	}
}

客户端功能:

1. 编写一个客户端程序,能链接到服务器端的8888端口

2. 客户端可以发送单行数据,然后就退出

3. 能通过终端输入数据(输入一行发送一行),并发送给服务器端[]

4. 在终端输入exit,表示退出程序

客户端代码:

package main
import (
	"fmt"
	"net"
	"bufio"
	"os"
	"strings"
)

func main() {
	// 1. 编写一个客户端程序,能链接到 服务器端的8888端口
	conn, err := net.Dial("tcp", "127.0.0.1:8888")
	if err != nil {
		fmt.Println("client dial err=",err)
		return
	}
	fmt.Println("connect success",conn)
	// 功能1: 客户端可以发送单行数据,然后就退出
	// Stdin、Stdout和Stderr是指向标准输入、标准输出、标准错误输出的文件描述符。
	// 所有的socket都会落在一个文件上
	reader := bufio.NewReader(os.Stdin) // os.Stdin 代表标准输入【终端】
	for {
		// 从终端读取一行用户输入,并准备发送给服务器
		line, err := reader.ReadString('\n')
		// 如果用户输入的是exit就退出
		line = strings.Trim(line," \r\n") // 去除空格换行
		if line == "exit" {
			fmt.Println("客户端退出...")
			break
		}
		// 再将line  发送给 服务器
		// n代表返回的字节
		_, err = conn.Write([]byte(line))
		if err != nil {
			fmt.Println("conn.Write err=",err)
		}
	}
}

声明:文章是个人做笔记用的,不喜勿喷....