Golang: 注意Channel的特性

作者:Ajian 发布时间:June 12, 2012 分类:Golang

  之前用channel练习的时候遇到一个问题,看似好像代码合理,而且编译也不会有问题,但忘记了一个重要的channel特性。

正确的代码:

package main
import "fmt"

func foo(){
    defer fmt.Println("World")
    fmt.Println("Hello")
}

func sum(x,y int,c chan int){
    c <- x + y
}

func main(){
    foo()
    c := make (chan int);
    go sum(24,18,c)
    fmt.Println(<-c);
}

编译输出//defer的作用应该知道吧

Hello

World

42

如果我把代码改成:
package main
import "fmt"

func foo(){
    defer fmt.Println("World")
    fmt.Println("Hello")
}

//func sum(x,y int,c chan int){
//    c <- x + y
//}

func main(){
    foo()
//    c := make (chan int);
//    go sum(24,18,c)
//    fmt.Println(<-c);
    c := make (chan int)
    d := 2
    c <- d+3 
    fmt.Println(<-c)
}

或者修改为

package main
import "fmt"

func foo(){
    defer fmt.Println("World")
    fmt.Println("Hello")
}

func sum(x,y int,c chan int){
    c <- x + y
}

func main(){
    foo()
    c := make (chan int);
    sum(24,18,c)
    fmt.Println(<-c);
}

修改的第一个是直接没有使用sum函数,赋值给管道之后直接打印出来,第二个修改是只是去掉了go,编译都可以通过,但是执行出来是

Hello
World
throw: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /root/Dropbox/code/go/channel.go:16 +0x5e

goroutine 2 [syscall]:
created by runtime.main
        /tmp/bindist046461602/go/src/pkg/runtime/proc.c:221

这是为什么呢,看似合理的程序,是忽略了Channel是阻塞的,如果没有使用go Channel就一直在阻塞的状态,执行就死循环了。这个特性也在很多场合带来了方便。

Golang: Simple HTTP Server

作者:Ajian 发布时间:June 12, 2012 分类:Golang

   最近还在学习Go语言,当然得多练习,尤其对于我这样的一个半路出家的人来说,对很多的专业术语和编程思想都不够,最近要学习的知识点和事情(随便记录下),实现文件持久化gob,进程锁的概念,channel的灵活使用,http proxy ,如何写自己的配置文件token,如何来检查go程序的性能瓶颈pprof 等,慢慢来也得快快学。

今天实验了下http server , 参考了Andrew Gerrand 的goto程序 https://github.com/nf

 

package main
import(
    "fmt"
    "net/http"
    "io/ioutil"
    "log"
    "time"
)

func Handler( w http.ResponseWriter,r *http.Request ){
    path := r.URL.Path[1:]   
    if path == "favicon.ico"  {
        http.NotFound(w, r)
        return
    }
    if path == ""{
        path = "index.html"
    }
    contents,err:= ioutil.ReadFile( path )
    if err !=nil{
        fmt.Fprintf( w,"404" )
        return
    }
    fmt.Fprintf( w,"%s\n",contents )
}

func Add( w http.ResponseWriter,r *http.Request ){
    name := r.FormValue("name")
    age := r.FormValue("age")
    if name == "" || age == "" {
        fmt.Fprint(w, AddForm)
        return
    }
    fmt.Fprintf(w, "Save : Your name is  %s , You age is %s",name,age)
}

func main(){
    http.HandleFunc( "/",Handler)
    http.HandleFunc( "/add",Add)
    s := &http.Server{  
        Addr:           ":8080",
        ReadTimeout:    30 * time.Second,
        WriteTimeout:   30 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    log.Fatal(s.ListenAndServe())
}

const AddForm = `
<html><body>
<form method="POST" action="/add">
Name: <input type="text" name="name">
Age: <input type="text" name="age">
<input type="submit" value="Add">
</form>
</body></html>
`

Golang: 扫描目录并获取相关信息Scan Directory and Get File Information

作者:Ajian 发布时间:May 18, 2012 分类:Golang

前言:最近看到Go里面有一个func很容易就可以扫描整个目录,并且可以得到相应的目录和文件信息,所以我将其进行了封装,拿到file info的所有信息 这样就可以方便的做其它用途了。

直接上代码,代码基于Go version 1

package main

import (
    "path/filepath"
    "os"
    "flag"
    "fmt"
    "time"
)

const (
    IsDirectory             = iota
    IsRegular
    IsSymlink
)

type sysFile struct {  
    fType       int
    fName       string
    fLink       string
    fSize       int64
    fMtime      time.Time
    fPerm       os.FileMode
}

type F struct {
    files []*sysFile
}

func (self *F) visit(path string, f os.FileInfo, err error) error {
    if ( f == nil ) {
        return err
    }
    var tp int
    if f.IsDir() {
        tp = IsDirectory
    }else if (  f.Mode() & os.ModeSymlink ) > 0 {
        tp = IsSymlink 
    }else{
        tp = IsRegular
    }
    inoFile := &sysFile{
        fName : path,
        fType : tp,
        fPerm : f.Mode(),
        fMtime: f.ModTime(),
        fSize : f.Size(),
    }
    self.files = append( self.files, inoFile )
    return nil
} 


func main() {
    flag.Parse()
    root := flag.Arg(0)
    self := F{
        files: make( []*sysFile, 0 ),
    }
    err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
        return self.visit(path, f, err)
    })

    if err != nil {  
      fmt.Printf("filepath.Walk() returned %v\n", err)
    }

    for _, v := range self.files {
        fmt.Println( v.fName,v.fSize )
    }
}

