客户端和服务器
//socket: BSD UNIX的进程通信机制,通常也称为“套接字”。来描述IP地址和端口。任何协议,HTTP,https,底层封装的都是socketsocket可以理解成tcp和upd网络通信API,定义很多函数,程序员可以用这些函数开发TCP/IP网络应用程序
处理流程
监听端口,mysql服务启动后开始监听3306端口
接收客户端的连接请求
处理连接,
当一个客户发送请求过来,我们要处理连接
所以我们要启动一个协程
客户端处理流程:
服务器不会主动连接客户端
客户端主动与服务器建立连接
向服务器接收或发送数据
之后关闭连接
服务端
启用socket
绑定端口
进入监听
等待客户端连接过来
客户端
启动socket
调用connect方法连接客户端
连接好后服务器写消息客户端来读
你一句我一句就搞起来了
TCP处理连接函数
tcp
服务器
package main
import (
"fmt"
"net"
)
//处理连接函数
func process(conn net.Conn) {
//结束时候关闭conn
defer conn.Close()
//接收发来的内容
for {
//接收1兆数据
var buf [1024]byte
//读取数据
n, err := conn.Read(buf[:])
if err != nil {
fmt.Println("read err:", err)
return
}
//将数据转换成字符串
str := string(buf[:n])
fmt.Println("接收到客户端发来的数据内容为:", str)
//conn.Write([]byte("消息已经收到"))
}
}
func main() {
//1,启动监听端口
listener, err := net.Listen("tcp", "127.0.0.1:7777")
if err != nil {
fmt.Println("listen failed,err:", err)
return
}
//方法退出时关闭连接,释放端口
defer listener.Close()
for {
//检测一次连接
conn, err := listener.Accept()
//如果检测不到继续检测
if err != nil {
fmt.Println("accept failed,err:", err)
continue
}
//启动一个协程处理客户端连接
go process(conn)
}
}
客户端
package main
import (
"bufio"
"fmt"
"net"
"os"
)
//客户端
func main() {
//连接服务器的服务。conn就是连接
conn, err := net.Dial("tcp", "127.0.0.1:7777")
if err != nil {
fmt.Println("连接服务器失败,err:", err)
return
}
//结束的时候把连接断开
defer conn.Close()
for {
//在控制台输入信息
reader := bufio.NewReader(os.Stdin)
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("获取控制台消息失败")
return
}
//给连接端发送一个字符串
_, err = conn.Write([]byte(input))
if err != nil {
fmt.Println("发送消息失败")
return
}
}
}
拨号,如果拨号不到就会报错
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
UDP服务器处理连接
package main
import (
"fmt"
"net"
)
func main() {
//127.0.0.1:8888端口接收UDP请求
listener, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 8888,
})
if err != nil {
fmt.Println("UDP server服务启动失败,err", err)
return
}
defer listener.Close()
for {
//设置个方法,接收1024位
var buf [1024]byte
//n代表长度
n, addr, err := listener.ReadFromUDP(buf[:])
if err != nil {
fmt.Println("接收数据失败,err:", err)
return
}
fmt.Println("接收到的消息", addr, string(buf[:n]))
//回复消息
listener.WriteToUDP([]byte("hi"), addr)
if err != nil {
fmt.Println("回复失败,err:", err)
return
}
}
}
客户端
package main
import (
"fmt"
"net"
)
func main() {
//发送udp给127.0.0.1:8888
conn, err := net.Dial("udp", "127.0.0.1:8888")
if err != nil {
fmt.Println("连接失败,err:", err)
return
}
defer conn.Close()
//给目标发一个hello udp
n, err := conn.Write([]byte("hello udp"))
if err != nil {
fmt.Println("发送失败,err:", err)
return
}
//接收消息,只接收1024字节
var buf [1024]byte
n, err = conn.Read(buf[:])
if err != nil {
fmt.Println("接收消息失败,err:", err)
return
}
fmt.Println("收到消息:", string(buf[:n]))
}
写个聊天软件
package main
func main() {
server := NewServer("0.0.0.0", 8888)
server.Start()
}
package main
import (
"fmt"
"io"
"net"
"sync"
)
//项目整体
type Server struct {
Ip string
Port int
//存放在线用户列表
OnlineMap map[string]*User
//创建读写锁,防止登录时列表出错
maplock sync.RWMutex
//用于消息广播的chan
Messsage chan string
}
//创建server的连接口
func NewServer(ip string, port int) *Server {
Server := &Server{
Ip: ip,
Port: port,
OnlineMap: make(map[string]*User),
Messsage: make(chan string),
}
return Server
}
//创建一个用于广播的方法,广播内容组装,并将组装好的内容存放入server的massagechan中
func (this *Server) BroadCast(user *User, msg string) {
//放到群发消息里,就能触发群发规则了
sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msg
this.Messsage <- sendMsg
}
//群发规则
//监听message channnel中的广播消息,一旦有消息需要广播,要将消息广播给所有在线user
func (this *Server) ListenMessage() {
for {
msg := <-this.Messsage
this.maplock.RLock()
//对所有人的C里发送消息
for _, cli := range this.OnlineMap {
cli.C <- msg
}
this.maplock.RUnlock()
}
}
//每个用户启动一个协程
func (this *Server) Handler(conn net.Conn) {
//当前连接业务
fmt.Println("建立连接成功", conn.RemoteAddr().String())
//每一个客户端连接成功后都会被创建一个user对象
user := NewUser(conn)
this.maplock.Lock()
//将用户存放如服务器的在线列表的数组中
this.OnlineMap[user.Name] = user
this.maplock.Unlock()
//新用户上线广播
this.BroadCast(user, "已上线")
//接受客户端发送消息
go func() {
//设置上限1024,创建一个管道
buf := make([]byte, 1024)
for {
//循环读取内容,会一直等待,断开时会读取0
//读取后的信息会放到n里
//buf表示读1024
n, err := conn.Read(buf)
if err != nil && err != io.EOF {
fmt.Println("Conn Read err:", err)
return
}
//如果用户字符串长度等于0就下线了
if n == 0 {
this.BroadCast(user, "下线了")
return
}
//接收客户端发来的消息。buf[:n-1]表示吧多余的字符串删除
msg := string(buf[:n-1])
//将用户发送的消息广播给所有用户
this.BroadCast(user, msg)
}
}()
}
//启动server方法
func (this *Server) Start() {
//开始监听tcp连接
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))
if err != nil {
fmt.Println("net.listen err:", err)
return
}
//在结束的时候关闭连接
defer listener.Close()
//开启监听端口
go this.ListenMessage()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener accept is err:", err)
continue
}
//启动一个协程连接客户端
go this.Handler(conn)
}
}
package main
import "net"
//每个TCP请求,如果成功后就会创建一个User客户端
type User struct {
Name string
Addr string
conn net.Conn
C chan string
}
//创建用户方法API
func NewUser(conn net.Conn) *User {
//conn.RemoteAddr()目标地址,String()转换成字符串
userAddr := conn.RemoteAddr().String()
user := &User{
Name: userAddr,
Addr: userAddr,
//自己的ip地址
conn: conn,
//消息队列
C: make(chan string),
}
go user.ListenMessage()
return user
}
//监听当前User C channel方法,一旦有消息,就发送给客户端
func (this *User) ListenMessage() {
for {
//如果C接收到信息则打印conn.Write([]byte(msg + "\n"))
msg := <-this.C
//打印到目标的终端上
this.conn.Write([]byte(msg + "\n"))
}
}
开始监听
开启服务器端口
listener, err := net.Listen("tcp", "127.0.0.1:8888")
服务器的地址和开放端口
等待别人接入
conn, err := listener.Accept()
等待直到有端口接入,conn记录的是端口
得到客户的IP和端口
userAddr := conn.RemoteAddr().String()
读取端口的IP和端口字符串
关闭监听
listener.Close()
listener是变量
对客户读写
写
conn.Write([]byte("字符串" + "\n"))
写给客户端
读
//从this.conn中读取,也就是从服务器读数据,并写到终端上
io.Copy(os.Stdout, this.conn)
//设置上限1024,创建一个管道
buf := make([]byte, 1024)
//读取客户终端信息,会一直等待,断开时会读取0
//读取后的信息会放到n里
//buf表示读1024,其他用空格补全
ok, err := conn.Read(buf)
//将空格都删除,如此得到读取的字符串
msg := string(buf[:ok-1])