英文:
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 (
"bufio"
"bytes"
"fmt"
"io"
"log"
"net"
"os"
"strings"
)
const BUFFER_SIZE = 1024
func main() {
//get port and ip address to dial
if len(os.Args) != 3 {
fmt.Println("useage example: 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("There was an error making a connection")
}
//read from
reader := bufio.NewReader(os.Stdin)
fmt.Print("Please enter 'get <filename>' or 'send <filename>' to transfer files to the server\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("Bad Command")
}
}
func sendFileToServer(fileName string, connection net.Conn) {
var currentByte int64 = 0
fmt.Println("send to client")
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("-1"))
log.Fatal(err)
}
connection.Write([]byte("send " + 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("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("start listening")
server, error := net.Listen("tcp", "localhost:"+PORT)
if error != nil {
fmt.Println("There was an error starting the server" + error.Error())
return
}
//infinate loop
for {
connection, error := server.Accept()
if error != nil {
fmt.Println("There was am error with the connection" + error.Error())
return
}
fmt.Println("connected")
//handle the connection, on it'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("There is an error reading from connection", error.Error())
return
}
fmt.Println("command recieved: " + string(buffer))
//loop until disconntect
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("getting a file")
getFileFromClient(arrayOfCommands[1], connection)
} else {
_, error = connection.Write([]byte("bad command"))
}
}
func sendFileToClient(fileName string, connection net.Conn) {
var currentByte int64 = 0
fmt.Println("send to client")
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("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 //
答案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, "\x00")
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, "bytes sent")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论