[+]文章目录

聊天室的开发

前期准备

  • 需要 import "net"包
  • IP 类型,其中一个重要的方法是 IP.ParseIP(ipaddr string)来判断是否是合法的 IP 地址

TCP Client

  • func (c *TCPConn) Write(b []byte) (n int, err os.Error)用于发送数据,返回发送的数据长度或者返回错误,是TCPConn的方法
  • func (c *TCPConn) Read(b []byte) (n int, err os.Error)用于接收数据,返回接收的长度或者返回错误,是 TCPConn 的方法
  • TCPAddr 类型,保存 TCP 的地址信息,包括地址和端口
 type TCPAddr struct {
      IP IP
      Port int
  }
  • func ResolveTCPAddr(net, addr string) (*TCPAddr, os.Error)获取一个 TCPAddr,参数都是 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)到远程服务器上,net 表示协议方式,tcp,tcp4 或者 tcp6,laddr 表示本机地址,一般为 nil,raddr 表示远程地址,这里的 laddr 和 raddr 都是 TCPAddr 类型的,一般是上一个函数的返回值。
  • 作为一个 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)用来监听端口,net 表示协议类型,laddr 表示本机地址,是 TCPAddr 类型,注意,此处的 laddr 包括端口,返回一个*TCPListener类型或者错误
  • func (l *TCPListener) Accept() (c Conn, err os.Error)用来返回一个新的连接,进行后续操作,这是 TCPListener 的方法,一般 TCPListener 从上一个函数返回得来。

  • 服务器的基本操作流程为:
  service:=":9090"
  tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
  l,err := net.ListenTCP("tcp",tcpAddr)
  conn,err := l.Accept()
  go Handler(conn) //此处使用go关键字新建线程处理连接,实现并发

实现一个公共聊天服务器。

  • 服务器接收客户端的信息
  • 接收完以后将客户端的信息发送到所有的客户端上
  • 客户端使用/quit退出聊天
  • 只使用一套代码,通过命令行参数启动服务器还是客户端

实现:

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  
}  

////////////////////////////////////////////////////////  
//  
//服务器端接收数据线程  
//参数:  
//      数据连接 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  

    }  

}  

////////////////////////////////////////////////////////  
//  
//服务器发送数据的线程  
//  
//参数  
//      连接字典 conns  
//      数据通道 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)  
            }  

        }  
    }  

}  

////////////////////////////////////////////////////////  
//  
//启动服务器  
//参数  
//  端口 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)  
        //启动服务器广播线程  
        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)   

        }  

}  

////////////////////////////////////////////////////////  
//  
//客户端发送线程  
//参数  
//      发送连接 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  
        }  

    }  

}  

////////////////////////////////////////////////////////  
//  
//客户端启动函数  
//参数  
//      远程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")  
    //启动客户端发送线程  
    go chatSend(conn)     

    //开始客户端轮训  
    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]))  

    }  
}  

////////////////////////////////////////////////////////  
//  
//主程序  
//  
//参数说明:  
//  启动服务器端:  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)代码中包括了服务器和客户端的内容,如果是服务器,直接输入./chat server 9090即可,客户端也很简单,输入./chat client :9090就好;

(2)如果是客户端,其实就包括了两部分内容,一部分是 chatSend 函数,接受用户的输入;另一部分是connect 到 server,接受相关信息;

(3)如果是 server,稍微复杂一点,有三个部分组成。第一部分就是不停地 accept 各个客户端;第二个就是为每一个客户端创立 Handler 函数,接受客户端发来的信息;第三个就是 echoHandler 函数,它的作用就是将从某一用户接受过来的信息广播给其他所有的客户端,就是这么简单。


« 前一篇