在Go语言中进行TCP客户端/服务器文件传输

huangapple go评论85阅读模式
英文:

TCP client / server file transfer in Go

问题

我是你的中文翻译助手,以下是你要翻译的内容:

我刚开始学习Go语言,我在调试这个客户端/服务器文件传输代码时遇到了问题。当我从服务器请求一个719kb的png文件时,我得到了719kb的文件,但是打开时并没有完全显示(有一部分被截断了)。我在哪里出错了?

// CLIENT ///
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
)

const BUFFER_SIZE = 1024

func main() {

	//获取要拨号的端口和IP地址

	if len(os.Args) != 3 {
		fmt.Println("用法示例:tcpClient 127.0.0.1 7005")
		return
	}

	var ip string = os.Args[1]
	var port string = os.Args[2]

	connection, err := net.Dial("tcp", ip+":"+port)
	if err != nil {
		fmt.Println("建立连接时出错")
	}

	//从标准输入读取
	reader := bufio.NewReader(os.Stdin)
	fmt.Print("请输入'get <filename>'或'send <filename>'以将文件传输到服务器\n\n")
	inputFromUser, _ := reader.ReadString('\n')
	arrayOfCommands := strings.Split(inputFromUser, " ")

	if arrayOfCommands[0] == "get" {
		getFileFromServer(arrayOfCommands[1], connection)

	} else if arrayOfCommands[0] == "send" {
		sendFileToServer(arrayOfCommands[1], connection)
	} else {
		fmt.Println("错误的命令")
	}

}

func sendFileToServer(fileName string, connection net.Conn) {

	var currentByte int64 = 0
	fmt.Println("发送到客户端")
	fileBuffer := make([]byte, BUFFER_SIZE)

	var err error

	//要读取的文件
	file, err := os.Open(strings.TrimSpace(fileName)) // 仅限读取访问权限。
	if err != nil {
		connection.Write([]byte("-1"))
		log.Fatal(err)
	}
	connection.Write([]byte("send " + fileName))
	//读取文件直到出现错误
	for err == nil || err != io.EOF {

		_, err = file.ReadAt(fileBuffer, currentByte)
		currentByte += BUFFER_SIZE
		fmt.Println(fileBuffer)
		connection.Write(fileBuffer)
	}

	file.Close()
	connection.Close()

}

func getFileFromServer(fileName string, connection net.Conn) {

	var currentByte int64 = 0

	fileBuffer := make([]byte, BUFFER_SIZE)

	var err error
	file, err := os.Create(strings.TrimSpace(fileName))
	if err != nil {
		log.Fatal(err)
	}
	connection.Write([]byte("get " + fileName))
	for {

		connection.Read(fileBuffer)
		cleanedFileBuffer := bytes.Trim(fileBuffer, "\x00")

		_, err = file.WriteAt(cleanedFileBuffer, currentByte)

		currentByte += BUFFER_SIZE

		if err == io.EOF {
			break
		}

	}

	file.Close()
	return

}

// END CLIENT //
// SERVER //
package main

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strings"
)

const BUFFER_SIZE = 1024
const PORT = "7005"

func main() {

	fmt.Println("开始监听")

	server, error := net.Listen("tcp", "localhost:"+PORT)
	if error != nil {
		fmt.Println("启动服务器时出错" + error.Error())
		return
	}

	//无限循环
	for {

		connection, error := server.Accept()
		if error != nil {
			fmt.Println("连接出错" + error.Error())
			return
		}
		fmt.Println("已连接")
		//处理连接,每个连接在自己的线程上处理
		go connectionHandler(connection)
	}
}

func connectionHandler(connection net.Conn) {
	buffer := make([]byte, BUFFER_SIZE)

	_, error := connection.Read(buffer)
	if error != nil {
		fmt.Println("从连接中读取时出错", error.Error())
		return
	}
	fmt.Println("接收到命令:" + string(buffer))

	//循环直到断开连接

	cleanedBuffer := bytes.Trim(buffer, "\x00")
	cleanedInputCommandString := strings.TrimSpace(string(cleanedBuffer))
	arrayOfCommands := strings.Split(cleanedInputCommandString, " ")

	fmt.Println(arrayOfCommands[0])
	if arrayOfCommands[0] == "get" {
		sendFileToClient(arrayOfCommands[1], connection)
	} else if arrayOfCommands[0] == "send" {
		fmt.Println("获取文件")

		getFileFromClient(arrayOfCommands[1], connection)

	} else {
		_, error = connection.Write([]byte("错误的命令"))
	}

}

