鍍金池/ 教程/ GO/ 聊天室的開發(fā)
面向?qū)ο?/span>
Go 語言簡介
網(wǎng)頁下載
聊天室的開發(fā)
image 網(wǎng)站開發(fā)
基本語法
高級應(yīng)用

聊天室的開發(fā)

前期準(zhǔn)備

  • 需要 import "net"包
  • IP 類型,其中一個重要的方法是 IP.ParseIP(ipaddr string)來判斷是否是合法的 IP 地址

TCP Client

  • func (c *TCPConn) Write(b []byte) (n int, err os.Error)用于發(fā)送數(shù)據(jù),返回發(fā)送的數(shù)據(jù)長度或者返回錯誤,是TCPConn的方法
  • func (c *TCPConn) Read(b []byte) (n int, err os.Error)用于接收數(shù)據(jù),返回接收的長度或者返回錯誤,是 TCPConn 的方法
  • TCPAddr 類型,保存 TCP 的地址信息,包括地址和端口
 type TCPAddr struct {
      IP IP
      Port int
  }
  • func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error)獲取一個 TCPAddr,參數(shù)都是 string 類型,net 是個 const string,包括 tcp4,tcp6,tcp 一般使用 tcp,兼容 v4 和 v6,addr 表示 ip 地址,包括端口號,如www.google.com:80之類的
  • func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err os.Error)用來連接(connect)到遠(yuǎn)程服務(wù)器上,net 表示協(xié)議方式,tcp,tcp4 或者 tcp6,laddr 表示本機地址,一般為 nil,raddr 表示遠(yuǎn)程地址,這里的 laddr 和 raddr 都是 TCPAddr 類型的,一般是上一個函數(shù)的返回值。
  • 作為一個 TCP 的客戶端,基本的操作流程如下:
  service="www.google.com:80"
  tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
  conn, err := net.DialTCP("tcp", nil, tcpAddr)
  _, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
  _, err = conn.Read(b) / result, err := ioutil.ReadAll(conn)

TCP Server

  • func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)用來監(jiān)聽端口,net 表示協(xié)議類型,laddr 表示本機地址,是 TCPAddr 類型,注意,此處的 laddr 包括端口,返回一個*TCPListener類型或者錯誤
  • func (l *TCPListener) Accept() (c Conn, err os.Error)用來返回一個新的連接,進(jìn)行后續(xù)操作,這是 TCPListener 的方法,一般 TCPListener 從上一個函數(shù)返回得來。

  • 服務(wù)器的基本操作流程為:
  service:=":9090"
  tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
  l,err := net.ListenTCP("tcp",tcpAddr)
  conn,err := l.Accept()
  go Handler(conn) //此處使用go關(guān)鍵字新建線程處理連接,實現(xiàn)并發(fā)

實現(xiàn)一個公共聊天服務(wù)器。

  • 服務(wù)器接收客戶端的信息
  • 接收完以后將客戶端的信息發(fā)送到所有的客戶端上
  • 客戶端使用/quit退出聊天
  • 只使用一套代碼,通過命令行參數(shù)啟動服務(wù)器還是客戶端

實現(xiàn):

package main  

import(  
    "fmt"  
    "os"  
    "net"  
)  

////////////////////////////////////////////////////////  
//  
//錯誤檢查  
//  
////////////////////////////////////////////////////////  
func checkError(err error,info string) (res bool) {  

    if(err != nil){  
        fmt.Println(info+"  " + err.Error())  
        return false  
    }  
    return true  
}  

////////////////////////////////////////////////////////  
//  
//服務(wù)器端接收數(shù)據(jù)線程  
//參數(shù):  
//      數(shù)據(jù)連接 conn  
//      通訊通道 messages  
//  
////////////////////////////////////////////////////////  
func Handler(conn net.Conn,messages chan string){  

    fmt.Println("connection is connected from ...",conn.RemoteAddr().String())  

    buf := make([]byte,1024)  
    for{  
        lenght, err := conn.Read(buf)  
        if(checkError(err,"Connection")==false){  
            conn.Close()  
            break  
        }  
        if lenght > 0{  
            buf[lenght]=0  
        }  
        //fmt.Println("Rec[",conn.RemoteAddr().String(),"] Say :" ,string(buf[0:lenght]))  
        reciveStr :=string(buf[0:lenght])  
        messages <- reciveStr  

    }  

}  

