1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
package pactor
import (
"fmt"
"io"
"net"
"runtime"
"strconv"
"time"
"github.com/la5nta/wl2k-go/transport"
)
// DialURL dials pactor:// URLs
//
// BLOCK until connection is established or timeoud occured
func (p *Modem) DialURL(url *transport.URL) (net.Conn, error) {
if url.Scheme != "pactor" {
return nil, transport.ErrUnsupportedScheme
}
if err := p.call(url.Target); err != nil {
return nil, err
}
return p, nil
}
// Read bytes from the Software receive buffer. The receive thread takes care of
// reading them from the pactor modem
//
// BLOCK until receive buffer has any data!
func (p *Modem) Read(d []byte) (int, error) {
p.mux.read.Lock()
defer p.mux.read.Unlock()
if p.state != Connected {
return 0, fmt.Errorf("Read from closed connection")
}
if len(d) == 0 {
return 0, nil
}
data, ok := <-p.recvBuf
if !ok {
return 0, io.EOF
}
if len(data) > len(d) {
panic("too large") //TODO: Handle
}
for i, b := range data {
d[i] = byte(b)
}
return len(data), nil
}
// Write bytes to the software send buffer. The send thread will take care of
// fowarding them to the pactor modem
//
// BLOCK if send buffer is full! Remains as soon as there is space left in the
// send buffer
func (p *Modem) Write(d []byte) (int, error) {
p.mux.write.Lock()
defer p.mux.write.Unlock()
if p.state != Connected {
return 0, fmt.Errorf("Read from closed connection")
}
for _, b := range d {
select {
case <-p.flags.closeWriting:
return 0, fmt.Errorf("Writing on closed connection")
case p.sendBuf <- b:
}
p.mux.bufLen.Lock()
p.sendBufLen++
p.mux.bufLen.Unlock()
}
return len(d), nil
}
// Flush waits for the last frames to be transmitted.
//
// Will throw error if remaining frames could not bet sent within 120s
func (p *Modem) Flush() (err error) {
if p.state != Connected {
return fmt.Errorf("Flush a closed connection")
}
writeDebug("Flush called", 2)
if err = p.waitTransmissionFinish(120 * time.Second); err != nil {
writeDebug(err.Error(), 2)
}
return
}
// Close closes the current connection.
//
// Will abort ("dirty disconnect") after 60 seconds if normal "disconnect" have
// not succeeded yet.
func (p *Modem) Close() error {
if p.flags.closed {
return nil
}
p.mux.close.Lock()
_, file, no, ok := runtime.Caller(1)
if ok {
writeDebug("Close called from "+file+"#"+strconv.Itoa(no), 2)
} else {
writeDebug("Close called", 2)
}
if p.flags.closeCalled != true {
defer p.mux.close.Unlock()
p.flags.closeCalled = true
if p.state == Connected {
// Connected to remote, try to send remaining frames and disconnect
// gracefully
defer p.close()
// Wait for remaining data to be transmitted and acknowledged
if err := p.waitTransmissionFinish(90 * time.Second); err != nil {
writeDebug(err.Error(), 2)
}
p.disconnect()
// Wait for disconnect command to be transmitted and acknowledged
if err := p.waitTransmissionFinish(30 * time.Second); err != nil {
writeDebug(err.Error(), 2)
}
} else {
// Link Setup (connection) not yet successful, force disconnect
p.forceDisconnect()
}
// Wait for the modem to change state from connected to disconnected
select {
case <-p.flags.disconnected:
writeDebug("Disconnect successful", 1)
return nil
case <-time.After(60 * time.Second):
p.forceDisconnect()
return fmt.Errorf("Disconnect timed out")
}
}
writeDebug("Should never reach this...", 1)
return nil
}
// TxBufferLen returns the number of bytes in the out buffer queue.
//
func (p *Modem) TxBufferLen() int {
p.mux.bufLen.Lock()
defer p.mux.bufLen.Unlock()
writeDebug("TxBufferLen called ("+strconv.Itoa(p.sendBufLen)+" bytes remaining in buffer)", 2)
return p.sendBufLen + (p.getNumFramesNotTransmitted() * MaxSendData)
}
|