func sendFileToClient(fileName string, connection net.Conn) {
	var currentByte int64 = 0
	fmt.Println("发送到客户端")
	fileBuffer := make([]byte, BUFFER_SIZE)

	//要读取的文件
	file, err := os.Open(strings.TrimSpace(fileName)) // 仅限读取访问权限。
	if err != nil {

		log.Fatal(err)
	}
	var err2 error

	//读取文件直到出现错误
	for {

		_, err2 = file.ReadAt(fileBuffer, currentByte)
		currentByte += BUFFER_SIZE
		fmt.Println(fileBuffer)
		connection.Write(fileBuffer)

		if err2 == io.EOF {
			break
		}
	}

	file.Close()
	return

}

func getFileFromClient(fileName string, connection net.Conn) {

	var currentByte int64 = 0

	fileBuffer := make([]byte, BUFFER_SIZE)

	var err error
	file, err := os.Create(strings.TrimSpace(fileName))
	if err != nil {
		log.Fatal(err)
	}
	connection.Write([]byte("get " + fileName))
	for err == nil || err != io.EOF {

		connection.Read(fileBuffer)

		cleanedFileBuffer := bytes.Trim(fileBuffer, "\x00")

		_, err = file.WriteAt(cleanedFileBuffer, currentByte)
		if len(string(fileBuffer)) != len(string(cleanedFileBuffer)) {
			break
		}
		currentByte += BUFFER_SIZE

	}

	connection.Close()
	file.Close()
	return

}

// END SERVER //

希望对你有帮助!如果还有其他问题,请随时提问。

英文:

I'm new to Go and I'm having troubles debugging this client / server file transfer code. When I request a 719kb png file from the server, I get the 719kb.. but not perfectly, the png when I open it isn't completely displayed (some is cut off. Where am I going wrong here?