////////////////////////////////////////////////////////  
//  
//服務(wù)器發(fā)送數(shù)據(jù)的線程  
//  
//參數(shù)  
//      連接字典 conns  
//      數(shù)據(jù)通道 messages  
//  
////////////////////////////////////////////////////////  
func echoHandler(conns *map[string]net.Conn,messages chan string){  

    for{  
        msg:= <- messages  
        fmt.Println(msg)  

        for key,value := range *conns {  

            fmt.Println("connection is connected from ...",key)  
            _,err :=value.Write([]byte(msg))  
            if(err != nil){  
                fmt.Println(err.Error())  
                delete(*conns,key)  
            }  

        }  
    }  

}  

////////////////////////////////////////////////////////  
//  
//啟動服務(wù)器  
//參數(shù)  
//  端口 port  
//  
////////////////////////////////////////////////////////  
func StartServer(port string){  
        service:=":"+port //strconv.Itoa(port);  
        tcpAddr, err := net.ResolveTCPAddr("tcp4", service)  
        checkError(err,"ResolveTCPAddr")  
        l,err := net.ListenTCP("tcp",tcpAddr)  
        checkError(err,"ListenTCP")  
        conns:=make(map[string]net.Conn)  
        messages := make(chan string,10)  
        //啟動服務(wù)器廣播線程  
        go echoHandler(&conns,messages)  

        for  {  
            fmt.Println("Listening ...")  
            conn,err := l.Accept()  
            checkError(err,"Accept")  
            fmt.Println("Accepting ...")  
            conns[conn.RemoteAddr().String()]=conn  
            //啟動一個新線程  
            go Handler(conn,messages)   

        }  

}  

////////////////////////////////////////////////////////  
//  
//客戶端發(fā)送線程  
//參數(shù)  
//      發(fā)送連接 conn  
//  
////////////////////////////////////////////////////////  
func chatSend(conn net.Conn){  

    var input string  
    username := conn.LocalAddr().String()  
    for {  

        fmt.Scanln(&input)  
        if input == "/quit"{  
            fmt.Println("ByeBye..")  
            conn.Close()  
            os.Exit(0);  
        }  

        lens,err :=conn.Write([]byte(username + " Say :::" + input))  
        fmt.Println(lens)  
        if(err != nil){  
            fmt.Println(err.Error())  
            conn.Close()  
            break  
        }  

    }  

}  

////////////////////////////////////////////////////////  
//  
//客戶端啟動函數(shù)  
//參數(shù)  
//      遠(yuǎn)程ip地址和端口 tcpaddr  
//  
////////////////////////////////////////////////////////  
func StartClient(tcpaddr string){  

    tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpaddr)  
    checkError(err,"ResolveTCPAddr")  
    conn, err := net.DialTCP("tcp", nil, tcpAddr)  
    checkError(err,"DialTCP")  
    //啟動客戶端發(fā)送線程  
    go chatSend(conn)     

    //開始客戶端輪訓(xùn)  
    buf := make([]byte,1024)  
    for{  

        lenght, err := conn.Read(buf)  
        if(checkError(err,"Connection")==false){  
            conn.Close()  
            fmt.Println("Server is dead ...ByeBye")  
            os.Exit(0)  
        }  
        fmt.Println(string(buf[0:lenght]))  

    }  
}  

////////////////////////////////////////////////////////  
//  
//主程序  
//  
//參數(shù)說明:  
//  啟動服務(wù)器端:  Chat server [port]             eg: Chat server 9090  
//  啟動客戶端:    Chat client [Server Ip Addr]:[Server Port]    eg: Chat client 192.168.0.74:9090  
//  
////////////////////////////////////////////////////////  
func main(){  

    if len(os.Args)!=3  {     
        fmt.Println("Wrong pare")  
        os.Exit(0)  
    }  

    if os.Args[1]=="server" && len(os.Args)==3 {  

        StartServer(os.Args[2])  
    }  

    if os.Args[1]=="client" && len(os.Args)==3 {  

        StartClient(os.Args[2])  
    }  

}  

主要知識點如下:

(1)代碼中包括了服務(wù)器和客戶端的內(nèi)容,如果是服務(wù)器,直接輸入./chat server 9090即可,客戶端也很簡單,輸入./chat client :9090就好;

(2)如果是客戶端,其實就包括了兩部分內(nèi)容,一部分是 chatSend 函數(shù),接受用戶的輸入;另一部分是connect 到 server,接受相關(guān)信息;

(3)如果是 server,稍微復(fù)雜一點,有三個部分組成。第一部分就是不停地 accept 各個客戶端;第二個就是為每一個客戶端創(chuàng)立 Handler 函數(shù),接受客戶端發(fā)來的信息;第三個就是 echoHandler 函數(shù),它的作用就是將從某一用戶接受過來的信息廣播給其他所有的客戶端,就是這么簡單。

上一篇:Go 語言簡介下一篇:高級應(yīng)用