1. 程式人生 > 實用技巧 >go實現聊天系統(三)

go實現聊天系統(三)

配置檔案【通訊協議】

package main
import (
	Common "common"
	"encoding/json"
	"fmt"
	"io"
	"net"
)
//處理登入
func serverProcessLogin(conn net.Conn, msg *Common.Message) (err error){
	var Loginmes Common.LoginMes;
	err=json.Unmarshal([]byte(msg.Data),&Loginmes);//引數必須是位元組切片後面必須是指標這個位置大坑
	if err != nil {
		fmt.Println("json.Unmarshal is eroor");
		return;
	}
	//宣告返回的結構體 & 組裝結構體
	var resmsg Common.Message;//總結構體
	resmsg.Type = Common.LoginResType;
	var loginresmsg Common.LoginResMes;//宣告返回結構體型別

	userId:=Loginmes.UserId;
	userName:=Loginmes.UserName;
	if userId == 100 && userName == "123456" {
		loginresmsg.Code=200;
		loginresmsg.Error="使用者登入";
	}else{
		loginresmsg.Code=500;
		loginresmsg.Error="登入失敗";
	}
	//組裝結構體
	data,err:=json.Marshal(loginresmsg);//返回一個位元組切片
	if err != nil {
		fmt.Println("loginresmsg is error");
		return;
	}
	resmsg.Data = string(data);
	data,err = json.Marshal(resmsg);
	if err != nil {
		fmt.Println("resmsg is error");
		return;
	}
	//下面傳送
	Common.WritePkg(conn,data);
	return;
}
//根據客戶端傳送的訊息型別進行處理
func serverProcessMes(conn net.Conn,msg *Common.Message) (err error){
	switch msg.Type {
		case Common.LoginMesType:
			serverProcessLogin(conn,msg);
		case Common.LoginResType:
		default:
			fmt.Println("訊息型別不對,無法處理");
			return ;
	}
	return;
}
func process(conn net.Conn){
	for {
		mes,err:= Common.ReadPkg(conn);
		if err != nil {
			if err == io.EOF {
				fmt.Println("客戶端退出,伺服器也退出");
				return;
			}else{
				fmt.Println("read pkg is error");
				return;
			}
		}
		fmt.Println(mes);
		err=serverProcessMes(conn,&mes);//傳送請求處理
		if err != nil {
			return;
		}
	}
	defer  conn.Close();
}
func main(){
	fmt.Println("伺服器在8889埠監聽....");
	listen,err:= net.Listen("tcp","0.0.0.0:8889");
	defer listen.Close();
	if err !=nil {
		fmt.Printf("net.Listen is error");
		return;
	}
	for {
		fmt.Println("等待客戶端的連線");
		conn,err := listen.Accept();
		if err != nil {
			fmt.Println("listen accept is err");
			return;
		}
		go process(conn);
	}
}

2.客戶端

package main
import "fmt"
var userId int;
var userPwd string;
func main(){
	var key int;
	var loop=true;
	for loop {
		fmt.Println("----------------------歡迎多人聊天系統----------------------");
		fmt.Println("\t\t\t 1:登入聊天室");
		fmt.Println("\t\t\t 2:註冊使用者");
		fmt.Println("\t\t\t 3:退出系統");
		fmt.Println("\t\t\t 請選擇(1-3):");
		fmt.Scanf("%d\n",&key);
		switch key {
			case 1:
				fmt.Println("登入聊天系統頁面");
				loop=false;
			case 2:
				fmt.Println("註冊使用者頁面");
				loop=false;
			case 3:
				fmt.Println("退出系統");
				loop=false;
			default:
				fmt.Println("你的輸入有誤,請重新輸入");
		}
	}
	if key == 1 {
		fmt.Println("請輸入使用者ID:");
		fmt.Scanf("%d\n",&userId);
		fmt.Println("請輸入使用者密碼:");
		fmt.Scanf("%s\n",&userPwd);
		login(userId,userPwd);
	}
}

 

package main