// CLIENT ///
package main
import (
&quot;bufio&quot;
&quot;bytes&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
&quot;net&quot;
&quot;os&quot;
&quot;strings&quot;
)
const BUFFER_SIZE = 1024
func main() {
//get port and ip address to dial
if len(os.Args) != 3 {
fmt.Println(&quot;useage example: tcpClient 127.0.0.1 7005&quot;)
return
}
var ip string = os.Args[1]
var port string = os.Args[2]
connection, err := net.Dial(&quot;tcp&quot;, ip+&quot;:&quot;+port)
if err != nil {
fmt.Println(&quot;There was an error making a connection&quot;)
}
//read from
reader := bufio.NewReader(os.Stdin)
fmt.Print(&quot;Please enter &#39;get &lt;filename&gt;&#39; or &#39;send &lt;filename&gt;&#39; to transfer files to the server\n\n&quot;)
inputFromUser, _ := reader.ReadString(&#39;\n&#39;)
arrayOfCommands := strings.Split(inputFromUser, &quot; &quot;)
if arrayOfCommands[0] == &quot;get&quot; {
getFileFromServer(arrayOfCommands[1], connection)
} else if arrayOfCommands[0] == &quot;send&quot; {
sendFileToServer(arrayOfCommands[1], connection)
} else {
fmt.Println(&quot;Bad Command&quot;)
}
}
func sendFileToServer(fileName string, connection net.Conn) {
var currentByte int64 = 0
fmt.Println(&quot;send to client&quot;)
fileBuffer := make([]byte, BUFFER_SIZE)
var err error
//file to read
file, err := os.Open(strings.TrimSpace(fileName)) // For read access.
if err != nil {
connection.Write([]byte(&quot;-1&quot;))
log.Fatal(err)
}
connection.Write([]byte(&quot;send &quot; + fileName))
//read file until there is an error
for err == nil || err != io.EOF {
_, err = file.ReadAt(fileBuffer, currentByte)
currentByte += BUFFER_SIZE
fmt.Println(fileBuffer)
connection.Write(fileBuffer)
}
file.Close()
connection.Close()
}
func getFileFromServer(fileName string, connection net.Conn) {
var currentByte int64 = 0
fileBuffer := make([]byte, BUFFER_SIZE)
var err error
file, err := os.Create(strings.TrimSpace(fileName))
if err != nil {
log.Fatal(err)
}
connection.Write([]byte(&quot;get &quot; + fileName))
for {
connection.Read(fileBuffer)
cleanedFileBuffer := bytes.Trim(fileBuffer, &quot;\x00&quot;)
_, err = file.WriteAt(cleanedFileBuffer, currentByte)
currentByte += BUFFER_SIZE
if err == io.EOF {
break
}
}
file.Close()
return
}
// END CLIENT //
// SERVER //
package main
import (
&quot;bytes&quot;
&quot;fmt&quot;
&quot;io&quot;
&quot;log&quot;
&quot;net&quot;
&quot;os&quot;
&quot;strings&quot;
)
const BUFFER_SIZE = 1024
const PORT = &quot;7005&quot;
func main() {
fmt.Println(&quot;start listening&quot;)
server, error := net.Listen(&quot;tcp&quot;, &quot;localhost:&quot;+PORT)
if error != nil {
fmt.Println(&quot;There was an error starting the server&quot; + error.Error())
return
}
//infinate loop
for {
connection, error := server.Accept()
if error != nil {
fmt.Println(&quot;There was am error with the connection&quot; + error.Error())
return
}
fmt.Println(&quot;connected&quot;)
//handle the connection, on it&#39;s own thread, per connection
go connectionHandler(connection)
}
}
func connectionHandler(connection net.Conn) {
buffer := make([]byte, BUFFER_SIZE)
_, error := connection.Read(buffer)
if error != nil {
fmt.Println(&quot;There is an error reading from connection&quot;, error.Error())
return
}
fmt.Println(&quot;command recieved: &quot; + string(buffer))
//loop until disconntect
cleanedBuffer := bytes.Trim(buffer, &quot;\x00&quot;)
cleanedInputCommandString := strings.TrimSpace(string(cleanedBuffer))
arrayOfCommands := strings.Split(cleanedInputCommandString, &quot; &quot;)
fmt.Println(arrayOfCommands[0])
if arrayOfCommands[0] == &quot;get&quot; {
sendFileToClient(arrayOfCommands[1], connection)
} else if arrayOfCommands[0] == &quot;send&quot; {
fmt.Println(&quot;getting a file&quot;)
getFileFromClient(arrayOfCommands[1], connection)
} else {
_, error = connection.Write([]byte(&quot;bad command&quot;))
}
}
func sendFileToClient(fileName string, connection net.Conn) {
var currentByte int64 = 0
fmt.Println(&quot;send to client&quot;)
fileBuffer := make([]byte, BUFFER_SIZE)
//file to read
file, err := os.Open(strings.TrimSpace(fileName)) // For read access.
if err != nil {
log.Fatal(err)
}
var err2 error
//read file until there is an error
for {
_, err2 = file.ReadAt(fileBuffer, currentByte)
currentByte += BUFFER_SIZE
fmt.Println(fileBuffer)
connection.Write(fileBuffer)
if err2 == io.EOF {
break
}
}
file.Close()
return
}
func getFileFromClient(fileName string, connection net.Conn) {
var currentByte int64 = 0
fileBuffer := make([]byte, BUFFER_SIZE)
var err error
file, err := os.Create(strings.TrimSpace(fileName))
if err != nil {
log.Fatal(err)
}
connection.Write([]byte(&quot;get &quot; + fileName))
for err == nil || err != io.EOF {
connection.Read(fileBuffer)
cleanedFileBuffer := bytes.Trim(fileBuffer, &quot;\x00&quot;)
_, err = file.WriteAt(cleanedFileBuffer, currentByte)
if len(string(fileBuffer)) != len(string(cleanedFileBuffer)) {
break
}
currentByte += BUFFER_SIZE
}
connection.Close()
file.Close()
return
}
// END SERVER //

答案1

得分: 10

你需要考虑从ReadAt返回的字节数,否则你发送的最后一个fileBuffer将包含额外的垃圾字节。

示例:

n, err := file.ReadAt(fileBuffer, currentByte)
connection.Write(fileBuffer[:n])

另外,bytes.Trim(fileBuffer, "\x00")会破坏几乎所有的二进制文件,因为通常它们使用空字节来填充空间。

此外,正确的做法只需使用io.Copy

file, err := os.Open(strings.TrimSpace(fileName)) // 用于读取访问权限。
if err != nil {
log.Fatal(err)
}
defer file.Close() // 确保即使发生 panic,也要关闭文件。
n, err = io.Copy(connection, file)
if err != nil {
log.Fatal(err)
}
fmt.Println(n, "字节已发送")
英文:

You need to account for the number of bytes returned from ReadAt, otherwise the last fileBuffer you send will have extra garbage bytes.

Example:

n, err := file.ReadAt(fileBuffer, currentByte)
connection.Write(fileBuffer[:n])

Also bytes.Trim(fileBuffer, &quot;\x00&quot;) will destroy almost any binary file since usually they use null bytes to fill space.

Also the proper way of doing this is just using io.Copy:

file, err := os.Open(strings.TrimSpace(fileName)) // For read access.
if err != nil {
log.Fatal(err)
}
defer file.Close() // make sure to close the file even if we panic.
n, err = io.Copy(connection, file)
if err != nil {
log.Fatal(err)
}
fmt.Println(n, &quot;bytes sent&quot;)

huangapple
  • 本文由 发表于 2014年9月25日 12:37:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/26030627.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定