服务器接收客户端消息|学习笔记

简介: 快速学习服务器接收客户端消息

开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:服务器接收客户端消息】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/626/detail/9780


服务器接收客户端消息

 

内容介绍

一、开发的难度

二、客户端功能

三、客户端代码

四、修改服务端代码

 

一、开发的难度

写代码时可能写了一段服务器代码然后写客户端代码测试,然后在写服务器代码客户端代码测试这样的开发模式,但是如果服务器端和客户端不是同一个类型那就没有办法,只能自己写模拟程序,不能完全不测试,因为服务器代码很多,必须依赖客户端的测试才能成功。所以开发难度比单机版难。

 

二、客户端功能

1.续写一个客户端程序,能链接到服务器端的8888接口

2.客户端可以发送单行数据,然后退出

3.能通过终端输入数据(输入一行发送一行),发送给服务器端口

4.在终端输入 exit,表示退出程序

 

三、客户端代码

1.代码

package  main  

import(

"fmt""

"net"

func main(){

conn, err := net.Dial("tcp", "192.168.20.253:8888")

if err !=nil{

fmt. Println("client dial err=",err)

return

fmt.Println(  "conn  成功=",conn)

(1)Dial 函数

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')  

//...

(2)代码解释

google.com:,80"为对方服务器的 ip➕端口,因为一个程序一定是 ip➕端口才能区分究竟是与哪一个 ip进行沟通,一个 ip 地址可以跑几万个程序,所以不能只写 IP 不写端口。最后会返回conn连接,使两个连接兑上,就像打电话接通一样。bufioNewReader(conn).Readstring('\n')是想创建一个 reader,reader 可以接收键盘的输入,可以一行一行输,一个字符一个字符输入过慢

(3)查看连接

查看是否连上,服务器不可以关闭,新开一个

D:\go project\src\go_code\chapter18\tcpdemo\client>go run client.go

conn成功=&<<0xc04209a000>>

D:\go project\src\go_code\chapter18\tcpdemo\client>

显示等待客户端连接,表示成功

2.如何在服务器将IP地址显示出来?

在 server 中通过 conn,conn 中有一个方法是 RemoteAddr()Addr,返回的是 addr

type Addr interface {

Network()string//网络名

String()string  //字符串格式的地址  

addr 是一个接口,调 string 就可以得到字符串的地址

Server 中代码更改为fmt. Printf("Accept()conn. suc con=%V 客户端 ip=%v\n", conn,conn.RemoteAddr()  string()

重新连接看到显示客户端 ip=192.168.20.253:51741,客户端端口为51741,客户端端口是随机分配的。

 

四、修改服务端代码

接下来实现在服务端接收一句话并显示的功能

1.代码

func main() {

conn, err := net.Dial("tcp","192.168.20.253:8888")

if err != nil {

fmt.Println("client dial err=", err) return}

fmt.Println("conn 成功=",conn)

}

//功能一:客户端可以发送单行数据,然后就退出

reader := bufio.NewReader(os.stdin) //os.stdin 代表标准输入[终端]

//从终端读取一行用户输入,并准备发送给服务器

line, err := reader.Readstring("\n') if err != nil {

fmt.Println("readstring err=", err)

}

//再将line 发送给服务器

n,err := conn.write([]byte(line))

if err != nil {

fmt.Println("conn.write err=", err)

}

(1)os.stdin

Stdin、 Stdout 和 Stderr是指向标准输入、标准输出、标准错误输出的文件描述符。

var Args  []string

准确讲 stdin 指向的就是标准输入,标准输入指的就是终端,在底层中在终端中写东西也会落入到文件中去,另外一个是文件读取的,本质网络编程也是这样

(2)代码解释:

conn.write

//Write 方法可能会在超过某个固定时间限制后超时返回错误,该错误的 Timeout()方法返回真  

Write(b []byte)  (n int,err error)

客户端逻辑,先将号连上,再创建 new reader 再从键盘里得到一个输入发给 client,此时服务器未做接收

2.改 server

接收工作不要在主线写,在主线写会造成阻塞(有一个客户在向服务器发送数据时别的客户全部堵在这里)

package main

import(

"fmt'

"net"//做网络 socket 开发时,net 包含有我们需要所有的方法和函数  

"io"

func process(conn net.Conn) {

//这里我们循环的接收客户端发送的数据  

defer conn. close()//关闭 conn

for{

//创建一个新的切片

buf := make([]byte, 1024)

//conn.Read(buf)

//1.等待客户端通过conn发送信息

//2.如果客户端没有wrtie[发送],那么协程就阻塞在这里

fmt.Printf("服务器在等待客户端%s发送信息\n",    conn . RemoteAddr().string())  

n, err := conn.Read(buf)//从conn读取

if  err  s= io.EoF {

fmt.Println("客户端退出")

return //!

//3.显示客户端发送的内容到服务器的终端

fmt.print(string(buf[:n]))

}

}

Func main(){

fmt.Println("服务器开始监听....")

//net.Listen("tcp","0.0.0.0:8888")

//1.tcp 表示使用网络协议是tcp

//2.0.0.0.0:8888表示在本地监听 8888端口

listen, err := net.Listen("tcp","0.0.0.0:8888")

if err != nil {

fmt.Println("listen err=",err)

Return

}

defer listen.close()//延时关闭 listen

//循环等待客户端来链接我

for {

//等待客户端链接

fmt.Println("等待客户端来链接....") conn, err := listen.Accept()

if err != nil {

fmt.Println("Accept()err=",err)

} else {

fmt.Printf("Accept()suc con=%v 客户端ip=%v\n”,conn,conn.RemoteAddr().string())

}

//这里准备其一个协程,为客户端服务

Go process(conn)

}

//fmt.Printf("listen suc=%v\n",listen)

协程在处理时关键命令是协程一定要拿到连接,defer conn.Close 这里连接用完一定要关闭,如果这个连接不关闭连接会越来越多,服务器会因为连接没有释放而别的连接无法连上

如果服务器正在等待发送连接突然出现一个客户端直接关闭,导致连接无法收到,一旦检测到连接断掉也会报错,协程就会退出

连接的维护:每隔一段时间由 tcp 协议发送包:你还在吗?客户端通过 ycp 发送:我还在,隔一段时间发送一次。此过程我们无法看到

3.跑代码

D:\go project\src\go_code\chapter18\tcpdemo\client>go run client.go

hello,world,sgg回车

不停在循环,意味着一旦发生错误应该在

if err != nil {

fmt.Print1n("服务器的Read err=",err)

return

后加 return,不退出就会一直等

再次运行,没有出现问题

hello,world,ABC

服务器在等待客户端192.168.20.253:52156发送信息

服务器的 Read err=read tcp 192.168.20.253:8888->192.168.20.253:52156:wsarecv:An existing connection was forcibly closed by the remote host.

再去等待时发现对方已经关闭所以出错,这个错误信息可以不打出来,定义一个对方已退出

type Error interface {

error

Timeout()  bool  //错误是否为超时?                      

Temporary () bool // 错误是否是临时的?

}

Error代 表一个网络错误。

Erro 返回的还是一段字符串

相关文章
|
监控 网络协议 安全
网络攻击的常见手段
网络攻击的常见手段
470 0
java minio 8.x 通过https连接minio
java minio 8.x 通过https连接minio
1710 0
|
Prometheus Kubernetes 监控
云原生|kubernetes |使用Prometheus监控k8s cAdvisor篇(进阶篇--- 一)(centos操作系统)
云原生|kubernetes |使用Prometheus监控k8s cAdvisor篇(进阶篇--- 一)(centos操作系统)
2760 0
|
Web App开发 安全 网络协议
Qt开发技术:QWebSocket客户端、服务端介绍与开发
Qt开发技术:QWebSocket客户端、服务端介绍与开发
Qt开发技术:QWebSocket客户端、服务端介绍与开发
|
调度 Android开发 开发者
【颠覆传统!】Kotlin协程魔法:解锁Android应用极速体验,带你领略多线程优化的无限魅力!
【8月更文挑战第12天】多线程对现代Android应用至关重要,能显著提升性能与体验。本文探讨Kotlin中的高效多线程实践。首先,理解主线程(UI线程)的角色,避免阻塞它。Kotlin协程作为轻量级线程,简化异步编程。示例展示了如何使用`kotlinx.coroutines`库创建协程,执行后台任务而不影响UI。此外,通过协程与Retrofit结合,实现了网络数据的异步加载,并安全地更新UI。协程不仅提高代码可读性,还能确保程序高效运行,不阻塞主线程,是构建高性能Android应用的关键。
179 4
详解Java中的protected修饰的访问权限
详解Java中的protected修饰的访问权限
|
分布式计算 DataWorks 安全
DataWorks产品使用合集之如何编辑字段安全等级
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
算法 JavaScript 数据可视化
基于leaflet-velocity的二维动态风场展示
本文讲解了leaflet-velocity插件,并利用插件进行了模拟的动态风场、洋流等信息的综合展示,让读者掌握集成方式。
1600 0
基于leaflet-velocity的二维动态风场展示
|
数据采集 存储 自然语言处理
使用Python分析网易云歌曲评论信息并可视化处理
在数字化时代,音乐与我们的生活紧密相连,而网易云音乐作为国内知名的音乐平台,拥有庞大的用户群体和丰富的歌曲评论信息。这些评论信息不仅反映了用户对于歌曲的情感态度,还蕴含着大量的有价值的数据。通过对这些评论信息进行分析和可视化处理,我们可以更好地理解用户的喜好、情感变化以及歌曲的影响力。
440 0
|
Rust 前端开发 JavaScript
用Rust搭建React Server Components 的Web服务器(二)
用Rust搭建React Server Components 的Web服务器(二)
219 0