Unix Sockets in Go

转自:http://stackoverflow.com/questions/2886719/unix-sockets-in-go

Server:

package main

import "net"
import "fmt"

func echoServer(c net.Conn) {
    for {
        buf := make([]byte, 512)
        nr, err := c.Read(buf)
        if err != nil {
            return
        }

        data := buf[0:nr]
        fmt.Printf("Received: %v", string(data))
        _, err = c.Write(data)
        if err != nil {
            panic("Write: " + err.String())
        }
    }
}

func main() {
    l, err := net.Listen("unix", "/tmp/echo.sock")
    if err != nil {
        println("listen error", err.String())
        return
    }

    for {
        fd, err := l.Accept()
        if err != nil {
            println("accept error", err.String())
            return
        }

        go echoServer(fd)
    }
}

Client

package main

import (
    "net"
    "time"
    "io"
)

func reader(r io.Reader) {
    buf := make([]byte, 1024)
    for {
        n, err := r.Read(buf[:])
        if err != nil {
            return
        }
        println("Client got:", string(buf[0:n]))
    }
}

func main() {
    c,err := net.Dial("unix","", "/tmp/echo.sock")
    if err != nil {
        panic(err.String())
    }
    defer c.Close()

    go reader(c)
    for {
        _,err := c.Write([]byte("hi"))
        if err != nil {
            println(err.String())
            break
        }
        time.Sleep(1e9)
    }
}

转自:

Server:

package main

import (
    "fmt";
    "net";
    "log";
    "os";
    "container/list";
    "strings";
    "bytes";
    "flag";
)

// flag for debuging info. or a simple log
var debug = flag.Bool("d", false, "set the debug modus( print informations )")

type ClientChat struct {
    Name string;        // name of user
    IN chan string;     // input channel for to send to user
    OUT chan string;    // input channel from user to all
    Con *net.Conn;      // connection of client
    Quit chan bool;     // quit channel for all goroutines
    ListChain *list.List;    // reference to list
}

// read from connection and return true if ok
func (c *ClientChat) Read(buf []byte) bool{
    nr, err := c.Con.Read(buf);
    if err!=nil {
        c.Close();
        return false;
    }
    Log("Read():  ", nr, " bytes");
    return true;
}

// close the connection and send quit to sender
func (c *ClientChat) Close() {
    c.Quit<-true;
    c.Con.Close();
    c.deleteFromList();
}

// compare two clients: name and network connection
func (c *ClientChat) Equal(cl *ClientChat) bool {
    if bytes.Equal(strings.Bytes(c.Name), strings.Bytes(cl.Name)) {
        if c.Con == cl.Con {
            return true;
        }
    }
    return false;
}

// delete the client from list
func (c *ClientChat) deleteFromList() {
    for e := c.ListChain.Front(); e != nil; e = e.Next() {
        client := e.Value.(ClientChat);
        if c.Equal(&client) {
            Log("deleteFromList(): ", c.Name);
            c.ListChain.Remove(e);
        }
    }
}

// func Log(v ...): loging. give log information if debug is true
func Log(v ...) {
    if *debug == true {
        ret := fmt.Sprint(v);
        log.Stdoutf("SERVER: %s", ret);
    }
}

// func test(): testing for error
func test(err os.Error, mesg string) {
    if err!=nil {
        log.Stderr("SERVER: ERROR: ", mesg);
         os.Exit(-1);
    } else
        Log("Ok: ", mesg);
}

// handlingINOUT(): handle inputs from client, and send it to all other client via channels.
func handlingINOUT(IN <-chan string, lst *list.List) {
    for {
        Log("handlingINOUT(): wait for input");
        input := <-IN;  // input, get from client
        // send to all client back
        Log("handlingINOUT(): handling input: ", input);
        for value := range lst.Iter() {
            client := value.(ClientChat);
            Log("handlingINOUT(): send to client: ", client.Name);
            client.IN<- input;
        }  
    }
}

// clientreceiver wait for an input from network, after geting data it send to
// handlingINOUT via a channel.
func clientreceiver(client *ClientChat) {
    buf := make([]byte, 2048);

    Log("clientreceiver(): start for: ", client.Name);
    for client.Read(buf) {
        
        if bytes.Equal(buf, strings.Bytes("/quit")) {
            client.Close();
            break;
        }
        Log("clientreceiver(): received from ",client.Name, " (", string(buf), ")");
        send := client.Name+"> "+string(buf);
        client.OUT<- send;
        for i:=0; i<2048;i++ {
            buf[i]=0x00;
        }
    }    

    client.OUT <- client.Name+" has left chat";
    Log("clientreceiver(): stop for: ", client.Name);
}

