tcp socket 错误篇2 - golang

     先让我们看一段tcp 的socket代码:

     

        l, e := net.Listen("tcp", ":9090") //监听
	
	if e != nil {
		fmt.Println(e)
		return
	}
        defer l.Close()
	for {
		c, e := l.Accept()
		if e != nil {
			fmt.Println(e)
			c.Close()
			break
		}
		buf := make([]byte, 10)
		for {
			//t :=  make([]byte, 10)
			_, e := c.Read(buf)
                        //t=t[:n]
			//buf = append(buf,t...)
			if e == io.EOF {//对端关闭退出
				break
			}
                        fmt.Println("===")
		}
		fmt.Println(string(buf))
	}        

      这段代码是我们在用go做socket的时候经常能百度出的一种,基本上搜出来的和这个都大同小异。

     这段代码是一个服务端部分的代码,但在读写上无论在服务端还是客户端基本都是一致的。

     这段代码大致意思是,建立一个socket监听,同时循环读取客户端发来的消息。(因为我们并不知道客户端会发来多少数据

     首先让我们看第一个错误,也是一个忽视点。重点就在这句:

   

                        if e == io.EOF {//对端关闭退出
				break
			}

      这里说的很清楚,对端关闭后,才会EOF。那么问题来了,如果对端不关闭会怎么样呢?

      首先让我们看看不关闭会输出什么:

     

     可以看到他陷入了死循环永远也出不来了。那如何解决这个问题呢?

     其实很简单,read方法会返回读取的字节和错误,我们只需要判断当读取字节数为0就让为是一次读取完毕就可以了。代码如下:

   

                for {
			//t :=  make([]byte, 10)
			n, e := c.Read(buf)  //这里为修改的地方
                        //t=t[:n]
			//buf = append(buf,t...)
			if n==0 || e == io.EOF {//这里为修改的地方
				break
			}
                        fmt.Println("===")
		}
		fmt.Println(string(buf))

   修改成这样后再让我们看一下输出:

  

   我在客户端发送的是,我是 tcp socket 测试。但是我们看到接收的字符不全还有乱码。这是为什么呢?让我们该一句代码再看一下,这次我们把buf的长度输出一下:

    可以看到还是最初的10,并没有像我们想的一样进行扩容。这就是为什么我们会有乱码,因为我们3次数据都是在一个上面进行覆盖,并没有在后面继续写。大家也应该注意到了,我上面有2行注释的代码:

   

                buf := make([]byte, 0)
		for {
			//t :=  make([]byte, 10)
			_, e := c.Read(buf)
                        //t=t[:n]
			//buf = append(buf,t...)
			if e == io.EOF {//对端关闭退出
				break
			}
                        fmt.Println("===")
		}
		fmt.Println(string(buf))

         我们只需要把注释打开,同时每次读的数据放到t中即可。这就相当于我们用t作为一个缓存区,每次缓存的数据放到buf中,最终拼接为一次的数据,同时把buf的初始0个值。

最终代码为:

        l, e := net.Listen("tcp", ":9090")
	
	if e != nil {
		fmt.Println(e)
		return
	}
        defer l.Close()
	for {
		c, e := l.Accept()
		if e != nil {
			fmt.Println(e)
			c.Close()
			break
		}
		buf := make([]byte, 0)
		for {
			t :=  make([]byte, 10)
			n, e := c.Read(t)
                        t=t[:n]
			buf = append(buf,t...)
			if n == 0 || e == io.EOF {
				break
			}
			fmt.Println("===")
		}
		fmt.Println(string(buf),len(buf))
	}

输出结果为 :

但是这样就完了吗?上述的结果只是建立在,客户端发送一次数据后中断的情况下。其实服务端在read拿不到数据就是阻塞。等客户端异常中断后就会抛出wsarecv: An existing connection was forcibly closed by the remote host.

当我们客户端循环发送数据就会出现这种情况:

也就是你永远也拿不到0,你的数据会无限的拼到一个buf,

这样我就拿到了一个预期中的结果。下面让我们总结一下,这期注意说了3个问题:

1.当对端不关闭时,异常退出,服务端判断为e==io.EOF时会出现无法退出循环的问题。

2.放一个buf会出现数据被覆盖问题。解决方法是:建立一个临时的byte数组作为缓存区,然后统一放到buf中。同时要对缓存区做处理,因为最后一次取出的数据一般都放不满,会多很多0。

3..如果客户端一直保持连接,会出现一直阻塞不退出问题。在这点上可以设置超时时间,

注: 在使用过程中,e抛出的错误可能不是eof,如果客户端异常断开可能是wsarecv: An existing connection was forcibly closed by the remote host.

所以 e == io.EOF 最好换成 e != nil

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章