I've just written a ping application in Go. I'm proud of the logic, but I want some help to refactor it. The program has too much code in main, and pulling functions out of it requires a lot of variables (see cleanUpAndExit).package mainimport (    fmt    golang.org/x/net/icmp    golang.org/x/net/internal/iana    golang.org/x/net/ipv4    math    net    os    os/signal    runtime    time)const (    ICMP_RESPONSE_BUFFER_BYTES = 300    // EstimatedRTT and DevRTT constants    ALPHA = 0.125    BETA  = 0.25    // How many pings we want to be able to store    TIMES_ARRAY_INITIAL_CAPACITY = 100    // Bytes where the sequence number in the ICMP echo headers begins and ends    SEQ_BEGIN = 2    SEQ_END   = 3    SEND_INTERVAL = 2 * time.Second    // Initial estimates for timeouts    STATIC_TIMEOUT_INTERVAL          = 2 * time.Second    INITIAL_ESTIMATED_RTT            = 1.0 * time.Second    INITIAL_DEV_RTT                  = 0.0 * time.Millisecond    INITIAL_DYNAMIC_TIMEOUT_INTERVAL = 1.0 * time.Second    // Miscellaneous    BITS_IN_BYTE      = 8    HEADER_SIZE_BYTES = 8)type NoIPv4Error struct {    message string}// Error implements the error interface and thereby allows NoIPv4Error to be// passed as an error type through Go's polymorphism system.func (err NoIPv4Error) Error() string {    return err.message}// makeEchoRequest crafts an ICMP echo request based onfunc makeEchoRequest(sequenceNumber int) icmp.Message {    bodyID := os.Getpid() & 0xffff    body := icmp.Echo{bodyID, sequenceNumber, []byte(ping)}    message := icmp.Message{Type: ipv4.ICMPTypeEcho, Code: 0, Body: &body}    return message}// sendOneRequest sends a single binary request into a given connection and// IP address and feeds the binary response into a channel. It does not attempt// to interpret the reply, nor does it package the data.func sendOneRequest(    connection *icmp.PacketConn, targetIP string, message []byte,    c chan []byte, e chan error) {    // Send the request    _, err := connection.WriteTo(        message, &net.IPAddr{IP: net.ParseIP(targetIP)})    if err != nil {        fmt.Println(Error writing to connection)        e <- err        return    }    binaryResponse := make([]byte, ICMP_RESPONSE_BUFFER_BYTES)    responseLength, _, err := connection.ReadFrom(binaryResponse)    if err != nil {        fmt.Println(Error reading from connection)        e <- err        return    }    c <- binaryResponse[:responseLength]}// estimatedRTT calculates the new estimated RTT based on a sample.// time.Duration is actually just an alias for int64,// but we use it so that it formats automatically when printed.func getEstimatedRTT(estimatedRTTOld, sampleRTT time.Duration) time.Duration {    return time.Duration((1.0-ALPHA)*float64(estimatedRTTOld) + ALPHA*float64(sampleRTT))}// devRTT calculates the variability of the RTT based on the estimate and// the sample.func getDevRTT(devRTTOld, estimatedRTT, sampleRTT time.Duration) time.Duration {    return time.Duration((1.0-BETA)*float64(devRTTOld) + BETA*math.Abs(float64(sampleRTT-estimatedRTT)))}// timeoutInterval calculates how long we should wait before timing out based// on the estimated round-trip time and the observed variability.func getTimeoutInterval(estimatedRTT, devRTT time.Duration) time.Duration {    return estimatedRTT + 4*devRTT}// calculateStatistics calculates the min, max, mean, and standard deviation of// a slice of time.Duration values.func calculateStatistics(pingTimes []time.Duration) (min, mean, max, stddev time.Duration) {    if len(pingTimes) == 0 {        return    }    min = pingTimes[0]    // This is Go's for-each loop. Go programs will not compile if they have    // unused variables, so I discard the index, which I don't need, into the    // null-variable _.    for _, sampleTime := range pingTimes {        if sampleTime > max {            max = sampleTime        } else if sampleTime < min {            min = sampleTime        }        mean += sampleTime    }    numResponses := time.Duration(len(pingTimes))    mean /= numResponses    // Go's math library functions require float types, but time.Duration is    // an int type, so we need to do a lot of casting.    var stddevCalc float64    for _, sampleTime := range pingTimes {        stddevCalc += math.Pow(float64(sampleTime-mean), 2.0)    }    stddevCalc /= float64(numResponses)    stddevCalc = math.Sqrt(stddevCalc)    stddev = time.Duration(int64(math.Floor(stddevCalc)))    return}func printStatistics(    min, mean, max, stddev time.Duration, programDuration time.Duration,    packetsTransmitted, packetsReceived int) {    packetsReceivedDec := float64(packetsReceived) / float64(packetsTransmitted)    packetsLostPercent := (1.0 - packetsReceivedDec) * 100.0    fmt.Printf(        %d packets transmitted, %d received, %.1f%% packet loss, ,        packetsTransmitted, packetsReceived, packetsLostPercent)    fmt.Println(time, programDuration)    fmt.Printf(rtt min/avg/max/mdev = %s/%s/%s/%s, min, mean, max, stddev)    fmt.Println()}func cleanUpAndExit(    connection *icmp.PacketConn, programStartTime time.Time,    packetsTransmitted, packetsReceived int, pingTimes []time.Duration,    address string) {    programDuration := time.Since(programStartTime)    fmt.Println()    fmt.Printf(--- %s ping statistics ---\n, address)    min, mean, max, stddev := calculateStatistics(pingTimes)    printStatistics(        min, mean, max, stddev, programDuration,        packetsTransmitted, packetsReceived)    // Deferred functions are not executed when os.Exit is called,    // so we need to close the network connection again here.    connection.Close()    os.Exit(0)}// getSequenceNumber extracts the sequence number from an ICMP echo response.func getSequenceNumber(data []byte) int {    byte1 := data[SEQ_BEGIN]    byte2 := data[SEQ_END]    return (int(byte1) << BITS_IN_BYTE) + int(byte2)}func main() {    programStartTime := time.Now()    numCPUs := runtime.NumCPU()    runtime.GOMAXPROCS(numCPUs)    // Get the IP address from the command line    if len(os.Args) != 2 {        fmt.Println(Please provide an IP address, and only an IP address.)        return    }    destAddress := net.ParseIP(os.Args[1])    if destAddress == nil {        fmt.Println(Please provide a valid IP address.)        return    }    destAddressStr := destAddress.String()    var sampleRTT time.Duration    estimatedRTT := INITIAL_ESTIMATED_RTT    devRTT := INITIAL_DEV_RTT    timeoutInterval := INITIAL_DYNAMIC_TIMEOUT_INTERVAL    connection, err := icmp.ListenPacket(ip4:icmp, 0.0.0.0)    defer connection.Close()    if err != nil {        fmt.Println(Error opening connection:, err)        return    }    sendOneRequestChannel := make(chan []byte)    errorChannel := make(chan error)    var responsesReceived, sequenceNumber int    pingTimes := make([]time.Duration, 0, TIMES_ARRAY_INITIAL_CAPACITY)    // Sets up the ability to end the program when the user presses Ctrl+C.    // Source: http://stackoverflow.com/a/11269077/3161525    //    // The second argument to make here signifies that the channel has a buffer    // that can hold 1 piece of data, meaning that pressing Ctrl+C more than    // once will have no effect.    interruptChannel := make(chan os.Signal, 1)    signal.Notify(interruptChannel, os.Interrupt)    // We put it into its own goroutine so it is listening at all times,    // and is not affected by the wait statements at the end of the main    // program loop.    go func() {        _ = <-interruptChannel        cleanUpAndExit(            connection, programStartTime, sequenceNumber,            responsesReceived, pingTimes, destAddressStr)    }()exit_from_loop:    for {        request := makeEchoRequest(sequenceNumber)        binaryRequest, err := request.Marshal(nil)        if err != nil {            fmt.Println(Error encoding request:, err)            return        }        go sendOneRequest(            connection, destAddressStr, binaryRequest,            sendOneRequestChannel, errorChannel)        sequenceNumber++        // set up the timer for this ping        t0 := time.Now()        select {        // The server responded normally        case binaryResponse := <-sendOneRequestChannel:            sampleRTT = time.Since(t0)            response, err := icmp.ParseMessage(                iana.ProtocolICMP, binaryResponse)            // The server sent a garbled packet            if err != nil {                fmt.Println(Error, received garbled response:, err)            }            switch response.Type {            case ipv4.ICMPTypeEchoReply:                responsesReceived++                responseData, _ := response.Body.Marshal(iana.ProtocolICMP)                responseSequenceNumber := getSequenceNumber(responseData)                fmt.Printf(                    %d bytes from %s: icmp_seq=%d ,                    len(responseData)+HEADER_SIZE_BYTES, destAddressStr,                    responseSequenceNumber)                fmt.Println(time=, sampleRTT)            case ipv4.ICMPTypeDestinationUnreachable:                fmt.Println(Error: destination is unreachable)                break exit_from_loop            default:                fmt.Println(Unknown response code received)                break exit_from_loop            }        // Something went wrong while writing to or reading from the connection        case err := <-errorChannel:            fmt.Println(Error: , err)            break exit_from_loop        // Go has no way to react the same way to multiple channels returning,        // so I have to write this twice.        case <-time.After(STATIC_TIMEOUT_INTERVAL):            fmt.Println(Connection timed out.)            break exit_from_loop        case <-time.After(timeoutInterval):            fmt.Println(Connection timed out.)            break exit_from_loop        }        pingTimes = append(pingTimes, sampleRTT)        // Recalculate the timeout intervals.        estimatedRTT = getEstimatedRTT(estimatedRTT, sampleRTT)        devRTT = getDevRTT(devRTT, estimatedRTT, sampleRTT)        timeoutInterval = getTimeoutInterval(estimatedRTT, devRTT)        // We want to send packets only every 2 seconds. If 2 seconds have not        // elapsed yet, we wait until that amount of time has elapsed.        timeSinceLastPing := time.Since(t0)        if timeSinceLastPing < SEND_INTERVAL {            time.Sleep(SEND_INTERVAL - timeSinceLastPing)        }    }    // Handle termination of the program due to an error, and not the user    // pressing Ctrl+C.    cleanUpAndExit(        connection, programStartTime, sequenceNumber, responsesReceived,        pingTimes, destAddressStr)}
ICMP ping program in Go
networking;go