简介
Socket,英文含义是【插座、插孔】,一般称之为套接字,用于描述IP地址和端口。可以实现不同程序间的数据通信。
Socket起源于Unix,而Unix基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现,网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
- 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
- 建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
- Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。插座是用来给插头提供一个接口让其通电的,此时我们就可以将插座当做一个服务端,不同的插头当做客户端。
服务端
package main
import (
"fmt"
"net"
"strings"
)
func main() {
listener ,err := net.Listen("tcp",":8000")
if err != nil {
fmt.Println("listen err :",err)
}
defer listener.Close()
fmt.Println("等待客户端连接...")
// 接收多个用户
for {
conn ,err := listener.Accept()
if err != nil {
fmt.Println("err=",err)
return
}
go HandleConn(conn)
}
/* conn, err := listener.Accept()
if err != nil {
fmt.Println("accept err:",err)
}
defer conn.Close()
fmt.Println("客户端与服务器连接建立成功...")
buf := make([]byte,1024)
n,err := conn.Read(buf)
if err != nil {
fmt.Println("read err: ",err)
}
_,err = conn.Write([]byte("收到了"))
if err != nil {
fmt.Println("收到了:",err)
}
fmt.Println("服务器读到数据:",string(buf[:n]))*/
}
func HandleConn(conn net.Conn) {
//函数调用完毕,自动关闭conn
defer conn.Close()
//获取客户端的网络地址信息
addr := conn.RemoteAddr().String()
fmt.Println(addr, " conncet sucessful")
buf := make([]byte, 2048)
//把数据转换为大写,再给用户发送 这个地方for 是为了客户端和服务端保持连接不中断
for {
//读取用户数据
n, err := conn.Read(buf)
if err != nil {
fmt.Println("err = ", err)
return
}
fmt.Printf("[%s]: %s\n", addr, string(buf[:n]))
fmt.Println("len = ", len(string(buf[:n])))
//if "exit" == string(buf[:n-1]) { // nc测试,发送时,只有 \n
if "exit" == string(buf[:n-1]) { // 自己写的客户端测试, 发送时,多了2个字符, "\r\n"
fmt.Println(addr, " exit")
return
}
fmt.Println("ok",string(buf[:n]))
conn.Write([]byte(strings.ToUpper(string(buf[:n]))))
}
}
客户端
package main
import (
"fmt"
"net"
"os"
)
func main() {
conn,err := net.Dial("tcp","127.0.0.1:8000")
if err != nil {
fmt.Println("Dial err:",err)
}
defer conn.Close()
// 启动子协程,接收用户键盘输入
go func() {
str := make([]byte, 1024) // 创建用于存储用户键盘输入数据的切片缓冲区。
for { // 反复读取
n, err :=os.Stdin.Read(str) // 获取用户键盘输入
if err != nil {
fmt.Println("os.Stdin.Read err:", err)
return
}
// 将从键盘读到的数据,发送给服务器
_, err = conn.Write(str[:n]) // 读多少,写多少
if err != nil {
fmt.Println("conn.Write err:", err)
return
}
}
}()
// 主协程,接收服务器回发数据,打印至屏幕
buf := make([]byte, 1024) // 定义用于存储服务器回发数据的切片缓冲区
for {
n, err := conn.Read(buf) // 从通信 socket 中读数据,存入切片缓冲区
if err != nil {
fmt.Println("conn.Read err:", err)
return
}
fmt.Printf("服务器回发:%s\n", string(buf[:n]))
}
/* n,err := conn.Write([]byte("are you ok"))
if err != nil {
fmt.Println("write err:",err)
}
fmt.Println(n)
buf := make([]byte,1024)
n,err = conn.Read(buf)
if err != nil {
fmt.Println("read err: ",err)
}
fmt.Println("客户端读到数据:",string(buf[:n]))*/
}