Golang sending data over TCP/IP

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

What's wasting today is tomorrow to those who died yesterday; What's the future of hate now?

What you are wasting today is the tomorrow that the man who died yesterday expects, and what you dislike is now the future you will never go back to.


How do I send data from process A to process B with a simple TCP/IP connection?



In many cases, using a higher-level network protocol will undoubtedly do better, hiding all the technical details under a fancy API. And there is already a lot to choose from, depending on the need: Message Queuing protocol, grpc,protobuf,flatbuffers,restful Web Api,websockets, and so on.






However, in some cases (especially in small projects), any method you choose may seem completely oversized.



1. Connections is an IO stream



Net. The conn implements IO. Reader, Io. Writer, Io. Closer interface. So we're going to use the TCP link just like the IO stream.



First, let's look at the definition of these three types in the IO package of the Golang source code:


type Reader interface {
	Read(p []byte) (n int, err error)
}

type Writer interface {
	Write(p []byte) (n int, err error)
}

type Closer interface {
	Close() error
}


Then look at the definition of the Golang source NET package conn Type:


type Conn interface {
	
	Read(b []byte) (n int, err error)


	Write(b []byte) (n int, err error)

	
	Close() error

	LocalAddr() Addr

	RemoteAddr() Addr

	SetDeadline(t time.Time) error

	
	SetReadDeadline(t time.Time) error

	
	SetWriteDeadline(t time.Time) error
}


So we can send string strings over a TCP link, but how to send a complex type?



2. Go Coding Complex Types



JSON is easy to think of when it comes to sending structured data over the network, but go itself provides a GOB package that operates directly on IO stream data, serializes and deserializes data, does not require JSON to add tags, and then laborious JSON. Unmarshal () into binary data.






3. Basic elements for sending string data over TCP:



1. Sending party



1. Open a link to a receiving process



2. Writing a string



3. Close the link



The Golang NET package already provides all of the above methods.






RESOLVETCPADDR () accepts a string representing the TCP address (localhost, 127.0.0.1:80, [:: 1]:80 all represent local 80 ports), and returns a net. TCPADDR (), if this address cannot be resolved, returns an error.



DIALTCP () accepts a net. ADDR () then connects to this address and returns an open net after success. Tcpconn the linked object.



If we don't need to be more detailed about dialing settings. We can use net directly. Dial to replace.



If the link succeeds, the linked object can be encapsulated as a bufio. Readwriter,


type ReadWriter struct {
    *Reader
    *Writer
}


We can read the data using the ReadString () writestring () Readbytes () method



Note that buffered writes need to be called flush () after writing to forward all data to the underlying network connection



2. Sending party



1. Start listening to local ports



2. When a request is accepted, a goroutine is initiated to process the request



3. Read the data in this goroutine, optionally send a response.



4. Close the link



4. Processing of complex types



The service side gives the object's handling based on the requested data type. How to run it briefly:



Step One: When Listen () receives a new link, a new goroutine is generated to execute the request method of the corresponding data type Handlemessage (). The function reads the command name from the connection, finds the appropriate handler function from the map, and invokes the function.



Part Two: The selected handler reads and processes the request data.






Detailed Description:



Request data type and the corresponding processing method----new Goroutine--






The detailed code:



1. Project directory Schema






2. library files





