func ClientMode(mode TransferMode) ClientOpt {
	return func(c *Client) error {
		if mode != ModeNetASCII && mode != ModeOctet {
			return ErrInvalidMode
		}
		c.mode = mode
		return nil
	}
}
ClientMode configures the mode.

Valid options are ModeNetASCII and ModeOctet. Default is ModeNetASCII.
package tftp  
import (
	"fmt"
	"io"
	"net"
	"net/url"
	"strconv"
	"strings"
)
type Client struct {
	log  *logger
	net  string             
	mode TransferMode       
	opts map[string]string  
	retransmit int  
}
func NewClient(opts ...ClientOpt) (*Client, error) {
	options := map[string]string{}
	for k, v := range defaultOptions {
		options[k] = v
	}
	c := &Client{
		log:        newLogger("client"),
		net:        defaultUDPNet,
		opts:       options,
		mode:       defaultMode,
		retransmit: defaultRetransmit,
	}
	for _, opt := range opts {
		if err := opt(c); err != nil {
			return c, err
		}
	}
	return c, nil
}
func (c *Client) Get(url string) (*Response, error) {
	u, err := parseURL(url)
	if err != nil {
		return nil, err
	}
	conn, err := newConnFromHost(c.net, c.mode, u.host)
	if err != nil {
		return nil, err
	}
	conn.retransmit = c.retransmit
	if err := conn.sendReadRequest(u.file, c.opts); err != nil {
		return nil, err
	}
	return &Response{conn: conn}, nil
}
func (c *Client) Put(url string, r io.Reader, size int64) (err error) {
	u, err := parseURL(url)
	if err != nil {
		return err
	}
	conn, err := newConnFromHost(c.net, c.mode, u.host)
	if err != nil {
		return err
	}
	defer func() {
		cErr := conn.Close()
		if err == nil {
			err = cErr
		}
	}()
	conn.retransmit = c.retransmit
	if _, ok := c.opts[optTransferSize]; ok {
		if size < 1 {
			delete(c.opts, optTransferSize)
		} else {
			c.opts[optTransferSize] = fmt.Sprint(size)
		}
	}
	if err := conn.sendWriteRequest(u.file, c.opts); err != nil {
		return err
	}
	_, err = io.Copy(conn, r)
	return err
}
type parsedURL struct {
	host string
	file string
}
func parseURL(tftpURL string) (*parsedURL, error) {
	if tftpURL == "" {
		return nil, ErrInvalidURL
	}
	const kTftpPrefix = "tftp://"
	if !strings.HasPrefix(tftpURL, kTftpPrefix) {
		tftpURL = kTftpPrefix + tftpURL
	}
	u, err := url.Parse(tftpURL)
	if err != nil {
		return nil, err
	}
	file := u.RequestURI()
	if u.Fragment != "" {
		file = file + "#" + u.Fragment
	}
	p := &parsedURL{
		host: u.Hostname(),
		file: strings.TrimPrefix(file, "/"),
	}
	if p.host == "" {
		return nil, ErrInvalidHostIP
	}
	if isNumeric(p.host) {
		return nil, ErrInvalidHostIP
	}
	if p.file == "" {
		return nil, ErrInvalidFile
	}
	port := u.Port()
	if port == "" {
		port = defaultPort
	}
	if !isNumeric(port) {
		return nil, ErrInvalidHostIP
	}
	p.host = net.JoinHostPort(p.host, port)
	return p, nil
}
func isNumeric(s string) bool {
	_, err := strconv.Atoi(s)
	return err == nil
}
type Response struct {
	conn *conn
}
func (r *Response) Size() (int64, error) {
	if r.conn.tsize == nil {
		return 0, ErrSizeNotReceived
	}
	return *r.conn.tsize, nil
}
func (r *Response) Read(p []byte) (int, error) {
	return r.conn.Read(p)
}
type ClientOpt func(*Client) error