修复一个open too many files

作者:
淡白
创建时间:
2021-09-09 18:00:03
Go socket

摘要:在使用socks5代理客户端时,发现很多连接没有被正常关闭的问题。问题出现在代码中的两个地方。首先,在client.go文件中,当代理类型不为http时,会执行安全信道建立的代码。其次,在cryptogram.go文件中的copy方法中,会进行加密io复制。当一方连接断开时,另一方连接可能不会被正常关闭,导致锁一直没有释放。本地连接也一直没有关闭,从而打开了太多连接并出现报错。为了解决这个问题,对代码进行了修改。首先,在cryptogram.go文件的copy方法中,增加了对连接的关闭操作。其次,在client.go文件中,将连接的关闭放在copy方法内部,当一方连接被关闭时,关闭两个连接,另一个拷贝就能感知到并读取错误并正常关闭并退出。这样就解决了连接没有正常关闭的问题。

在使用socks5代理客户端发现,很多连接没有被正常关闭。 在文件client.go中当代理类型不为http时会执行以下代码

	// 和远程端建立安全信道
	wg := new(sync.WaitGroup)
	wg.Add(2)
	// 本地的内容copy到远程端
	go func() {
		defer wg.Done()
		SecureCopy(localClient, dstServer, auth.Encrypt)
	}()

	// 远程得到的内容copy到源地址
	go func() {
		defer wg.Done()
		SecureCopy(dstServer, localClient, auth.Decrypt)
	}()
	wg.Wait()

cryptogram.go中的copy方法如下

// 加密io复制,可接收加密函数作为参数
func SecureCopy(src io.ReadWriteCloser, dst io.ReadWriteCloser, secure func(b []byte) error) (written int64, err error) {
	size := 1024
	buf := make([]byte, size)
	for {
		nr, er := src.Read(buf)
		secure(buf)
		if nr > 0 {
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != io.EOF {
				err = er
			}
			break
		}
	}
	return written, err

当有一方连接断开时,另一方链接可能不会被正常关闭导致锁一直没有释放。本地连接也一直没有关闭。打开了太多连接也就出现了报错。

修改后的代码: cryptogram.go

func SecureCopy(src io.ReadWriteCloser, dst io.ReadWriteCloser, secure func(b []byte) error) (written int64, err error) {
	defer func() {
		if src != nil {
			src.Close()
		}
		if dst != nil {
			dst.Close()
		}
	}()
	buf := make([]byte, 1024)
	for {
		nr, er := src.Read(buf)
		if er != nil {
			break
		}
		if nr > 0 {
			err = secure(buf)
			if err != nil {
				return written, err
			}
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			if nr != nw {
				err = io.ErrShortWrite
				break
			}
		}
	}
	return written, err
}

client.go 把连接的关闭放到copy内部 当一方连接被关闭则关闭两个连接,另一个拷贝就能感知从而读取错误正常关闭并退出。

	go SecureCopy(dstServer, localClient, auth.Decrypt)
	SecureCopy(localClient, dstServer, auth.Encrypt)