在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。

常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

套接字通讯原理示意

TCP的C/S架构

在整个通信过程中,服务器端有两个socket参与进来,但用于通信的只有conn这个socket。它是由 listener创建的。隶属于服务器端。客户端有一个socket参与进来。

net.Listen() 建立一个用于连接监听的套接字

listen.Accept() // 阻塞监听客户端连接请求,成功用于连接,返回用于通信的socket

net.Dial() 客户端向服务端发起连接建立一个socket连接

并发的C/S模型通信

Server

Accept()函数的作用是等待客户端的链接,如果客户端没有链接,该方法会阻塞。如果有客户端链接,那么该方法返回一个Socket负责与客户端进行通信。所以,每来一个客户端,该方法就应该返回一个Socket与其通信,因此,可以使用一个死循环,将Accept()调用过程包裹起来。

需要注意,实现并发处理多个客户端数据的服务器,就需要针对每一个客户端连接,单独产生一个Socket,并创建一个单独的goroutine与之完成通信。

package main

import (
"fmt"
"net"
"strings"
) func handleConnect(conn net.Conn){
var (
b []byte
err error
n int
)
fmt.Println(conn.RemoteAddr(),"建立连接.")
defer conn.Close()
b = make([]byte,4096)
// 客户端可能持续不断的发送数据,因此接收数据的过程可以放在for循环中,服务端也持续不断的向客户端返回处理后的数据。
for {
n,err = conn.Read(b) content := strings.Trim(string(b[:n]),"\r\n") // window中传送的内容存在换行符,作为判断时需要删除
// 当客户端退出,服务端从chan中读取内容时是没有的,因此的到0 或者客户端主动退出输入exit或者quit
if n == 0 || content == "exit" || content == "quit" {
fmt.Println("客户端退出:",conn.RemoteAddr())
return
} if err != nil {
fmt.Println(err)
return
} if _,err = conn.Write([]byte(fmt.Sprintf("server reply:%s",b[:n])));err !=nil {
fmt.Println(err)
return
}
fmt.Println("client send: ",content)
}
} func main() {
var (
listener net.Listener
err error
conn net.Conn
)
// 建立一个用于连接监听的套接字
if listener, err = net.Listen("tcp", "10.0.0.1:8088"); err != nil {
fmt.Println(err)
return
}
defer listener.Close() fmt.Println("waiting client connect.") // 阻塞监听客户端连接请求,成功用于连接,返回用于通信的socket
for {
if conn, err = listener.Accept(); err != nil {
fmt.Println(err)
return
} go handleConnect(conn)
}
}

使用nc作为客户端向服务端发送信息

自定义客户端

客户端需要持续的向服务端发送数据,同时也要接收从服务端返回的数据。因此可将发送和接收放到不同的协程中。

  • 主协程循环接收服务器回发的数据(该数据应已转换为大写),并打印至屏幕;
  • 子协程循环从键盘读取用户输入数据。
  • 读取键盘输入可使用 os.Stdin.Read()

注意事项:

  • 服务端有对 exit返回的是 io.EOF
  • 当服务端断开时,chan读取的信息就为0了即服务端已经退出,如果客户端不退出会一直报错
package main

import (
"fmt"
"io"
"net"
"os"
"strings"
) func main() { var (
conn net.Conn
err error
n int
) if conn, err = net.Dial("tcp", "10.0.0.1:8088"); err != nil {
fmt.Println(err, 111)
return
}
defer conn.Close() go func() {
str := make([]byte, 1024)
for {
n, err := os.Stdin.Read(str)
content := strings.ToLower(strings.Trim(string(str[:n]), "\r\n")) if n == 0 {
fmt.Println("与服务端断开连接")
return
} if err == io.EOF || content == "quit" {
return
} if err != nil {
fmt.Println(1, err)
continue
} _, err = conn.Write([]byte(content))
if err != nil {
fmt.Println(111, err)
return
} }
}() byt := make([]byte, 1024)
for {
if _, err = conn.Read(byt); err != nil {
if err == io.EOF {
return
}
fmt.Println(err)
continue
}
fmt.Println("server reply:", string(byt[:n]))
}
}