Package Lib
Import (
"Bufio"
"Net"
"github.com/pkg/errors"
"FMT"
"Sync"
"IO"
"Strings"
"encoding/gob"
)
//Mixed type struct
type ComplexData struct{
N int
S string
M map[string]int
P []byte
C *ComplexData
}
Const (
Port = ": 61000" / / the port accepted by the server
)
* *
Net.conn implements io.reader io.writer io.closer interface
Open returns a TCP link buffer with timeout readwrite
* /
func Open(addr string) (*bufio.ReadWriter, error) {
// Dial the remote process.
// Note that the local port is chosen on the fly. If the local port
// must be a specific one, use DialTCP() instead.
fmt.Println("Dial " + addr)
conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, errors.Wrap(err, "Dialing "+addr+" failed")
}
return bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)), nil
}
type HandleFunc func(*bufio.ReadWriter)
type EndPoint struct{
listener net.Listener
//Handlefunc is a function type that handles incoming commands. It receives open connections packaged in a reader writer interface.
handler map[string]HandleFunc
//Map is not thread safe, so read-write lock control is required
m sync.RWMutex
}
func NewEndPoint() *EndPoint{
return &EndPoint{
handler:map[string]HandleFunc{},
}
}
//Add data type processing method
func (e *EndPoint)AddHandleFunc(name string , f HandleFunc){
E.m.Lock ()
e.handler[name] = f
E.m.Unlock ()
}
//Verify the request data type and send it to the corresponding processing function
func (e *EndPoint)handleMessage(conn net.Conn){
rw := bufio.NewReadWriter(bufio.NewReader(conn),
bufio.NewWriter(conn))
defer conn.Close()
For{
cmd, err := rw.ReadString('\n')
Switch {
case err == io.EOF:
FMT. Println ("read complete.")
Return
case err != nil:
Fmt.println ("read error")
Return
}
cmd = strings.Trim(cmd, "\n ")
E.m.RLock ()
handleCmd , ok := e.handler[cmd]
If! Ok{
FMT. Println ("unregistered request data type.")
Return
}
//Specific processing of linked data
handleCmd(rw)
}
}
func (e *EndPoint) Listen()error{
var err error
e.listener, err = net.Listen("tcp", Port)
if err != nil{
Return errors.wrap (err, "TCP service cannot listen on port" + port)
}
FMT. Println ("service listening succeeded:", e.listener. Addr(). String())
For{
conn, err := e.listener.Accept()
if err != nil{
FMT. Println ("heart request listening failed!")
Continue
}
//Start processing new linked data
go e.handleMessage(conn)
}
}
func HandleStrings(rw *bufio.ReadWriter){
s, err := rw.ReadString('\n')
if err!= nil{
FMT. Println ("link unreadable.")
Return
}
s = strings.Trim(s , "\n ")
/ /...
_, err = rw.writestring ("processing completed... \ n")
if err != nil{
Fmt.println ("link write response failed")
Return
}
//Write underlying network link
err = rw.Flush()
if err != nil{
Fmt.println ("flush write failed")
Return
}
}
func HandleGob(rw *bufio.ReadWriter){
var data ComplexData
dec := gob.NewDecoder(rw)
err := dec.Decode(&data)
if err != nil{
FMT. Println ("unresolved binary data.")
Return
}
FMT. Println ("output:", data, data. C)
}


3. Service files



Server.go


package main

import(
	. "tcpNetWorking/lib"
	"fmt"
	"github.com/pkg/errors"
)

func server()error{
	endpoint := NewEndPoint()

	endpoint.AddHandleFunc("string", HandleStrings)
	endpoint.AddHandleFunc("gob", HandleGob)

	// start to listen
	return endpoint.Listen()
}

func main(){
	err := server()
	if err != nil {
		fmt.Println("Error:", errors.WithStack(err))
	}
}





Client.go


Package main
Import (
"FMT"
. "tcpNetWorking/lib"
"github.com/pkg/errors"
"encoding/gob"
"Strconv"
"Log"
)
func client(ip string) error {
cpData := ComplexData{
N: 10,
S: "Test string data",
M: map[string]int{"A": 1, "B": 2},
P: [] byte ("test [] byte data"),
C: &ComplexData{
N: 256,
S: "Recursive structs? Piece of cake!",
M: map[string]int{"01": 1, "10": 2, "11": 3},
}
}
rw, err := Open(ip + Port)
if err != nil {
Fmt.Println ("client cannot link to change address:" + IP + Port)
Return err
}
n, err := rw.WriteString("string\n")
if err != nil {
return errors.Wrap(err, "Could not send the STRING request ("+strconv.Itoa(n)+" bytes written)")
}
n, err = rw.WriteString("Additional data.\n")
if err != nil {
return errors.Wrap(err, "Could not send additional STRING data ("+strconv.Itoa(n)+" bytes written)")
}
err = rw.Flush()
if err != nil {
return errors.Wrap(err, "Flush failed.")
}
// Read the reply.
response, err := rw.ReadString('\n')
if err != nil {
return errors.Wrap(err, "Client: Failed to read the reply: '"+response+"'")
}
log.Println("STRING request: got a response:", response)
log.Println("Send a struct as GOB:")
log.Printf("Outer complexData struct: \n%#v\n", cpData)
log.Printf("Inner complexData struct: \n%#v\n", cpData.C)
enc := gob.NewEncoder(rw)
n, err = rw.WriteString("gob\n")
if err != nil {
return errors.Wrap(err, "Could not write GOB data ("+strconv.Itoa(n)+" bytes written)")
}
err = enc.Encode(cpData)
if err != nil {
return errors.Wrapf(err, "Encode failed for struct: %#v", cpData)
}
err = rw.Flush()
if err != nil {
return errors.Wrap(err, "Flush failed.")
}
Return nil
}
Func main () {
err := client("localhost")
if err != nil {
fmt.Println("Error:", errors.WithStack(err))
}
} 








The logic is basically the same as the Web routing service I wrote earlier, except that data processing uses the GOB package binary form. Take a look at it, please.



https://my.oschina.net/90design/blog/1604539








Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.