// clientsender(): get the data from handlingINOUT via channel (or quit signal from
// clientreceiver) and send it via network
func clientsender(client *ClientChat) {
    Log("clientsender(): start for: ", client.Name);
    for {
        Log("clientsender(): wait for input to send");
        select {
            case buf := <- client.IN:
                Log("clientsender(): send to \"", client.Name, "\": ", string(buf));
                client.Con.Write(strings.Bytes(buf));
            case <-client.Quit:
                Log("clientsender(): client want to quit");
                client.Con.Close();
                break;
        }
    }
    Log("clientsender(): stop for: ", client.Name);
}

// clientHandling(): get the username and create the clientsturct
// start the clientsender/receiver, add client to list.
func clientHandling(con *net.Conn, ch chan string, lst *list.List) {
    buf := make([]byte, 1024);
    con.Read(buf);
    name := string(buf);
    newclient := &ClientChat{name, make(chan string), ch, con, make(chan bool), lst};

    Log("clientHandling(): for ", name);
    go clientsender(newclient);
    go clientreceiver(newclient);
    lst.PushBack(*newclient);
    ch<- name+" has joinet the chat";
}

func main() {
    flag.Parse();
    Log("main(): start");

    // create the list of clients
    clientlist := list.New();
    in := make(chan string);
    Log("main(): start handlingINOUT()");
    go handlingINOUT(in, clientlist);
    
    // create the connection
    netlisten, err := net.Listen("tcp", "127.0.0.1:9988");
    test(err, "main Listen");
    defer netlisten.Close();

    for {
        // wait for clients
        Log("main(): wait for client ...");
        conn, err := netlisten.Accept();
        test(err, "main: Accept for client");
        go clientHandling(&conn, in, clientlist);
    }
}

Server has three part which are running as goroutines and communicate via channels.

1) handlingINOUT() simple wait for input of clientreceiver() and send to all clientsender() which are in the list.

2) clientreceiver() wait for his data from client via networkconnection and send it to a inputchannel to handlingINOUT

3) clientsender() wait for data from channel and send it to client

every client connection get a his own clientreceiver/sender and a list entry. on disconnection the list entry will be deleted.

Client:

package main

import (
    "fmt";
    "net";
    "log";
    "os";
    "bytes";
    "bufio";
    "strings";
    "time";
    "flag";
)

var running bool;  // global variable if client is running

var debug = flag.Bool("d", false, "set the debug modus( print informations )")

// func Log(v ...): loging. give log information if debug is true
func Log(v ...) {
    if *debug == true {
        ret := fmt.Sprint(v);
        log.Stdoutf("CLIENT: %s", ret);
    }
}

// func test(): testing for error
func test(err os.Error, mesg string) {
    if err!=nil {
        log.Stderr("CLIENT: ERROR: ", mesg);
         os.Exit(-1);
    } else
        Log("Ok: ", mesg);
}

// read from connection and return true if ok
func Read(con *net.Conn) string{
    var buf [4048]byte;
    _, err := con.Read(&buf);
    if err!=nil {
        con.Close();
        running=false;
        return "Error in reading!";
    }
    str := string(&buf);
    fmt.Println();
    return string(str);
}

// clientsender(): read from stdin and send it via network
func clientsender(cn *net.Conn) {
    reader := bufio.NewReader(os.Stdin);
    for {
        fmt.Print("you> ");
        input, _ := reader.ReadBytes('\n');
        if bytes.Equal(input, strings.Bytes("/quit\n")) {
            cn.Write(strings.Bytes("/quit"));
            running = false;
            break;
        }
        Log("clientsender(): send: ", string(input[0:len(input)-1]));
        cn.Write(input[0:len(input)-1]);
    }
}

// clientreceiver(): wait for input from network and print it out
func clientreceiver(cn *net.Conn) {
    for running {
        fmt.Println(Read(cn));
        fmt.Print("you> ");
    }
}

func main() {
    flag.Parse();
    running = true;
    Log("main(): start ");
    
    // connect
    destination := "127.0.0.1:9988";
    Log("main(): connecto to ", destination);
    cn, err := net.Dial("tcp", "", destination);
    test(err, "dialing");
    defer cn.Close();
    Log("main(): connected ");

    // get the user name
    fmt.Print("Please give you name: ");
    reader := bufio.NewReader(os.Stdin);
    name, _ := reader.ReadBytes('\n');

    //cn.Write(strings.Bytes("User: "));
    cn.Write(name[0:len(name)-1]);

    // start receiver and sender
    Log("main(): start receiver");
    go clientreceiver(&cn);
    Log("main(): start sender");
    go clientsender(&cn);
    
    // wait for quiting (/quit). run until running is true
    for ;running; {
        time.Sleep(1*1e9);
    }
    Log("main(): stoped");
}