golang:TCP总结的更多相关文章

  1. Golang TCP转发到指定地址

    Golang TCP转发到指定地址 第二个版本,设置指定ip地址 代码 // tcpForward package main import ( "fmt" "net&qu ...

  2. Golang tcp转发 remoteAddr错误

    Golang tcp 转发 第一版本 accept获取的Conn里的localAddr做为源地址,remoteAddr来做为目的地址 // tcpForward package main import ...

  3. 6行代码解决golang TCP粘包

    转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用golang的bufio.Scanner来实现 ...

  4. Golang Tcp粘包处理(转)

    在用golang开发人工客服系统的时候碰到了粘包问题,那么什么是粘包呢?例如我们和客户端约定数据交互格式是一个json格式的字符串: {"Id":1,"Name" ...

  5. golang tcp keepalive实践

    前文中已经介绍了TCP keep alive的做了详尽说明,本文结合golang,介绍如何使用TCP keep alive. 目前golang net包不提供TCP keep alive 空闲多长时间 ...

  6. 【GoLang】golang TCP 粘包处理 示例

    参考资料: http://www.01happy.com/golang-tcp-socket-adhere/

  7. <转>Go语言TCP Socket编程

    授权转载: Tony Bai 原文连接: https://tonybai.com/2015/11/17/tcp-programming-in-golang/ Golang的主要 设计目标之一就是面向大 ...

  8. Go语言学习之9 网络协议TCP、Redis与聊天室

    主要内容 1. Tcp编程2. redis使用 1. Tcp编程 (1)简介       Golang是谷歌设计开发的语言,在Golang的设计之初就把高并发的性能作为Golang的主要特性之一,也是 ...

  9. Go语言TCP Socket编程

      Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分.在日常应用中,我们也可以看到Go中的net以及其subdirectories下的 ...

  10. go socket

    https://tonybai.com/2015/11/17/tcp-programming-in-golang/ Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程 ...

随机推荐

  1. Jmeter(四十) - 从入门到精通进阶篇 - Jmeter配置文件的刨根问底 - 中篇(详解教程)

    1.简介 为什么宏哥要对Jmeter的配置文件进行一下讲解了,因为有的童鞋或者小伙伴在测试中遇到一些需要修改配置文件的问题不是很清楚也不是很懂,就算修改了也是模模糊糊的.更有甚者觉得那是禁地神圣不可轻 ...

  2. [Fundamental of Power Electronics]-PART I-5.不连续导电模式-5.1 DCM来源和模式边界

    引子: 当使用电流单向和/或电压单向半导体开关实现DC-DC变换器的理想开关时,可能会出现一种或多种被称为不连续导电模式(DCM)的新工作模式.当电感电流或电容电压的纹波大到足以导致所施加的开关电流或 ...

  3. 201871030133-徐作朝 实验二 个人项目—《D{0-1} KP》项目报告

    项目 内容 课程班级博客链接 课程班级博客链接 这个作业要求连接 作业要求链接 我的课程学习目标 (1)掌握软件项目个人开发流程.(2)掌握Github发布软件项目的操作方法. 这个作业在那些方面帮助 ...

  4. 【接入指南】一个Demo带你玩转华为帐号服务

    在<接入指南:一文带你了解华为帐号服务>中已经给大家介绍了华为帐号服务有哪些优势,如一键授权登录华为全场景共享.共享华为帐号所有用户资源.帐号安全可靠.接入方便快捷等,以及为什么能帮助开发 ...

  5. 敏捷史话(十五):我发明了敏捷估算扑克牌 —— James Greening

    雪鸟会议 雪鸟会议前夕,James Grenning 在 Object Mentor 与 Robert C. Martin 一同工作,彼时组织雪鸟会议的 Bob 大叔盛情邀请 James,告知他会议的 ...

  6. 004-Java中的运算符

    @ 目录 一.运算符 一.分类 二.算数运算符 三.关系运算符 四.逻辑运算符 五.赋值运算符 六.条件运算符(三目运算符) 七.+运算符 一.运算符 一.分类 二.算数运算符 加  $+$ 减  $ ...

  7. Day12 _63_获取当前线程对象

    给线程起名 * 1. Thread.currentThread(); 可以获取到当前线程对象,出现在哪就是获取哪个线程. * 2. thread.setName(); 给该线程起名字 * 3. thr ...

  8. 日志功能 - 使用 conf 配置文件

    配置文件:Logger.conf 工具类:LogUtil.py 测试类:testDemo.py 执行效果 配置文件:Logger.conf 定义日志的类型.级别.格式等信息. [loggers] # ...

  9. Linux(CentOS7)安装与卸载MySQL8.0图文详解

    Mysql数据库的安装对于开发者来说,是我们必然会面对的问题,它的安装过程其实并不复杂,并且网络上的安装教程也非常多,但是对于新手来说,各种不同形式的安装教程,又给新手们带来了要选择哪种方式进行安装的 ...

  10. Android埋点技术概览

    注:本文同步发布于微信公众号:stringwu的互联网杂谈Android无埋点技术概览 本文是Android无埋点系列的开篇---埋点技术概览 1 背景 埋点是数据产品经理(分析师)基于业务需求,对用 ...