func newLspServer(port int, params *LspParams) (*LspServer, error) {
	if params == nil {
		params = &LspParams{5, 2000}
	}
	hostport := fmt.Sprintf("localhost:%v", port)
	addr, err := lspnet.ResolveUDPAddr("udp", hostport)
	if lsplog.CheckReport(1, err) {
		return nil, err
	}
	udpconn, err := lspnet.ListenUDP("udp", addr)
	if lsplog.CheckReport(1, err) {
		return nil, err
	} else {
		lsplog.Vlogf(1, "[server] listen on %v\n", addr.String())
	}
	srv := &LspServer{
		server{
			nextConnId:     1,
			params:         params,
			connMap:        make(map[string]*lspConn),
			udpConn:        udpconn,
			udpAddr:        addr,
			netReadChan:    make(chan *udpPacket),
			appWriteChan:   make(chan *LspMsg),
			appReadChan:    make(chan *LspMsg),
			closeChan:      make(chan uint16),
			closeAllChan:   make(chan chan error),
			removeConnChan: make(chan uint16),
		},
	}
	go srv.loopServe()
	go srv.loopRead()
	return srv, nil
}
func newLspClient(hostport string, params *LspParams) (*LspClient, error) {
	if params == nil {
		params = &LspParams{5, 2000}
	}
	addr, err := lspnet.ResolveUDPAddr("udp", hostport)
	if lsplog.CheckReport(1, err) {
		return nil, err
	}
	udpConn, err := lspnet.DialUDP("udp", nil, addr)
	if err != nil {
		lsplog.Vlogf(1, "[client] connect to %v failed: %v\n", addr.String(), err)
		return nil, err
	} else {
		lsplog.Vlogf(1, "[client] connected to %v\n", addr.String())
	}
	removeChan := make(chan uint16)
	appReadChan := make(chan *LspMsg)
	conn := newLspConn(params, udpConn, addr, 0, appReadChan, removeChan)
	cli := &LspClient{
		client{
			udpConn:        udpConn,
			addr:           addr,
			conn:           conn,
			netReadChan:    make(chan *LspMsg),
			appWriteChan:   make(chan *LspMsg),
			appReadChan:    appReadChan,
			connIdChan:     make(chan uint16, 1),
			removeConnChan: removeChan,
			closeChan:      make(chan error),
		},
	}
	go cli.loopServe()
	go cli.loopRead()
	return cli, nil
}
func main() {
	var ihelp *bool = flag.Bool("h", false, "Show help information")
	var iport *int = flag.Int("p", 6666, "Port number")
	var ihost *string = flag.String("H", "localhost", "Host address")
	var iverb *int = flag.Int("v", 4, "Verbosity (0-6)")
	var irdrop *int = flag.Int("r", 0, "Network read packet drop percentage")
	var iwdrop *int = flag.Int("w", 0, "Network write packet drop percentage")
	var elim *int = flag.Int("k", 5, "Epoch limit")
	var ems *int = flag.Int("d", 2000, "Epoch duration (millisecconds)")
	flag.Parse()
	if *ihelp {
		flag.Usage()
		os.Exit(0)
	}
	if flag.NArg() > 0 {
		// Look for host:port on command line
		ok := true
		fields := strings.Split(flag.Arg(0), ":")
		ok = ok && len(fields) == 2
		if ok {
			*ihost = fields[0]
			n, err := fmt.Sscanf(fields[1], "%d", iport)
			ok = ok && n == 1 && err == nil
		}
		if !ok {
			flag.Usage()
			os.Exit(0)
		}
	}
	params := &lsp.LspParams{*elim, *ems}

	lsplog.SetVerbose(*iverb)
	lspnet.SetReadDropPercent(*irdrop)
	lspnet.SetWriteDropPercent(*iwdrop)
	hostport := fmt.Sprintf("%s:%v", *ihost, *iport)
	fmt.Printf("Connecting to server at %s\n", hostport)
	cli, err := lsp.NewLspClient(hostport, params)
	if err != nil {
		fmt.Printf("... failed.  Error message %s\n", err.Error())
	}
	if lsplog.CheckReport(1, err) {
		return
	}
	runclient(cli)
}