/*
runReceive receives all messages from a specified queue.
*/
func runReceive(conn *stompngo.Connection, q int, w *sync.WaitGroup) {
	ltag := tag + "-runreceive"

	qns := fmt.Sprintf("%d", q) // queue number
	id := stompngo.Uuid()       // A unique subscription ID
	d := sngecomm.Dest() + "." + qns

	ll.Printf("%stag:%s connsess:%s starts id:%s qns:%s d:%s\n",
		exampid, ltag, conn.Session(),
		id, qns, d)

	// Subscribe (use common helper)
	sc := sngecomm.HandleSubscribe(conn, d, id, sngecomm.AckMode())
	ll.Printf("%stag:%s connsess:%s subscribe_done id:%s qns:%s d:%s\n",
		exampid, ltag, conn.Session(),
		id, qns, d)

	//
	tmr := time.NewTimer(100 * time.Hour)

	pbc := sngecomm.Pbc() // Print byte count

	nmsgs := senv.Nmsgs()

	// Receive loop
	var md stompngo.MessageData
	for mc := 1; mc <= nmsgs; mc++ {
		ll.Printf("%stag:%s connsess:%s chanchek id:%s qns:%s lensc:%d capsc:%d\n",
			exampid, ltag, conn.Session(),
			id, qns, len(sc), cap(sc))
		select {
		case md = <-sc:
		case md = <-conn.MessageData:
			// Frames RECEIPT or ERROR not expected here
			ll.Fatalf("%stag:%s connsess:%s send_error qns:%v md:%v",
				exampid, ltag, conn.Session(),
				qns, md) // Handle this ......
		}

		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s receive_error qns:%v error:%v\n",
				exampid, ltag, conn.Session(),
				qns, md.Error)
		}

		// Process the inbound message .................
		ll.Printf("%stag:%s connsess:%s inbound id:%s qns:%s mc:%d\n",
			exampid, ltag, conn.Session(),
			id, qns, mc)
		// Sanity check the message Command, and the queue and message numbers
		mns := fmt.Sprintf("%d", mc) // string message number
		if md.Message.Command != stompngo.MESSAGE {
			ll.Fatalf("%stag:%s connsess:%s bad_frame qns:%s mc:%d md:%v\n",
				exampid, ltag, conn.Session(),
				qns, mc, md)
		}
		if !md.Message.Headers.ContainsKV("qnum", qns) || !md.Message.Headers.ContainsKV("msgnum", mns) {
			ll.Fatalf("%stag:%s connsess:%s dirty_message qns:%v msgnum:%v md:%v",
				exampid, tag, conn.Session(),
				qns, mns, md) // Handle this ......
		}

		sl := len(md.Message.Body)
		if pbc > 0 {
			sl = pbc
			if len(md.Message.Body) < sl {
				sl = len(md.Message.Body)
			}
		}

		ll.Printf("%stag:%s connsess:%s runReceive_recv_message id:%s body:%s qns:%s msgnum:%s\n",
			exampid, ltag, conn.Session(),
			id, string(md.Message.Body[0:sl]), qns,
			md.Message.Headers.Value("msgnum"))

		// Handle ACKs if needed
		if sngecomm.AckMode() != "auto" {
			ah := stompngo.Headers{}
			sngecomm.HandleAck(conn, ah, id)
		}
		if mc == nmsgs {
			break
		}
		if rw {
			dt := time.Duration(sngecomm.ValueBetween(min, max, rf))
			ll.Printf("%stag:%s connsess:%s recv_stagger dt:%v qns:%s mc:%d\n",
				exampid, ltag, conn.Session(),
				dt, qns, mc)
			tmr.Reset(dt)
			_ = <-tmr.C
			runtime.Gosched()
		}
	}
	// Unsubscribe
	sngecomm.HandleUnsubscribe(conn, d, id)

	ll.Printf("%stag:%s connsess:%s runRecieve_ends id:%s qns:%s\n",
		exampid, ltag, conn.Session(),
		id, qns)
	w.Done()
}
示例#2
0
func recv(conn *stompngo.Connection, s int) {
	ltag := tag + "-recv"

	ll.Printf("%stag:%s connsess:%s receiver_starts s:%d\n",
		exampid, ltag, conn.Session(),
		s)

	// Setup Headers ...
	id := stompngo.Uuid() // Use package convenience function for unique ID
	d := sngecomm.Dest()
	ackMode = sngecomm.AckMode() // get ack mode

	pbc := sngecomm.Pbc() // Print byte count

	sc := sngecomm.HandleSubscribe(conn, d, id, ackMode)
	// Receive loop.
	mc := 0
	var md stompngo.MessageData
	for {
		select {
		case md = <-sc: // Read a messagedata struct, with a MESSAGE frame
		case md = <-conn.MessageData: // Read a messagedata struct, with a ERROR/RECEIPT frame
			// Frames RECEIPT or ERROR not expected here
			ll.Fatalf("%stag:%s connsess:%s bad_frame md:%v",
				exampid, ltag, conn.Session(),
				md) // Handle this ......
		}
		//
		mc++
		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s error_read error:%v",
				exampid, ltag, conn.Session(),
				md.Error) // Handle this ......
		}
		ll.Printf("%stag:%s connsess:%s received_message s:%d id:%s mc:%d\n",
			exampid, ltag, conn.Session(),
			s, id, mc)
		if pbc > 0 {
			maxlen := pbc
			if len(md.Message.Body) < maxlen {
				maxlen = len(md.Message.Body)
			}
			ss := string(md.Message.Body[0:maxlen])
			ll.Printf("%stag:%s connsess:%s payload body:%s\n",
				exampid, tag, conn.Session(),
				ss)
		}

		// time.Sleep(3 * time.Second) // A very arbitrary number
		// time.Sleep(500 * time.Millisecond) // A very arbitrary number
		runtime.Gosched()
		time.Sleep(1500 * time.Millisecond) // A very arbitrary number
		runtime.Gosched()
		if ackMode != "auto" {
			sngecomm.HandleAck(conn, md.Message.Headers, id)
			ll.Printf("%stag:%s connsess:%s ack_complete s:%d id:%s mc:%d\n",
				exampid, ltag, conn.Session(),
				s, id, mc)
		}
		runtime.Gosched()
	}
}
示例#3
0
// Connect to a STOMP broker, receive some messages and disconnect.
func main() {

	st := time.Now()

	// Standard example connect sequence
	// Use AMQ port here
	n, conn, e := sngecomm.CommonConnect(exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_on_connect error:%v",
			exampid, tag, sngecomm.Lcs,
			e.Error()) // Handle this ......
	}

	pbc := sngecomm.Pbc() // Print byte count

	// Setup Headers ...
	id := stompngo.Uuid() // Use package convenience function for unique ID
	d := "jms.queue.exampleQueue"
	sc := sngecomm.HandleSubscribe(conn, d, id, "auto")
	ll.Printf("%stag:%s connsess:%s stomp_subscribe_complete\n",
		exampid, tag, conn.Session())

	//
	var md stompngo.MessageData
	// Read data from the returned channel
	for i := 1; i <= nmsgs; i++ {
		select {
		case md = <-sc:
		case md = <-conn.MessageData:
			// Frames RECEIPT or ERROR not expected here
			ll.Fatalf("%stag:%s connsess:%s bad_frame error:%v",
				exampid, tag, conn.Session(),
				e.Error()) // Handle this ......
		}
		ll.Printf("%stag:%s connsess:%s channel_read_complete\n",
			exampid, tag, conn.Session())
		// MessageData has two components:
		// a) a Message struct
		// b) an Error value.  Check the error value as usual
		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s error_read error:%v",
				exampid, tag, conn.Session(),
				e.Error()) // Handle this ......
		}
		ll.Printf("%stag:%s connsess:%s frame_type cmd:%s\n",
			exampid, tag, conn.Session(),
			md.Message.Command)

		if md.Message.Command != stompngo.MESSAGE {
			ll.Fatalf("%stag:%s connsess:%s error_frame_type error:%v",
				exampid, tag, conn.Session(),
				e.Error()) // Handle this ......
		}
		wh := md.Message.Headers
		for j := 0; j < len(wh)-1; j += 2 {
			ll.Printf("%stag:%s connsess:%s Header:%s:%s\n",
				exampid, tag, conn.Session(),
				wh[j], wh[j+1])
		}
		if pbc > 0 {
			maxlen := pbc
			if len(md.Message.Body) < maxlen {
				maxlen = len(md.Message.Body)
			}
			ss := string(md.Message.Body[0:maxlen])
			ll.Printf("%stag:%s connsess:%s payload body:%s\n",
				exampid, tag, conn.Session(),
				ss)
		}
	}

	//
	sngecomm.HandleUnsubscribe(conn, d, id)
	ll.Printf("%stag:%s connsess:%s unsubscribe_complete\n",
		exampid, tag, conn.Session())

	// Standard example disconnect sequence
	e = sngecomm.CommonDisconnect(n, conn, exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_disconnect error:%v",
			exampid, tag, conn.Session(),
			e.Error()) // Handle this ......
	}

	ll.Printf("%stag:%s connsess:%s main_elapsed:%v\n",
		exampid, tag, conn.Session(),
		time.Now().Sub(st))

}
示例#4
0
// Connect to a STOMP broker, receive some messages, ACK them, and disconnect.
func main() {

	st := time.Now()

	// Standard example connect sequence
	n, conn, e := sngecomm.CommonConnect(exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_on_connect error:%v",
			exampid, tag, sngecomm.Lcs,
			e.Error()) // Handle this ......
	}

	pbc := sngecomm.Pbc() // Print byte count

	// *NOTE* your application functionaltiy goes here!
	// With Stomp, you must SUBSCRIBE to a destination in order to receive.
	// Subscribe returns a channel of MessageData struct.
	// Here we use a common utility routine to handle the differing subscribe
	// requirements of each protocol level.
	d := senv.Dest()
	id := stompngo.Uuid()
	sc := sngecomm.HandleSubscribe(conn, d, id, "client")
	ll.Printf("%stag:%s connsess:%s main_subscribe_complete\n",
		exampid, tag, conn.Session())
	// Read data from the returned channel
	var md stompngo.MessageData
	for i := 1; i <= senv.Nmsgs(); i++ {

		select {
		case md = <-sc:
		case md = <-conn.MessageData:
			// Frames RECEIPT or ERROR not expected here
			ll.Fatalf("%stag:%s connsess:%s main_channel_frame error:%v",
				exampid, tag, conn.Session(),
				e.Error()) // Handle this ......
		}

		ll.Printf("%stag:%s connsess:%s main_channel_read_complete\n",
			exampid, tag, conn.Session())
		// MessageData has two components:
		// a) a Message struct
		// b) an Error value.  Check the error value as usual
		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s main_channel_read error:%v",
				exampid, tag, conn.Session(),
				e.Error()) // Handle this ......
		}
		//
		ll.Printf("%stag:%s connsess:%s frame_type:%v\n",
			exampid, tag, conn.Session(),
			md.Message.Command)
		if md.Message.Command != stompngo.MESSAGE {
			ll.Fatalf("%stag:%s connsess:%s bad_frame frame:%v",
				exampid, tag, conn.Session(),
				md.Message.Command) // Handle this ......
		}
		wh := md.Message.Headers
		for j := 0; j < len(wh)-1; j += 2 {
			ll.Printf("%stag:%s connsess:%s header:%s:%s\n",
				exampid, tag, conn.Session(),
				wh[j], wh[j+1])
		}
		if pbc > 0 {
			maxlen := pbc
			if len(md.Message.Body) < maxlen {
				maxlen = len(md.Message.Body)
			}
			ss := string(md.Message.Body[0:maxlen])
			ll.Printf("%stag:%s connsess:%s message_payload body:%s\n",
				exampid, tag, conn.Session(),
				ss)

		}
		// ACK the message just received.
		// Agiain we use a utility routine to handle the different requirements
		// of the protocol versions.
		sngecomm.HandleAck(conn, md.Message.Headers, id)
		ll.Printf("%stag:%s connsess:%s  ack_complete\n",
			exampid, tag, conn.Session())
	}
	// It is polite to unsubscribe, although unnecessary if a disconnect follows.
	// Again we use a utility routine to handle the different protocol level
	// requirements.
	sngecomm.HandleUnsubscribe(conn, d, id)
	ll.Printf("%stag:%s connsess:%s stomp_unsubscribe_complete\n",
		exampid, tag, conn.Session())

	// Standard example disconnect sequence
	e = sngecomm.CommonDisconnect(n, conn, exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s disconnect error:%v",
			exampid, tag, conn.Session(),
			e.Error()) // Handle this ......
	}

	ll.Printf("%stag:%s connsess:%s main_elapsed:%v\n",
		exampid, tag, conn.Session(),
		time.Now().Sub(st))

}
示例#5
0
// A forever reader.
func main() {

	st := time.Now()

	sngecomm.ShowRunParms(exampid)

	// Standard example connect sequence
	var e error
	n, conn, e = sngecomm.CommonConnect(exampid, tag, ll)
	if e != nil {
		if conn != nil {
			ll.Printf("%stag:%s  connsess:%s Connect Response headers:%v body%s\n",
				exampid, tag, conn.Session(), conn.ConnectResponse.Headers,
				string(conn.ConnectResponse.Body))
		}
		ll.Fatalf("%stag:%s connsess:%s main_on_connect error:%v",
			exampid, tag, sngecomm.Lcs,
			e.Error()) // Handle this ......
	}

	//**
	qn := 1
	ltag := tag + "-receiver"

	id := stompngo.Uuid() // A unique subscription ID
	d := sngecomm.Dest()

	ll.Printf("%stag:%s connsess:%s queue_info id:%v d:%v qnum:%v\n",
		exampid, ltag, conn.Session(),
		id, d, qn)
	// Subscribe
	sc := sngecomm.HandleSubscribe(conn, d, id, sngecomm.AckMode())
	ll.Printf("%stag:%s connsess:%s subscribe_complete id:%v d:%v qnum:%v\n",
		exampid, ltag, conn.Session(),
		id, d, qn)
	//
	var md stompngo.MessageData
	// Receive loop
	mc := 1
	for {
		ll.Println("========================== ", "Expecting Message:", mc,
			"==========================")
		select {
		case md = <-sc:
		case md = <-conn.MessageData:
			// A RECEIPT or ERROR frame is unexpected here
			ll.Fatalf("%stag:%s connsess:%s bad_frame qnum:%v headers:%v body:%s",
				exampid, tag, conn.Session(),
				qn, md.Message.Headers, md.Message.Body) // Handle this ......
		}
		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s recv_error qnum:%v error:%v",
				exampid, tag, conn.Session(),
				qn, md.Error) // Handle this ......
		}

		ll.Printf("%stag:%s connsess:%s next_frame mc:%v command:%v headers:%v body:%v\n",
			exampid, tag, conn.Session(),
			mc, md.Message.Command, md.Message.Headers, string(md.Message.Body)) // Handle this ......

		mc++
	}

	//**

	// Standard example disconnect sequence
	e = sngecomm.CommonDisconnect(n, conn, exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_on_disconnect error:%v",
			exampid, tag, conn.Session(),
			e.Error()) // Handle this ......
	}

	sngecomm.ShowStats(exampid, tag, conn)

	ll.Printf("%stag:%s connsess:%s main_elapsed:%v\n",
		exampid, tag, conn.Session(),
		time.Now().Sub(st))

}
// Receive messages from a particular queue
func receiver(conn *stompngo.Connection, qn, nmsgs int) {
	ltag := tag + "-receiver"

	qns := fmt.Sprintf("%d", qn) // queue number
	ll.Printf("%stag:%s connsess:%s starts qns:%d nmsgs:%d\n",
		exampid, ltag, conn.Session(),
		qn, nmsgs)
	//
	qp := sngecomm.Dest() // queue name prefix
	q := qp + "." + qns
	ll.Printf("%stag:%s connsess:%s queue_info q:%s qn:%d nmsgs:%d\n",
		exampid, ltag, conn.Session(),
		q, qn, nmsgs)
	id := stompngo.Uuid() // A unique subscription ID
	sc := sngecomm.HandleSubscribe(conn, q, id, sngecomm.AckMode())
	ll.Printf("%stag:%s connsess:%s subscribe_complete\n",
		exampid, ltag, conn.Session())
	// Many receivers running under the same connection can cause
	// (wire read) performance issues.  This is *very* dependent on the broker
	// being used, specifically the broker's algorithm for putting messages on
	// the wire.
	// To alleviate those issues, this strategy insures that messages are
	// received from the wire as soon as possible.  Those messages are then
	// buffered internally for (possibly later) application processing.

	bs := -1 //
	if s := os.Getenv("STOMP_CONN2BUFFER"); s != "" {
		i, e := strconv.ParseInt(s, 10, 32)
		if nil != e {
			ll.Fatalf("%stag:%s connsess:%s CONN2BUFFER_conversion_error error:%v",
				exampid, ltag, conn.Session(),
				e.Error()) // Handle this ......

		} else {
			bs = int(i)
		}
	}
	if bs < 1 {
		bs = nmsgs
	}
	ll.Printf("%stag:%s connsess:%s mdbuffersize_qnum bs:%d qn:%d\n",
		exampid, ltag, conn.Session(),
		bs, qn)

	// Process all inputs async .......
	// var mc chan stompngo.MessageData
	mdc := make(chan stompngo.MessageData, bs)      // MessageData Buffer size
	dc := make(chan bool)                           // Receive processing done channel
	go receiveWorker(mdc, qns, nmsgs, dc, conn, id) // Start async processor
	for i := 1; i <= nmsgs; i++ {
		mdc <- <-sc // Receive message data as soon as possible, and internally queue it
	}
	ll.Printf("%stag:%s connsess:%s waitforWorkersBegin qns:%s\n",
		exampid, ltag, conn.Session(),
		qns)
	<-dc // Wait until receive processing is done for this queue
	ll.Printf("%stag:%s connsess:%s waitforWorkersEnd qns:%s\n",
		exampid, ltag, conn.Session(),
		qns)

	// Unsubscribe
	sngecomm.HandleUnsubscribe(conn, q, id)
	ll.Printf("%stag:%s connsess:%s unsubscribe_complete\n",
		exampid, ltag, conn.Session())

	// Receiving is done
	ll.Printf("%stag:%s connsess:%s ends qns:%s\n",
		exampid, ltag, conn.Session(),
		qns)
	wgr.Done()
}
示例#7
0
func main() {

	// Make sure that the queue used by this example do not exist, or are
	// empty.

	// Following is a lengthy piece of code.  Read it striaght from top
	// to bottom.  There is zero complex logic here.

	// What this code will do:
	// Phase 1:
	// - Connect to a broker
	// - Verify a connection spec level
	// - Send a single message to the specified queue on that broker
	// - Disconnect from that broker
	//
	// Phase 2:
	// - Reconnect to the same broker
	// - Subscribe to the specified queue, using "ack:client-individual"
	// - Receive a single message
	// - Send an ACK, asking for a receipt
	//**************************************************************************
	// - Receive a RECEIPT // The point of this exercise.
	// - Show data from the RECEIPT and verify it // The point of this exercise.
	//**************************************************************************
	// - Disconnect from the broker

	// Start

	st := time.Now()

	// **************************************** Phase 1
	// Set up the connection.
	// Standard example connect sequence
	n, conn, e := sngecomm.CommonConnect(exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_on_connect error:%v",
			exampid, tag, sngecomm.Lcs,
			e.Error()) // Handle this ......
	}

	// ****************************************
	// App logic here .....

	d := sngecomm.Dest()
	ll.Printf("%stag:%s connsess:%s destination:%v\n",
		exampid, tag, conn.Session(),
		d)

	// ****************************************
	// Send exactly one message.
	sh := stompngo.Headers{"destination", sngecomm.Dest()}
	if senv.Persistent() {
		sh = sh.Add("persistent", "true")
	}
	m := exampid + " message: "
	t := m + "1"
	ll.Printf("%stag:%s connsess:%s sending_now body:%v\n",
		exampid, tag, conn.Session(),
		t)
	e = conn.Send(sh, t)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_bad_send error:%v",
			exampid, tag, conn.Session(),
			e.Error()) // Handle this ......
	}
	ll.Printf("%stag:%s connsess:%s send_complete body:%v\n",
		exampid, tag, conn.Session(),
		t)

	// ****************************************
	// Disconnect from the Stomp server
	// Standard example disconnect sequence
	e = sngecomm.CommonDisconnect(n, conn, exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_disconnect error:%v",
			exampid, tag, conn.Session(),
			e.Error()) // Handle this ......
	}

	// **************************************** Phase 2

	// Standard example connect sequence
	n, conn, e = sngecomm.CommonConnect(exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_on_connect error:%v",
			exampid, tag, sngecomm.Lcs,
			e.Error()) // Handle this ......
	}

	// ****************************************
	// Subscribe here
	id := stompngo.Uuid()
	// Get the "subscribe channel"
	sc := sngecomm.HandleSubscribe(conn, d, id, "client-individual")
	ll.Printf("%stag:%s connsess:%s stomp_subscribe_complete\n",
		exampid, tag, conn.Session())

	// Get data from the broker
	var md stompngo.MessageData // A message data instance
	select {
	case md = <-sc:
	case md = <-conn.MessageData:
		// This would be contain an ERROR or RECEIPT frame.  Both are unexpected
		// in this example.
		ll.Fatalf("%stag:%s connsess:%s bad_frame md:%v",
			exampid, tag, conn.Session(),
			md) // Handle this ......
	}
	ll.Printf("%stag:%s connsess:%s channel_read_complete\n",
		exampid, tag, conn.Session())

	// MessageData has two components:
	// a) a Message struct
	// b) an Error value.  Check the error value as usual
	if md.Error != nil {
		ll.Fatalf("%stag:%s connsess:%s message_error md:%v",
			exampid, tag, conn.Session(),
			md.Error) // Handle this ......
	}

	ll.Printf("%stag:%s connsess:%s read_message_COMMAND command:%s\n",
		exampid, tag, conn.Session(),
		md.Message.Command)
	ll.Printf("%stag:%s connsess:%s read_message_HEADERS headers:%s\n",
		exampid, tag, conn.Session(),
		md.Message.Headers)
	ll.Printf("%stag:%s connsess:%s read_message_BODY body:%s\n",
		exampid, tag, conn.Session(),
		string(md.Message.Body))

	// Here we need to send an ACK.  Required Headers are different between
	// a 1.1 and a 1.2 connection level.
	var ah stompngo.Headers
	if conn.Protocol() == stompngo.SPL_11 { // 1.1
		ah = ah.Add("subscription", md.Message.Headers.Value("subscription"))
		ah = ah.Add("message-id", md.Message.Headers.Value("message-id"))
	} else { // 1.2
		ah = ah.Add("id", md.Message.Headers.Value("ack"))
	}
	// We are also going to ask for a RECEIPT for the ACK
	rid := "receipt-001"
	ah = ah.Add("receipt", rid)
	//
	ll.Printf("%stag:%s connsess:%s ACK_receipt_headers headers:%v\n",
		exampid, tag, conn.Session(),
		ah)
	e = conn.Ack(ah)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s ack_error error:%v",
			exampid, tag, conn.Session(),
			e.Error()) // Handle this ......
	}

	// ****************************************
	// Finally get the RECEIPT.  Where is it?  It is *not* on the "subscribe
	// channel".  It is on the connection level MessageData channel.  Why?
	// Because the broker does *not* include a "subscription" header in
	// RECEIPT frames..
	// ****************************************

	// ***IMPORTANT***
	// ***NOTE*** which channel this RECEIPT MessageData comes in on.

	ll.Printf("%stag:%s connsess:%s start_receipt_read\n",
		exampid, tag, conn.Session())
	var rd stompngo.MessageData
	select {
	case rd = <-sc:
		// This would contain a MESSAGE frame.  It is unexpected here
		// in this example.
		ll.Fatalf("%stag:%s connsess:%s bad_frame_channel rd:%v\n",
			exampid, tag, conn.Session(),
			rd) // Handle this ......
	case rd = <-conn.MessageData: // RECEIPT frame s/b in the MessageData
		// Step 1 of Verify
		if rd.Message.Command != stompngo.RECEIPT {
			ll.Fatalf("%stag:%s connsess:%s bad_frame_command rd:%v\n",
				exampid, tag, conn.Session(),
				rd) // Handle this ......
		}
	}
	ll.Printf("%stag:%s connsess:%s end_receipt_read\n",
		exampid, tag, conn.Session())

	// ****************************************
	// Show details about the RECEIPT MessageData struct
	ll.Printf("%stag:%s connsess:%s receipt_COMMAND command:%s\n",
		exampid, tag, conn.Session(),
		rd.Message.Command)
	ll.Printf("%stag:%s connsess:%s receipt_HEADERS headers:%v\n",
		exampid, tag, conn.Session(),
		rd.Message.Headers)
	ll.Printf("%stag:%s connsess:%s receipt_BODY body:%s\n",
		exampid, tag, conn.Session(),
		string(rd.Message.Body))

	// Step 2 of Verify
	// Verify that the receipt has the id we asked for
	if rd.Message.Headers.Value("receipt-id") != rid {
		ll.Fatalf("%stag:%s connsess:%s bad_receipt_id wanted:%v got:%v\n",
			exampid, tag, conn.Session(),
			rid, rd.Message.Headers.Value("receipt-id")) // Handle this ......
	}
	ll.Printf("%stag:%s connsess:%s receipt_id_verified rid:%s\n",
		exampid, tag, conn.Session(),
		rid)

	// ****************************************
	// Disconnect from the Stomp server
	// Standard example disconnect sequence
	e = sngecomm.CommonDisconnect(n, conn, exampid, tag, ll)
	if e != nil {
		ll.Fatalf("%stag:%s connsess:%s main_disconnect error:%v",
			exampid, tag, sngecomm.Lcs,
			e.Error()) // Handle this ......
	}

	ll.Printf("%stag:%s connsess:%s main_elapsed:%v\n",
		exampid, tag, conn.Session(),
		time.Now().Sub(st))

}
func receiveMessages(conn *stompngo.Connection, qnum int, nc net.Conn) {
	ltag := tag + "-receivemessages"

	qns := fmt.Sprintf("%d", qnum) // queue number
	d := sngecomm.Dest() + "." + qns
	id := stompngo.Uuid() // A unique subscription ID

	ll.Printf("%stag:%s connsess:%s receiveMessages_start id:%s d:%s qnum:%d nmsgs:%d\n",
		exampid, ltag, conn.Session(),
		id, d, qnum, nmsgs)
	// Subscribe
	sc := sngecomm.HandleSubscribe(conn, d, id, sngecomm.AckMode())

	pbc := sngecomm.Pbc() // Print byte count

	//
	tmr := time.NewTimer(100 * time.Hour)
	var md stompngo.MessageData
	for mc := 1; mc <= nmsgs; mc++ {

		select {
		case md = <-sc:
		case md = <-conn.MessageData:
			// Frames RECEIPT or ERROR not expected here
			ll.Fatalf("%stag:%s connsess:%s send_error qns:%v md:%v",
				exampid, ltag, conn.Session(),
				qns, md) // Handle this ......
		}
		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s receive_error qns:%v error:%v\n",
				exampid, ltag, conn.Session(),
				qns, md.Error)
		}

		if md.Message.Command != stompngo.MESSAGE {
			ll.Fatalf("%stag:%s connsess:%s bad_frame qns:%s mc:%d md:%v\n",
				exampid, ltag, conn.Session(),
				qns, mc, md)
		}

		mcs := fmt.Sprintf("%d", mc) // message number
		if !md.Message.Headers.ContainsKV("qnum", qns) || !md.Message.Headers.ContainsKV("msgnum", mcs) {
			ll.Fatalf("%stag:%s connsess:%s dirty_message qns:%v msgnum:%v md:%v",
				exampid, tag, conn.Session(),
				qns, mcs, md) // Handle this ......
		}

		// Process the inbound message .................
		sl := len(md.Message.Body)
		if pbc > 0 {
			sl = pbc
			if len(md.Message.Body) < sl {
				sl = len(md.Message.Body)
			}
		}
		ll.Printf("%stag:%s connsess:%s receiveMessages_msg d:%s body:%s qnum:%d msgnum:%s\n",
			exampid, ltag, conn.Session(),
			d, string(md.Message.Body[0:sl]), qnum,
			md.Message.Headers.Value("msgnum"))
		if mc == nmsgs {
			break
		}
		// Handle ACKs if needed
		if sngecomm.AckMode() != "auto" {
			ah := []string{}
			sngecomm.HandleAck(conn, ah, id)
		}
		if mc == nmsgs {
			break
		}
		//
		if rw {
			runtime.Gosched() // yield for this example
			dt := time.Duration(sngecomm.ValueBetween(min, max, rf))
			ll.Printf("%stag:%s connsess:%s recv_stagger dt:%v qns:%s mc:%d\n",
				exampid, ltag, conn.Session(),
				dt, qns, mc)
			tmr.Reset(dt)
			_ = <-tmr.C
		}
	}
	ll.Printf("%stag:%s connsess:%s end d:%s qnum:%d nmsgs:%d\n",
		exampid, ltag, conn.Session(),
		d, qnum, nmsgs)

	// Unsubscribe
	sngecomm.HandleUnsubscribe(conn, d, id)
	//
}
// Receive messages from a particular queue
func receiver(qn, mc int) {
	ltag := tag + "-receiver"

	qns := fmt.Sprintf("%d", qn) // string queue number
	pbc := sngecomm.Pbc()
	id := stompngo.Uuid() // A unique subscription ID
	d := sngecomm.Dest() + "." + qns

	ll.Printf("%stag:%s connsess:%s queue_info id:%v d:%v qnum:%v mc:%v\n",
		exampid, ltag, conn.Session(),
		id, d, qn, mc)
	// Subscribe
	sc := sngecomm.HandleSubscribe(conn, d, id, sngecomm.AckMode())
	ll.Printf("%stag:%s connsess:%s subscribe_complete id:%v d:%v qnum:%v mc:%v\n",
		exampid, ltag, conn.Session(),
		id, d, qn, mc)
	//
	tmr := time.NewTimer(100 * time.Hour)
	var md stompngo.MessageData
	// Receive loop
	for i := 1; i <= mc; i++ {
		ll.Printf("%stag:%s connsess:%s recv_ranchek id:%v d:%v qnum:%v mc:%v chlen:%v chcap:%v\n",
			exampid, ltag, conn.Session(),
			id, d, qn, mc, len(sc), cap(sc))

		select {
		case md = <-sc:
		case md = <-conn.MessageData:
			// A RECEIPT or ERROR frame is unexpected here
			ll.Fatalf("%stag:%s connsess:%s bad_frame qnum:%v headers:%v body:%s",
				exampid, tag, conn.Session(),
				qn, md.Message.Headers, md.Message.Body) // Handle this ......
		}
		if md.Error != nil {
			ll.Fatalf("%stag:%s connsess:%s recv_error qnum:%v error:%v",
				exampid, tag, conn.Session(),
				qn, md.Error) // Handle this ......
		}

		// Process the inbound message .................
		ll.Printf("%stag:%s connsess:%s recv_message qnum:%v i:%v\n",
			exampid, tag, conn.Session(),
			qn, i)
		if pbc > 0 {
			maxlen := pbc
			if len(md.Message.Body) < maxlen {
				maxlen = len(md.Message.Body)
			}
			ss := string(md.Message.Body[0:maxlen])
			ll.Printf("%stag:%s connsess:%s payload qnum:%v body:%s\n",
				exampid, tag, conn.Session(),
				qn, ss)

		}

		// Sanity check the message Command, and the queue and message numbers
		mns := fmt.Sprintf("%d", i) // message number
		if md.Message.Command != stompngo.MESSAGE {
			ll.Fatalf("%stag:%s connsess:%s bad_frame qnum:%v command:%v headers:%v body:%v\n",
				exampid, tag, conn.Session(),
				qn, md.Message.Command, md.Message.Headers, string(md.Message.Body)) // Handle this ......

		}
		if !md.Message.Headers.ContainsKV("qnum", qns) || !md.Message.Headers.ContainsKV("msgnum", mns) {
			ll.Fatalf("%stag:%s connsess:%s dirty_message qns:%v msgnum:%v command:%v headers:%v body:%v\n",
				exampid, tag, conn.Session(),
				qns, mns, md.Message.Command, md.Message.Headers, string(md.Message.Body)) // Handle this ......) // Handle this ......
		}

		if i == mc {
			break
		}

		if rw {
			dt := time.Duration(sngecomm.ValueBetween(min, max, rf))
			ll.Printf("%stag:%s connsess:%s recv_stagger id:%v d:%v qnum:%v stagger:%v\n",
				exampid, ltag, conn.Session(),
				id, d, qn, dt)
			tmr.Reset(dt)
			_ = <-tmr.C
			runtime.Gosched()
		}

		// Handle ACKs if needed
		if sngecomm.AckMode() != "auto" {
			sngecomm.HandleAck(conn, md.Message.Headers, id)
		}
	}
	// Unsubscribe
	sngecomm.HandleUnsubscribe(conn, d, id)
	ll.Printf("%stag:%s connsess:%s unsubscribe_complete id:%v d:%v qnum:%v mc:%v\n",
		exampid, ltag, conn.Session(),
		id, d, qn, mc)

	// Receiving is done
	ll.Printf("%stag:%s connsess:%s recv_end id:%v d:%v qnum:%v mc:%v\n",
		exampid, ltag, conn.Session(),
		id, d, qn, mc)

	wgr.Done()
}