Golang网络编程——Demo

1. Demo背景

功能展示:

  • Server:
    在这里插入图片描述

  • Client:
    在这里插入图片描述

网络聊天室Demo简介:

  • Demo简介:
    • Golang实现UDP-Server
    • Python实现Client
      • Server实现功能:用户上下线,消息的转发,心跳检测
      • CLient实现功能:基本聊天,用户下线

技术选型简介:

  • 技术选型:

    Golang使用包:

基本网络协议包net
定时器包(用于心跳检测)time

Python使用包:

多线程_thread
GUIPyQT5
定时任务QTimer
  • 运行环境:
    Windows10
    Golang版本14.4
    Python版本 3.7

2. 源码:

  • Server:
package main

import (
	"fmt"
	"net"
	"time"

)

type UDPAddr struct {
	IP   []byte
	Port int
}

var UDPServerLog = `                                                                 
 _    _ _____  _____     _____                          
| |  | |  __ \|  __ \   / ____|                         
| |  | | |  | | |__) | | (___   ___ _ ____   _____ _ __ 
| |  | | |  | |  ___/   \___ \ / _ \ '__\ \ / / _ \ '__|
| |__| | |__| | |       ____) |  __/ |   \ V /  __/ |   
 \____/|_____/|_|      |_____/ \___|_|    \_/ \___|_|   
`

var Ip_port_Map = make(map[string]UDPAddr)
var heartbeat = ""

func main() {
	fmt.Println(UDPServerLog)
	// 1. 设置接收数据IP端口
	listen, err := net.ListenUDP("udp", &net.UDPAddr{
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 8977,
	})
	fmt.Println("[Server Start] IP: ", listen.LocalAddr())
	if err != nil {
		fmt.Println("listen failed, err:", err)
		return
	}
	defer listen.Close()

	// 1.1 心跳检测
	go func() {
		for {
			timers_heartbeat := time.NewTimer(time.Minute)
			<-timers_heartbeat.C
			fmt.Println("发送心跳包:", Ip_port_Map)
			if len(Ip_port_Map) != 0 {
				for k , v := range Ip_port_Map {
					data := k + ":" + "AreYouOk"
					var addra *net.UDPAddr
					addra = &net.UDPAddr{IP: v.IP, Port: v.Port}
					_, err = listen.WriteToUDP([]byte(data), addra)
					if err != nil {
						fmt.Println("Write to udp failed, err: ", err)
					}
					// 休眠等待接收包
					time.Sleep(500000000)
					if heartbeat != string(data[:6]) {
						delete(Ip_port_Map, string(data[:6]))
						fmt.Println(heartbeat, "用户不在线")
					}
				}
			}
			timers_heartbeat.Reset(1*time.Second)
		}
	}()

	// 主业务
	for {
		var data [128]byte
		// 2. 接收数据
		n, addr, err := listen.ReadFromUDP(data[:])
		if err != nil {
			fmt.Println("read udp failed, err:", err)
			continue
		}

		// 3. 新上线用户加入Map及移除下线用户
		go func() {
			if string(data[7:13]) == "OnLine" {
				Ip_port_Map[string(data[:6])] = UDPAddr{IP:addr.IP, Port:addr.Port}
			} else if string(data[7:14]) == "OffLine" {
				delete(Ip_port_Map, string(data[:6]))
				fmt.Println("用户下线:", string(data[:6]))
			} else if string(data[7:11]) == "ImOK" {
				heartbeat = string(data[:6])
			}
		}()

		// 4. 收到数据发送OK
		go func() {
			data_OK := "OK"
			_, err = listen.WriteToUDP([]byte(data_OK), addr)
			if err != nil {
				fmt.Println("Write to udp failed, err: ", err)
			}
		}()

		// 5. 群发数据
		go func() {
			fmt.Println()
			fmt.Println("群发ing.....")
			for _ , v := range Ip_port_Map {
				var addra *net.UDPAddr
				addra = &net.UDPAddr{IP: v.IP, Port: v.Port}
				_, err = listen.WriteToUDP([]byte(data[:n]), addra)
				if err != nil {
					fmt.Println("Write to udp failed, err: ", err)
				}
			}
			fmt.Println("群发结束.....")
		}()


		fmt.Println("当前在线用户:",Ip_port_Map)
		fmt.Printf("数据:%v 地址:%v 字节长度:%v\n", string(data[:n]), addr, n)
	}
}
  • Client:
