Go实战项目(1)在线聊天室

本文原地址
欢迎大家访问作者个人网站

本文主要展示利用golang实现一个简单在线聊天室的过程,使用的主要技术为socket编程。项目通过搭建一个简单的socket服务端,在不同客户端间实现通信。具体的效果如下所示,当某一个窗口发送信息时,其它窗口都会接收到相关信息,即实现广播功能。

相关示例代码参见simplechatroom。

一、Socket简介

Socket编程在我们日常工作生活中十分常见,甚至可以说Socket是现代网络编程的基础。那么什么是Socket呢?

其实Socket就是一种文件描述符。通过这个描述符,用户可以实现“读/写”操作,以此为基础,种类繁多的互联网应用应运而生,如视频直播、在线游戏等等。

常见的Socket描述符形式非常简单,就是IP+端口,例如127.0.0.1:9999。

使用Socket套接字的大体流程可以分为两个步骤:一是找到需要访问的地址(如127.0.0.1:9999);二是建立一个缓冲区传送数据

下文将结合Go提供的函数来介绍如何实现一个在线聊天室。

二、Go 语言提供的net库

Go为用户提供了简单而强大的网络编程开发包“net”。用户可以利用net包以及Go的协程机制(goroutine),轻易的实现多并发服务。

net包提供的函数较多,但是大部分使用者只需要Dial、Listen和Accept函数提供的基本接口;以及相关的Conn和Listener接口

Dial函数和服务端建立连接:

conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
     
	// handle error
}
fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
status, err := bufio.NewReader(conn).ReadString('\n')
// ...

Listen函数创建的服务端:

ln, err := net.Listen("tcp", ":8080")
if err != nil {
     
	// handle error
}
for {
     
	conn, err := ln.Accept()
	if err != nil {
     
		// handle error
		continue
	}
	go handleConnection(conn)
}

三、实现思路

一个在线聊天室的最关键的部分是是通信广播功能模块。

利用Socket实现这一功能需要注意以下几点:一是新用户的注册;二是消息分发;三是下线用户注销。

所谓用户,实际上就是一个套接字Socket。

可以通过将新进用户的套接字添加到一个数组里来完成用户注册功能。

消息分发可以分为两个步骤,第一个步骤是客户端将请求发送到服务端,第二个步骤是服务端程序在读取信息后,再根据注册用户数组进行数据分发。

可以通过监听套接字是否为空来判断用户下线。

四、具体实现

具体实现时可以暂时不考虑广播功能,先实现一个客户端与一个服务器之间的通信。

以下代码参考了Socket编程

服务端代码:

package main
import (
	"fmt"
	"net"
	"os"
	"time"
)
func main() {
     
	service := ":7777"
	tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
	checkError(err)
	listener, err := net.ListenTCP("tcp", tcpAddr)
	checkError(err)
	for {
     
		conn, err := listener.Accept()
		if err != nil {
     
			continue
		}
		daytime := time.Now().String()
		conn.Write([]byte(daytime)) // don't care about return value
		conn.Close()                // we're finished with this client
	}
}func checkError(err error) {
     
	if err != nil {
     
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		os.Exit(1)
	}
}

客户端代码:

package main
import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
)
func main() {
     
	if len(os.Args) != 2 {
     
		fmt.Fprintf(os.Stderr, "Usage: %s host:port ", os.Args[0])
		os.Exit(1)
	}
	service := os.Args[1]
	tcpAddr, err := net.ResolveTCPAddr("tcp4", service)
	checkError(err)
	conn, err := net.DialTCP("tcp", nil, tcpAddr)
	checkError(err)
	_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))
	checkError(err)
	// result, err := ioutil.ReadAll(conn)
	result := make([]byte, 256)
	_, err = conn.Read(result)
	checkError(err)
	fmt.Println(string(result))
	os.Exit(0)
}func checkError(err error) {
     
	if err != nil {
     
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		os.Exit(1)
	}
}

实现了相关功能后,用户可以通过go关键字实现多并发功能。

广播功能可以借助构建一个连接数组(如connSlice实现),关键代码如下所示:

if read_len == 0 || conn == nil {
     
			for i := 0; i < len(connSlice); i++ {
     
				if connSlice[i] == conn {
     
					connSlice = append(connSlice[:i], connSlice[i+1:]...)
					break
				}
			}

		} else {
     
			for i := 0; i < len(connSlice); i++ {
     
				if connSlice[i] == conn {
     
					continue
				} else {
     
					_, err := connSlice[i].Write([]byte(conn.RemoteAddr().String() + ":  " + string(request[:read_len])))
					checkError(err)
				}
			}

五、代码展示

完整代码获取地址simplechatroom。

你可能感兴趣的:(golang实战项目,golang实战项目)