5.3 سرور tcp پیشرفته

5.3 سرور tcp پیشرفته

در این قسمت به طراحی و ساخت یک سرور tcp بصورت پیشرفته میپردازیم. در قسمت قبل بعد از ایجاد سرور TCP ساده با استفاده از کلاینت telnet به آن متصل شدیم و دیتایی را انتقال دادیم.

در این قسمت یک سرور tcp را با هدف انتقال فایل های حجیم بصورت جریان ایجاد میکنم.

قبل از شروع ایجاد سرور خود به دلیل اینکه چرا فایل های حجیم را استریم میکنیم و یا اصلا استریم چیست میپردازیم. زمانی که شما فایل های کم حجم را مستقیما انتقال میدهید با تاخیر کم و بصورت مطلوب انجام میشود. اما روایت برای فایل های سنگین تر متفاوت است، اگر این عمل بصورت مستقیم و یکجا انجام شود باعث ایجاد تاخیر و مصرف منابع بیش از حد روی سرور میشود. اما ما با استفاده از روش استریم، داده و فایل خود را بصورت قطعه های کم حجم و پشت سر هم ارسال میکنیم.

در ادامه به پیاده سازی سرور خود با استفاده از پروتکل tcp میپردازیم:

package main

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
	"time"
)


type FileServer struct { }


func (fs *FileServer) start() {
	ln, err := net.Listen("tcp", "0.0.0.0:3000")
	if err != nil {
		panic(err)
	}

	for {
		conn, err := ln.Accept()

		if err != nil {
			log.Fatal(err)
			continue
		}

		fmt.Printf("new connection: %s\n", conn.RemoteAddr().String())

		// read data from accepted connections
		go fs.readLoop(conn)
	}
}

func (fs *FileServer) readLoop(conn net.Conn) {

	// make a new buffer
	buf := new(bytes.Buffer)

	for {

		var size int64

		// get the size from connection
		binary.Read(conn, binary.LittleEndian, &size)

		// copy from connection until the end of file
		n, err := io.CopyN(buf, conn, size)
		if err != nil {
			log.Fatal(err)
			continue
		}

		fmt.Println(buf.Bytes())
		fmt.Printf("received %d bytes over the network\n", n)
	}
}

func main() {

	go func ()  {

		time.Sleep(4 * time.Second)

    // set your file szie
		sendFile(2000000)
	}()

	s := &FileServer{}
	s.start()
}

// client example that send a large file to server!
func sendFile(size int) error {

	file := make([]byte, size)

	// make a random file from the size provided
	_, err := io.ReadFull(rand.Reader, file)
	if err != nil {
		return err
	}

	// dial with the tcp server (you can do this is an other file)
	conn, err := net.Dial("tcp", ":3000")
	if err != nil {
		return err
	}

	// send the size of file
	binary.Write(conn, binary.LittleEndian, int64(size))

	// copy file over the network until the end of file
	n, err := io.CopyN(conn, bytes.NewReader(file), int64(size))
	if err != nil {
		return err
	}

	fmt.Printf("written %d byte over the network\n", n)
	return nil
}

بعد از پایان نوشتن کد شما میتوانید با اجرا کردن کد خود (ترجیحا انتخاب یک عدد بزرگ برای حجم فایل یا همان ورودی تابع sendFile) میتوانید استریم شدن بایت هارا بصورت چانک چانک در لاگ های سمت سرور ببینید.