import sys, socket, random, _thread
from PyQt5.Qt import *
from PyQt5.QtWidgets import (QWidget, QPushButton, QLineEdit, QTextEdit,QLabel, QApplication)
from PyQt5.QtCore import QTimer


BUFSIZE = 128
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
ip_port = ('127.0.0.1', 8977)
# 1. 随机生成用户名
name = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz!@#$%&*123456789', 6))
print("用户名: " + name)
data = name + ":" + "OnLine"
string_data = ""
try:
    client.sendto(data.encode('utf-8'), ip_port)
    data, server_addr = client.recvfrom(BUFSIZE)
    if data.decode('utf-8') == "OK":
        print('服务器在线!', data.decode('utf-8'), server_addr)
except Exception:
    print('服务器离线请稍后尝试!')
    quit()


class Example(QWidget):
    def __init__(self):
        global string_data
        super().__init__()
        self.initUI()

    def initUI(self):
        # 输入框
        self.qle = QLineEdit(self)
        self.qle.move(170, 260)

        # 用户名显示
        self.qla = QLabel(self)
        self.qla.move(60, 10)
        self.qla.setText("User name: " + name)

        # 聊天数据显示
        self.lbl = QTextEdit(self)
        self.lbl.setReadOnly(True)
        self.lbl.setMinimumSize(485, 200)
        self.lbl.move(60, 40)

        # 发送消息按键
        redb = QPushButton('发送', self)
        redb.setCheckable(True)
        redb.move(200, 300)

        # 下线退出
        reclo = QPushButton('退出', self)
        reclo.setCheckable(True)
        reclo.move(380, 300)

        redb.clicked[bool].connect(self.onChanged)
        reclo.clicked[bool].connect(self.OffLine)

        self.setMaximumSize(600, 370)
        self.setMinimumSize(600, 370)
        self.setWindowTitle('UDP Client')
        self.setWindowFlags(Qt.WindowMinimizeButtonHint)
        self.setWindowIcon(QIcon('C:/Users/KAILIN/AppData/Local/Microsoft/Edge Beta/User Data/Default/Web Applications/_crx_ihmafllikibpmigkcoadcmckbfhibefp/Edge Feedback.ico'))
        self.show()
        # 初始化定时器
        self.timers = QTimer(self)
        self.timers.timeout.connect(self.Data_look)
        self.startTimer()

    def startTimer(self):
        self.timers.start(1000)  # 1000 单位是毫秒, 即1秒

    def Data_look(self):
        # 消息显示
        self.lbl.setText(string_data)
        self.lbl.adjustSize()
        # 滑动条显示最底下
        self.lbl.verticalScrollBar().setValue(self.lbl.verticalScrollBar().maximum())
        self.lbl.moveCursor(QTextCursor.End)

    def onChanged(self):
        if self.qle.text() == "":
            return
        # 消息拼接
        data = name + ":" + self.qle.text()
        try:
            # 发送数据
            client.sendto(data.encode('utf-8'), ip_port)
        except Exception:
            print('服务器离线请稍后尝试!')
        self.qle.clear()

    def OffLine(self):
        super(Example, self).close()
        data = name + ":" + "OffLine"
        try:
            client.sendto(data.encode('utf-8'), ip_port)
        except Exception:
            print("下线失败!")
            self.OffLine()

def Server_data(threadName):
    global string_data
    print("接收服务器数据")
    while True:
        # 6. 接收服务器数据
        data, server_addr = client.recvfrom(BUFSIZE)
        print(data.decode('utf-8')[7:])
        if data.decode('utf-8') == "OK":
            print('服务器接收到数据!', data.decode('utf-8'), server_addr)
        elif data.decode('utf-8')[7:] == "ImOK":
            continue
        elif data.decode('utf-8')[7:] == "AreYouOk":
            try:
                heartbeat_data = name+":"+"ImOK"
                client.sendto(heartbeat_data.encode('utf-8'), ip_port)
            except Exception:
                print("心跳包发送失败!")
            continue
        elif data.decode('utf-8')[6:7] == ":":
            print('接收到群数据!', data.decode('utf-8'), server_addr)
            string_data += data.decode('utf-8') + "\n"
        else:
            string_data += "消息发送失败! " + "\n"
            print('消息发送失败!')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    _thread.start_new_thread(Server_data, ("Server_data_Thread",))
    sys.exit(app.exec_())

client.close()


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值