import (
	Common "common"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"net"
)
func login(userId int,userPwd string) (err error) {
	fmt.Printf("登入學生ID為%v 密碼為%v",userId,userPwd);
	//連線遠端伺服器
	conn,err:=net.Dial("tcp","localhost:8889");
	if err != nil {
		fmt.Println("net.Dial is error");
	}
	defer  conn.Close();

	var msg Common.Message;
	//1.設定訊息型別
	msg.Type=Common.LoginMesType;//設定登入結構體型別
	//2.建立訊息結構體
	var logMes Common.LoginMes;
	logMes.UserId=userId;
	logMes.UserName=userPwd;
	data,err:= json.Marshal(logMes);//將訊息內容序列化但是data是切片
	if err != nil {
		fmt.Println("json.Marshal is error");
		return;
	}
	msg.Data=string(data);
	//3.將全部訊息序列化
	data,err = json.Marshal(msg);//這是切片
	if err != nil {
		fmt.Println("json.Marshal is error");
		return;
	}
	//討論如何傳送
	pkgLen:= uint32(len(data));//uint32的數字
	var bytes [4]byte;
	//為了相容不同裝置之間需要傳送位元組序列
	binary.BigEndian.PutUint32(bytes[0:4],pkgLen);
	n,err:=conn.Write(bytes[0:4]);
	if n !=4 || err != nil {
		fmt.Println("conn.Write() is error");
		return;
	}
	//傳送訊息本身
	_,err=conn.Write(data);
	if err != nil {
		fmt.Println("conn.write(data) is fail");
		return;
	}
	msg,err=Common.ReadPkg(conn);
	if err !=nil {
		fmt.Println("readpagek is error");
		return;
	}
	var loginRestype Common.LoginResMes;
	json.Unmarshal([]byte(msg.Data),&loginRestype);
	if loginRestype.Code == 200 {
		fmt.Println("登入成功");
	}else{
		fmt.Println("login is fail");
	}
	return;
}

3.服務端

package main
import (
	Common "common"
	"encoding/json"
	"fmt"
	"io"
	"net"
)
//處理登入
func serverProcessLogin(conn net.Conn, msg *Common.Message) (err error){
	var Loginmes Common.LoginMes;
	err=json.Unmarshal([]byte(msg.Data),&Loginmes);//引數必須是位元組切片後面必須是指標這個位置大坑
	if err != nil {
		fmt.Println("json.Unmarshal is eroor");
		return;
	}
	//宣告返回的結構體 & 組裝結構體
	var resmsg Common.Message;//總結構體
	resmsg.Type = Common.LoginResType;
	var loginresmsg Common.LoginResMes;//宣告返回結構體型別

	userId:=Loginmes.UserId;
	userName:=Loginmes.UserName;
	if userId == 100 && userName == "123456" {
		loginresmsg.Code=200;
		loginresmsg.Error="使用者登入";
	}else{
		loginresmsg.Code=500;
		loginresmsg.Error="登入失敗";
	}
	//組裝結構體
	data,err:=json.Marshal(loginresmsg);//返回一個位元組切片
	if err != nil {
		fmt.Println("loginresmsg is error");
		return;
	}
	resmsg.Data = string(data);
	data,err = json.Marshal(resmsg);
	if err != nil {
		fmt.Println("resmsg is error");
		return;
	}
	//下面傳送
	Common.WritePkg(conn,data);
	return;
}
//根據客戶端傳送的訊息型別進行處理
func serverProcessMes(conn net.Conn,msg *Common.Message) (err error){
	switch msg.Type {
		case Common.LoginMesType:
			serverProcessLogin(conn,msg);
		case Common.LoginResType:
		default:
			fmt.Println("訊息型別不對,無法處理");
			return ;
	}
	return;
}
func process(conn net.Conn){
	for {
		mes,err:= Common.ReadPkg(conn);
		if err != nil {
			if err == io.EOF {
				fmt.Println("客戶端退出,伺服器也退出");
				return;
			}else{
				fmt.Println("read pkg is error");
				return;
			}
		}
		fmt.Println(mes);
		err=serverProcessMes(conn,&mes);//傳送請求處理
		if err != nil {
			return;
		}
	}
	defer  conn.Close();
}
func main(){
	fmt.Println("伺服器在8889埠監聽....");
	listen,err:= net.Listen("tcp","0.0.0.0:8889");
	defer listen.Close();
	if err !=nil {
		fmt.Printf("net.Listen is error");
		return;
	}
	for {
		fmt.Println("等待客戶端的連線");
		conn,err := listen.Accept();
		if err != nil {
			fmt.Println("listen accept is err");
			return;
		}
		go process(conn);
	}
}