golang-网络编程经典项目

经典项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


第一步:
在这里插入图片描述
main.go

// 2019-11-7 下午二点 系统index首页
package main

import (
	"fmt"
	"os"
)

var (
	userid int
	userpwd string
)

func main(){
   
	var loop = true
	var work int

	// 这里真是没想到,loop=true则为死循环,loop=false退出死循环
	for loop{
   
		fmt.Println("----------欢迎登录多人聊天系统----------")
		fmt.Println("			1 登录聊天系统")
		fmt.Println("			2 注册用户")
		fmt.Println("			3 退出系统")
		fmt.Println("请输入1-3选择服务")
		fmt.Scanln(&work)
		switch work{
   
		case 1:
			fmt.Println("---登录聊天系统---")
			loop = false
		case 2:
			fmt.Println("---注册用户---")
			loop = false
		case 3:
			fmt.Println("---退出系统---")
			// os.Exit可以用于退出当前程序
			os.Exit(0)
		default :
			fmt.Println("请输入正确选项1-3")
		}
	}

	if work == 1{
   
		fmt.Println("请输入用户id:")
		fmt.Scanf("%d\n",&userid)
		fmt.Println("请输入用户密码:")
		fmt.Scanf("%s",&userpwd)

		err := login(userid,userpwd)
		if err != nil{
   
			fmt.Println("登录失败")
		}else{
   
			fmt.Println("登录成功!")
		}

	}else if work != 1{
   
		fmt.Println("其他选项...")
	}
}

login.go

package main

import "fmt"

func login(userid int,userpwd string)(err error){
   
	fmt.Printf("userid=%v userpwd=%v",userpwd,userid)
	return nil
}

指定文件夹下go文件生成exe文件名:
此时编译client文件夹下的main.go文件生成的可执行文件就为client.exe

PS E:\software\go-path\goproject> go build -o client.exe .\src\gotcp\chatroom\client

疑惑:
这里我不甚理解为何要跑到src文件夹下去编译client文件夹下的main.go文件,然后main.go文件才能调用到同级文件夹下的函数
个人理解:
由于client文件夹下的go文件package声明都为main包,main包是声明主函数的如果同级有两个文件都声明为main包则会产生冲突,如果一个文件编译时候指定其他名字,则不会和另一个文件编译产生冲突


第二步:
实现完成用户登录功能
在这里插入图片描述
在这里插入图片描述
数据发送流程
client客户端:

  • 1.接收到输入的id和pwd
  • 2.发送id和密码
  • 3.接收到服务端返回的结果
  • 判断是成功还是失败,并显示对应页面

server服务端:

  • 1.接收用户id,pwd【goroutine】
  • 2.比较
  • 3.返回结果
    在这里插入图片描述
    1.完成客户端发送消息长度,服务端可以正常收到该长度值
    在这里插入图片描述

代码实现:
在这里插入图片描述
server.go

package main

import (
	"fmt"
	"net"
)

func process(conn net.Conn){
   
	defer conn.Close()
	// 循环读取
	for {
   
		fmt.Println("准备读取客户端传输数据...")
		buf := make([]byte,1024)
		n,err := conn.Read(buf[:4])
		if err != nil{
   
			fmt.Println("conn.Read err = ",err)
			return
		}
		fmt.Printf("读取到的数据buf=%v,读取到的长度=%v\n",buf[0:4],n)
	}
}

func main(){
   
	listener,err := net.Listen("tcp","0.0.0.0:8889")
	defer listener.Close()
	if err != nil{
   
		fmt.Println("net.Listener err = ",err)
		return
	}

	fmt.Printf("服务器网络类型为:%v 地址为:%v\n",listener.Addr().Network(),listener.Addr().String())

	for{
   
		// Accept返回listen服务端的连接并等待下一个连接
		conn,err := listener.Accept()
		defer conn.Close()
		if err != nil{
   
			fmt.Println("listener.Accept err = ",err)
			return
		}

		fmt.Println("客户端连接成功!ip地址为:",conn.RemoteAddr())
		go process(conn)
	}
}

