介绍
网络通信:就是两个程序(我们称为客户端、服务端)通信方式。
网络通信协议:就是通信约定的规范协议(tcp/udp/websocket/http)。
Tcp: 传输控制协议(TCP,Transmission Control Protocol),是一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transport layer)通信协议,因为是面向连接的协议,数据像水流一样传输,会存在黏包问题。
场景:短信、聊天
Udp:用户数据报协议(UDP,User Datagram Protocol),不需要建立连接就能直接进行数据发送和接收,属于不可靠的、没有时序的通信,但是UDP协议的实时性比较好,通常用于视频直播相关领域。
场景:直播、广播
WebSocket:是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
HTTP:超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。
参考
Go语言基础可参看:《Go语言入门及技术指南》 https://blog.csdn.net/yan_dk/article/details/110557155
示例
示例(TCP)
服务端server/main.go
package main
import (
"bufio"
"fmt"
"net"
)
func main() {
fmt.Println("启动服务端 : tcp://127.0.0.1:3333")
// 1. 监听端口 tcp://0.0.0.0:3333 监听的网络主要以本机可用ip为主
listen, err := net.Listen("tcp", "127.0.0.1:3333")
if err != nil {
fmt.Println("err : ", err)
return // return 表示程序结束
}
for {
// 2. 接收客户向服务端建立的连接
conn, err := listen.Accept() // 可以与客户端建立连接 , 如果没有连接挂起阻塞状态
if err != nil {
fmt.Println("err : ", err)
return // return 表示程序结束
}
// 3. 处理用户的连接信息
go handler(conn)
}
}
// 处理用户的连接信息
func handler(c net.Conn) {
defer c.Close() // 一定要写 ,关闭连接
for {
var data [1024]byte // 数组 - 》定义每一次数据读取的量
// Read(p []byte) 需要采用切片接收
n, err := bufio.NewReader(c).Read(data[:])
if err != nil {
fmt.Println("server-err : ", err)
break
}
fmt.Println("n", string(data[:n]))
// Write(b []byte) (n int, err error)
c.Write([]byte("hello world i'm is server"))
}
}
客户端client/main.go
package main
import (
"fmt"
"net"
)
// tcp客户端
func main() {
// 1. 创建建立连接
conn, _ := net.Dial("tcp", "127.0.0.1:3333")
fmt.Println("与tcp://127.0.0.1:3333建立连接")
defer conn.Close()
// 2. 进行数据的发送&接收数据
conn.Write([]byte("你好 server"))
var data [1024]byte
n, _ := conn.Read(data[:])
fmt.Println("client-n : ", string(data[:n])) // 切片获取信息
// 3. 关闭 // 不关闭不会造成太大 ,如果服务端没有心跳会存在问题
}
启动运行
服务端
客户端
示例(UDP)
服务端server/main.go
package main
import (
"fmt"
"net"
)
func main() {
// 监听地址 : 注意,传递net.UDPAddr对象
fmt.Println("启动udp://127.0.0.1:5555")
listen, _ := net.ListenUDP("udp", &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 5555,
})
defer listen.Close()
for {
// 接收信息
var data [1024]byte
n, addr, err := listen.ReadFromUDP(data[:]) // 接收数据
fmt.Printf("udp_server-data : %v,来源地址:%v", string(data[:n]),*addr)
if err != nil {
continue
}
// 发送信息
listen.WriteToUDP([]byte("i'm is udp"), addr)
}
}
客户端client/main.go
package main
import (
"fmt"
"net"
)
func main() {
socket, _ := net.DialUDP("udp", nil, &net.UDPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 5555,
})
defer socket.Close()
// 发送信息
socket.Write([]byte("hello upd server"))
// 读取数据
var data [1024]byte
n, addr, _ := socket.ReadFromUDP(data[:]) // 接收数据
fmt.Println("udp_client-data : ", string(data[:n]), addr)
}
启动运行
服务端
客户端
示例(Http)
服务端server/main.go
package main
import (
"fmt"
"net/http"
)
func main() {
// http.Handle("/foo", fooHandler)
fmt.Println("启动http://127.0.0.1:8888")
// http://127.0.0.1:8888/bar
http.HandleFunc("/bar", fooHandler) // route
// http.ListenAndServeTLS("") // https
http.ListenAndServe(":8888", nil) // http
}
// 要求传递的方法的类型 func(http.ResponseWriter, *http.Request)
// fooHandler(w http.ResponseWriter, r *http.Request)
func fooHandler(w http.ResponseWriter, r *http.Request) {
// 处理逻辑事项的方法
fmt.Println("处理逻辑")
fmt.Println("得到连接", r.RemoteAddr)
fmt.Println("url", r.URL.Path)
fmt.Println("method", r.Method)
w.Write([]byte("http-server 666"))
}
客户端client/main.go
package main
import (
"fmt"
"net/http"
)
func main() {
resp, _ := http.Get("http://127.0.0.1:8888/bar")
fmt.Println("resp", resp)
var data [1024]byte // [:] data := make([]byte, 1024)
n, _ := resp.Body.Read(data[:])
fmt.Println("信息 : ", string(data[:n]))
}
启动运行
服务端
客户端
示例(Websocket)
需要下载golang的websocket包支持
# go get golang.org/x/net
如果被墙,可设置go proxy代理,或git手动下载
# git clone https://github.com/golang/net.git
服务端server/main.go
package main
import (
"fmt"
"net/http"
"golang.org/x/net/websocket"
)
func main() {
fmt.Println("启动websocket://:7777")
// 设定websocket的服务信息处理
http.Handle("/", websocket.Handler(server))
// 设定监听
http.ListenAndServe(":7777", nil)
}
// 当有连接进来的时候底层会自动
func server(ws *websocket.Conn) {
fmt.Println("new connection")
data := make([]byte, 1024)
for {
// 读取信息
d, err := ws.Read(data)
if err != nil {
fmt.Println("err : ", err)
break
}
fmt.Println("读取到的信息 ", d)
ws.Write([]byte("你好I'm is webscoket server"))
}
}
客户端client/index.html
客户端需要一些资源文件img,css,js等
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTML5模拟微信聊天界面</title>
<link rel="stylesheet" href="css/style.css">
<script src="js/jquery.min.js" charset="utf-8"></script>
<script>
window.onload = function(){
var arrIcon = ['img/1.jpg','img/2.jpg'];
var num = 0; //控制头像改变
var iNow = -1; //用来累加改变左右浮动
var icon = document.getElementById('user_face_icon').getElementsByTagName('img');
var btn = document.getElementById('btn');
var text = document.getElementById('text');
var content = document.getElementsByTagName('ul')[0];
var img = content.getElementsByTagName('img');
var span = content.getElementsByTagName('span');
btn.onclick = function(){
if(text.value ==''){
alert('不能发送空消息');
}else {
content.innerHTML += '<li><img src="'+arrIcon[0]+'"><span>'+text.value+'</span></li>';
iNow++;
if(num==0){
img[iNow].className += 'imgright';
span[iNow].className += 'spanright';
} else {
img[iNow].className += 'imgleft';
span[iNow].className += 'spanleft';
}
webSocket.send(`{"method":"chat","msg":"${text.value}"}`);
text.value = '';
// 内容过多时,将滚动条放置到最底端
content.scrollTop=content.scrollHeight;
}
}
var webSocket = new WebSocket('ws://127.0.0.1:7777');
webSocket.onerror = function(event) {
console.log("error" + event.data);
};
// 打开websocket
webSocket.onopen = function(event) {
console.log("open:" + sockState());
};
//监听消息
webSocket.onmessage = function(event) {
console.log("onMessage");
// var data = eval('(' + event.data + ')');
var data = event.data
content.innerHTML += '<li><img src="'+arrIcon[1]+'"><span>'+data+'</span></li>';
iNow++;
img[iNow].className += 'imgleft';
span[iNow].className += 'imgleft';
text.value = '';
// 内容过多时,将滚动条放置到最底端
content.scrollTop=content.scrollHeight;
};
webSocket.onclose = function(event) {
document.getElementById("message").innerHTML = "<p>close</p>";
console.log("close:" + sockState());
webSocket.close();
}
function sockState() {
var status = ['未连接', '连接成功,可通讯', '正在关闭', '连接已关闭或无法打开'];
return status[webSocket.readyState];
}
}
</script>
</head>
<body>
<div id="container">
<div class="header">
<span style="float: left;">微信聊天界面</span>
<span style="float: right;">14:21</span>
</div>
<ul class="content">
</ul>
<div class="footer">
<div id="user_face_icon">
<img src="img/1.jpg" alt="">
</div>
<input id="text" type="text" placeholder="说点什么吧...">
<span id="btn">发送</span>
</div>
</div>
</body>
</html>
启动运行
服务端
客户端
websocket应用构建成功。