github.com/wangyougui/gf/v2@v2.6.5/net/gudp/gudp_conn.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gudp
     8  
     9  import (
    10  	"io"
    11  	"net"
    12  	"time"
    13  
    14  	"github.com/wangyougui/gf/v2/errors/gerror"
    15  )
    16  
    17  // Conn handles the UDP connection.
    18  type Conn struct {
    19  	*net.UDPConn                 // Underlying UDP connection.
    20  	remoteAddr     *net.UDPAddr  // Remote address.
    21  	deadlineRecv   time.Time     // Timeout point for reading data.
    22  	deadlineSend   time.Time     // Timeout point for writing data.
    23  	bufferWaitRecv time.Duration // Interval duration for reading buffer.
    24  }
    25  
    26  const (
    27  	defaultRetryInterval  = 100 * time.Millisecond // Retry interval.
    28  	defaultReadBufferSize = 1024                   // (Byte)Buffer size.
    29  	receiveAllWaitTimeout = time.Millisecond       // Default interval for reading buffer.
    30  )
    31  
    32  type Retry struct {
    33  	Count    int           // Max retry count.
    34  	Interval time.Duration // Retry interval.
    35  }
    36  
    37  // NewConn creates UDP connection to `remoteAddress`.
    38  // The optional parameter `localAddress` specifies the local address for connection.
    39  func NewConn(remoteAddress string, localAddress ...string) (*Conn, error) {
    40  	if conn, err := NewNetConn(remoteAddress, localAddress...); err == nil {
    41  		return NewConnByNetConn(conn), nil
    42  	} else {
    43  		return nil, err
    44  	}
    45  }
    46  
    47  // NewConnByNetConn creates an UDP connection object with given *net.UDPConn object.
    48  func NewConnByNetConn(udp *net.UDPConn) *Conn {
    49  	return &Conn{
    50  		UDPConn:        udp,
    51  		deadlineRecv:   time.Time{},
    52  		deadlineSend:   time.Time{},
    53  		bufferWaitRecv: receiveAllWaitTimeout,
    54  	}
    55  }
    56  
    57  // Send writes data to remote address.
    58  func (c *Conn) Send(data []byte, retry ...Retry) (err error) {
    59  	for {
    60  		if c.remoteAddr != nil {
    61  			_, err = c.WriteToUDP(data, c.remoteAddr)
    62  		} else {
    63  			_, err = c.Write(data)
    64  		}
    65  		if err != nil {
    66  			// Connection closed.
    67  			if err == io.EOF {
    68  				return err
    69  			}
    70  			// Still failed even after retrying.
    71  			if len(retry) == 0 || retry[0].Count == 0 {
    72  				err = gerror.Wrap(err, `Write data failed`)
    73  				return err
    74  			}
    75  			if len(retry) > 0 {
    76  				retry[0].Count--
    77  				if retry[0].Interval == 0 {
    78  					retry[0].Interval = defaultRetryInterval
    79  				}
    80  				time.Sleep(retry[0].Interval)
    81  			}
    82  		} else {
    83  			return nil
    84  		}
    85  	}
    86  }
    87  
    88  // Recv receives and returns data from remote address.
    89  // The parameter `buffer` is used for customizing the receiving buffer size. If `buffer` <= 0,
    90  // it uses the default buffer size, which is 1024 byte.
    91  //
    92  // There's package border in UDP protocol, we can receive a complete package if specified
    93  // buffer size is big enough. VERY NOTE that we should receive the complete package in once
    94  // or else the leftover package data would be dropped.
    95  func (c *Conn) Recv(buffer int, retry ...Retry) ([]byte, error) {
    96  	var (
    97  		err        error        // Reading error
    98  		size       int          // Reading size
    99  		data       []byte       // Buffer object
   100  		remoteAddr *net.UDPAddr // Current remote address for reading
   101  	)
   102  	if buffer > 0 {
   103  		data = make([]byte, buffer)
   104  	} else {
   105  		data = make([]byte, defaultReadBufferSize)
   106  	}
   107  	for {
   108  		size, remoteAddr, err = c.ReadFromUDP(data)
   109  		if err == nil {
   110  			c.remoteAddr = remoteAddr
   111  		}
   112  		if err != nil {
   113  			// Connection closed.
   114  			if err == io.EOF {
   115  				break
   116  			}
   117  			if len(retry) > 0 {
   118  				// It fails even it retried.
   119  				if retry[0].Count == 0 {
   120  					break
   121  				}
   122  				retry[0].Count--
   123  				if retry[0].Interval == 0 {
   124  					retry[0].Interval = defaultRetryInterval
   125  				}
   126  				time.Sleep(retry[0].Interval)
   127  				continue
   128  			}
   129  			err = gerror.Wrap(err, `ReadFromUDP failed`)
   130  			break
   131  		}
   132  		break
   133  	}
   134  	return data[:size], err
   135  }
   136  
   137  // SendRecv writes data to connection and blocks reading response.
   138  func (c *Conn) SendRecv(data []byte, receive int, retry ...Retry) ([]byte, error) {
   139  	if err := c.Send(data, retry...); err != nil {
   140  		return nil, err
   141  	}
   142  	return c.Recv(receive, retry...)
   143  }
   144  
   145  // RecvWithTimeout reads data from remote address with timeout.
   146  func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry) (data []byte, err error) {
   147  	if err = c.SetDeadlineRecv(time.Now().Add(timeout)); err != nil {
   148  		return nil, err
   149  	}
   150  	defer func() {
   151  		_ = c.SetDeadlineRecv(time.Time{})
   152  	}()
   153  	data, err = c.Recv(length, retry...)
   154  	return
   155  }
   156  
   157  // SendWithTimeout writes data to connection with timeout.
   158  func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retry) (err error) {
   159  	if err = c.SetDeadlineSend(time.Now().Add(timeout)); err != nil {
   160  		return err
   161  	}
   162  	defer func() {
   163  		_ = c.SetDeadlineSend(time.Time{})
   164  	}()
   165  	err = c.Send(data, retry...)
   166  	return
   167  }
   168  
   169  // SendRecvWithTimeout writes data to connection and reads response with timeout.
   170  func (c *Conn) SendRecvWithTimeout(data []byte, receive int, timeout time.Duration, retry ...Retry) ([]byte, error) {
   171  	if err := c.Send(data, retry...); err != nil {
   172  		return nil, err
   173  	}
   174  	return c.RecvWithTimeout(receive, timeout, retry...)
   175  }
   176  
   177  // SetDeadline sets the read and write deadlines associated with the connection.
   178  func (c *Conn) SetDeadline(t time.Time) (err error) {
   179  	if err = c.UDPConn.SetDeadline(t); err == nil {
   180  		c.deadlineRecv = t
   181  		c.deadlineSend = t
   182  	} else {
   183  		err = gerror.Wrapf(err, `SetDeadline for connection failed with "%s"`, t)
   184  	}
   185  	return err
   186  }
   187  
   188  // SetDeadlineRecv sets the read deadline associated with the connection.
   189  func (c *Conn) SetDeadlineRecv(t time.Time) (err error) {
   190  	if err = c.SetReadDeadline(t); err == nil {
   191  		c.deadlineRecv = t
   192  	} else {
   193  		err = gerror.Wrapf(err, `SetDeadlineRecv for connection failed with "%s"`, t)
   194  	}
   195  	return err
   196  }
   197  
   198  // SetDeadlineSend sets the deadline of sending for current connection.
   199  func (c *Conn) SetDeadlineSend(t time.Time) (err error) {
   200  	if err = c.SetWriteDeadline(t); err == nil {
   201  		c.deadlineSend = t
   202  	} else {
   203  		err = gerror.Wrapf(err, `SetDeadlineSend for connection failed with "%s"`, t)
   204  	}
   205  	return err
   206  }
   207  
   208  // SetBufferWaitRecv sets the buffer waiting timeout when reading all data from connection.
   209  // The waiting duration cannot be too long which might delay receiving data from remote address.
   210  func (c *Conn) SetBufferWaitRecv(d time.Duration) {
   211  	c.bufferWaitRecv = d
   212  }
   213  
   214  // RemoteAddr returns the remote address of current UDP connection.
   215  // Note that it cannot use c.conn.RemoteAddr() as it is nil.
   216  func (c *Conn) RemoteAddr() net.Addr {
   217  	return c.remoteAddr
   218  }