login.go

package main

import (
	"fmt"
	"net"
	"gotcp/chatroom/common/message"
	"encoding/json"
	"encoding/binary"
)

func login(userid int,userpwd string)(err error){
   
	// 下一步制定协议
	conn,err := net.Dial("tcp","0.0.0.0:8889")
	defer conn.Close()

	if err != nil{
   
		fmt.Println("net.Dial err = ",err)
	}

	// 实例化一个消息struct并将消息类型放入并将用户信息序列化后放入
	var msg message.Message
	msg.Type = message.LoginMsgType

	// 实例化一个用户信息struct并将用户信息放入实例中并序列化
	var loginmsg message.LoginMsg
	loginmsg.UserId = userid
	loginmsg.UserPwd = userpwd

	// 序列化usermessage
	data,err := json.Marshal(loginmsg)
	if err != nil{
   
		fmt.Println("json.Marshal err = ",err)
		return
	}

	// 将loginmsg序列化后转为string赋值给msg.Data
	msg.Data = string(data)

	// 将msg.struct序列化
	data,err = json.Marshal(msg)
	if err != nil{
   
		fmt.Println("json.Marshal err = ",err)
		return
	}

	relay := uint32(len(data))
	var buf [4]byte
	/* Big-Endian就是高位字节排放在内存的低地址端,
	 低位字节排放在内存的高地址端 */
	 // 将uint32类型的长度转化为byte数组并按BigEndian排列于字节数组中
	 // 先获取到relay的长度再转为表示长度的切片
	binary.BigEndian.PutUint32(buf[:4],relay)
	n,err := conn.Write(buf[0:4])
	if err != nil || n != 4{
   
		fmt.Println("conn.Write err = ",err)
	}

	fmt.Printf("客户端发送消息长度为:%d 发送内容为:%v 发送完毕!\n",len(data),string(data))
	return
}

main.go

// 2019-11-7 下午二点 系统index首页
package main

import (
	"fmt"
	"os"
)

var (
	userid int
	userpwd string
)

func main(){
   
	var loop = true
	var work int

	// 这里真是没想到,loop=true则为死循环,loop=false退出死循环
	for loop{
   
		fmt.Println("----------欢迎登录多人聊天系统----------")
		fmt.Println("			1 登录聊天系统")
		fmt.Println("			2 注册用户")
		fmt.Println("			3 退出系统")
		fmt.Println("请输入1-3选择服务")
		fmt.Scanln(&work)
		switch work{
   
		case 1:
			fmt.Println("---登录聊天系统---")
			loop = false
		case 2:
			fmt.Println("---注册用户---")
			loop = false
		case 3:
			fmt.Println("---退出系统---")
			// os.Exit可以用于退出当前程序
			os.Exit(0)
		default :
			fmt.Println("请输入正确选项1-3")
		}
	}

	if work == 1{
   
		fmt.Println("请输入用户id:")
		fmt.Scanf("%d\n",&userid)
		fmt.Println("请输入用户密码:")
		fmt.Scanf("%s",&userpwd)

		err := login(userid,userpwd)
		if err != nil{
   
			fmt.Println("登录失败")
		}else{
   
			fmt.Println("登录成功!")
		}

	}else if work != 1{
   
		fmt.Println("其他选项...")
	}
}

message.go

package message

// 声明两个常量表示LoginMsg和LoginResMsg类型
const(
	LoginMsgType = "LoginMsg"
	LoginResMsgType = "LoginResMsg"
)

// Message和LoginMsg用户存放消息及给消息打tag
type Message struct{
   
	Type string `json:"type"` // 消息类型
	Data string  `json:"data"` // 消息内容
}

type LoginMsg struct{
   
	UserId int `json:"userId"` // 用户id
	UserPwd string `json:"userPwd"` // 用户密码
	UserName string `json:"userName"` // 用户名称
}

// LoginResMsg用来表示返回消息发送情况
type LoginResMsg struct{
   
	Code int `json:"
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值