Golang: RPC Authorization 简单ip安全验证

作者:Ajian 发布时间:May 7, 2012 分类:Golang

前言:写网络服务,总要考虑安全机制,对ip和网段进行判断是最简单的一个验证机制。之后想做一个类似注册式的安全验证机制,既可以减少配置文件的麻烦,又可以很好的进行安全管理。

直接上代码 (基于go version 1 编译)

package main
import(
    "net"
    "fmt"
    "time"
    "strings"
)

func main(){
    IP_ARRAY := "192.168.1.234,192.168.1.47,192.168.2.0/28"
    servPort:=":7272"
    l,err := net.Listen( "tcp",servPort )
    if err != nil {
        fmt.Printf( "Listen is error" )
        return 
    }
    allowList :=strings.Split( IP_ARRAY,"," )
    for{  
        conn,err:=l.Accept()
        if err != nil {
            fmt.Printf( "start connect  is error" )
            return 
        }
        ipAddr:=conn.RemoteAddr()
        Addr := strings.Split( ipAddr.String(), ":")
        rAddr := net.ParseIP( Addr[0] )
        var authorized bool = false
        for v := range allowList{
            _,ipNet,err := net.ParseCIDR( allowList[v] )
            if err != nil{
                fmt.Printf( "parse ip net error" )
                ipHost := net.ParseIP( allowList[v])
                if ipHost != nil{
                   if ipHost.Equal( rAddr ) {
                      authorized =true
                   }
                }else{
                    fmt.Printf( "ip list error" )
                }
            }else{ 
                fmt.Printf( "Contains ip " )
                if ipNet.Contains( rAddr ) {
                    authorized =true
                }
            }
        }
        if authorized == true{  
            curTime:=time.Now()
            fmt.Printf( curTime.Format( "2006-01-02 15:04:05" )  )
            conn.Write( []byte(curTime.Format( "2006-01-02 15:04:05" ) ) )
            time.Sleep( 10)
        }else{
            conn.Close()
        }
    }
}

从不同的网络telnet 看下效果吧。

Golang: 简单smtp邮件发送样例

作者:Ajian 发布时间:April 25, 2012 分类:Golang

用GO语言写邮件发送发现也是件很简单和方便的事,可能最关键还是对字符处理和业务逻辑上的很麻烦,不过主体的smtp邮件发送功能是很简单的。最近在思考一个邮件发送路由,主要为的是解决分布式邮件报警和保证邮件送达率为100%.

样例参考:http://code.google.com/p/go-wiki/wiki/SendingMail

具体其它用法可以参考pkg/net/smtp

sendMail.go 基于Go Version 1

package main

import (
        "log"
        "net/smtp"
        "flag"
        "fmt"
        "strings"
)

var (
    subject = flag.String( "s","","subject of the mail" )
    body= flag.String( "b","","body of themail" )
    reciMail = flag.String( "m","","recipient mail address" )
)

func main() {
        // Set up authentication information.
        flag.Parse()
        sub := fmt.Sprintf("subject: %s\r\n\r\n",*subject)
        content :=  *body
        mailList := strings.Split( *reciMail,",")

        auth := smtp.PlainAuth(
                "",
                "smtpuser@example.com",
                "password",
                "smtp.example.com",
                //"smtp.gmail.com",
        )
        // Connect to the server, authenticate, set the sender and recipient,
        // and send the email all in one step.
        err := smtp.SendMail(
                "smtp.example.com:25",
                auth,
                "senduser@example.com",
                mailList,
                []byte(sub+content),
        )
        if err != nil {
                log.Fatal(err)
        }
}

使用

./sendMail -s "发送测试邮件主题" -b "我的邮件内容  yyyyyyyy" -m user1@gmail.com,user2@qq.com

看看是不是收到邮件了。发送到多个人就是这样方便。