go-tcpsvr

在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法 一.TcpSvr的使用方法A.测试程序:using System;using Ibms.Net.TcpCSFramework;using System.Collections;using System.Net.Sockets;namespace Ibms.Test{ ///  /// 测试TcpSvr的类 ///  public class TestTcpSvr {    public TestT 阅读详情

go语言用于制作socket相关的事情是非常方便的。在底层框架直接有支持。在这里编写一个简单版本的服务器通讯逻辑。参考了leaf的代码。

package main

import (
    "encoding/binary"
    "fmt"
    "io"
    "net"
    "os"
    "os/signal"
    "sync"
    "time"
)

type (
    // 会话对象
    session struct {
        sync.Mutex
        conn      net.Conn
        writeChan chan []byte
        closeFlag bool
        owner     *server
    }

    SessionSet map[*session]struct{}

    // 服务器对象
    server struct {
        sync.Mutex
        tempDelay  time.Duration
        ln         net.Listener
        wgLn       sync.WaitGroup
        mutexConns sync.Mutex
        conns      SessionSet
        wgSession  sync.WaitGroup
    }
)

func (this *server) Close() {
    fmt.Printf("server.Close() address: %v\n", this.ln.Addr().String())
    this.ln.Close()
    this.wgLn.Wait()

    this.mutexConns.Lock()
    for conn := range this.conns {
        conn.Close()
    }
    this.conns = nil
    this.mutexConns.Unlock()
    this.wgSession.Wait()
    fmt.Printf("server.Close() done\n", this.ln.Addr().String())
}

func (this *server) ProcTempErr() {
    tempDelay := this.tempDelay
    if tempDelay == 0 {
        tempDelay = 5 * time.Millisecond
    } else {
        tempDelay *= 2
    }
    if max := 1 * time.Second; tempDelay > max {
        tempDelay = max
    }
    fmt.Printf("server.ProcTempErr accept retrying in %v\n", tempDelay)
    time.Sleep(tempDelay)
    this.tempDelay = tempDelay

}

func (this *server) onNewSession(conn net.Conn) {
    fmt.Printf("server.onNewSession remote %v\n", conn.RemoteAddr().String())

    c := &session{conn: conn, closeFlag: false, owner: this}
    c.writeChan = make(chan []byte, 512*1024)

    this.mutexConns.Lock()
    this.conns[c] = struct{}{}
    this.mutexConns.Unlock()

    go c.goRecvData(this.wgSession)
    go c.goSendData(this.wgSession)
    fmt.Println("server.onNewSession done")

    c.PostData(`I had seen little of Holmes lately.
My marriage had drifted us away from each other.`)
}

func (this *server) onSessionLost(conn *session) {
    fmt.Println("server.onSessionLost")
    this.mutexConns.Lock()
    if _, ok := this.conns[conn]; ok {
        delete(this.conns, conn)
        fmt.Println("server.onSessionLost done")
    }
    this.mutexConns.Unlock()
}

func (this *server) runAccept() {
    this.wgLn.Add(1)
    defer this.wgLn.Done()
    for {
        conn, err := this.ln.Accept()
        if err != nil {
            if ne, ok := err.(net.Error); ok && !ne.Temporary() {
                fmt.Printf("Accept err: %v\n", ne.Error())
                return
            }
            this.ProcTempErr()
            continue
        }
        this.onNewSession(conn)
    }
    fmt.Println("server.runAccept done")
}

func (this *server) Listen() {
    ln, err := net.Listen("tcp4", "0.0.0.0:8089")
    if err != nil {
        fmt.Println("listen fail: ", err)
        return
    }
    this.ln = ln
    this.conns = make(SessionSet)
    go this.runAccept()
    fmt.Printf("server.Listen address: %v done\n", ln.Addr().String())
}

func (this *session) PostData(data string) {
    msgLen := len(data)
    buf := []byte(data)
    msg := make([]byte, 2+msgLen)
    binary.LittleEndian.PutUint16(msg, uint16(msgLen))
    copy(msg[2:], buf)

    this.Lock()
    if !this.closeFlag {
        this.writeChan <- msg
    }
    this.Unlock()
    fmt.Printf("session.PostData address: %v, data size: %v\n",
        this.conn.RemoteAddr().String(), len(msg))
}

func (this *session) Close() {
    fmt.Println("session.Close")
    this.innorCloseSock()
}

func (this *session) innorCloseSock() {
    this.Lock()
    if !this.closeFlag {
        this.closeFlag = true
        this.conn.Close()
        close(this.writeChan)
        fmt.Println("session innorCloseSock done")
    }
    this.Unlock()
}

func (this *session) goRecvData(wg sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()
    conn := this.conn
    for {
        data, err := procData(conn)
        if err != nil {
            fmt.Printf("session.goRecvData procData fail, err: %v\n", err)
            break
        }
        this.handleData(data)
    }
    this.innorCloseSock()
}

func (this *session) goSendData(wg sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()
    conn := this.conn

    for b := range this.writeChan {
        if b == nil {
            fmt.Println("session.goSendData b is nil")
            break
        }
        _, err := conn.Write(b)
        if err != nil {
            fmt.Printf("session.goSendData Write fail, err: %v\n", err)
            break
        }
    }
    this.owner.onSessionLost(this)
    this.innorCloseSock()
}

func procData(conn net.Conn) ([]byte, error) {
    var bufHeader []byte = make([]byte, 2, 2)
    if _, err := io.ReadFull(conn, bufHeader); err != nil {
        fmt.Printf("procData io.ReadFull header fail, err: %v\n", err.Error())
        return nil, err
    }
    var msgLen uint32
    msgLen = uint32(binary.LittleEndian.Uint16(bufHeader))
    fmt.Printf("procData header get size: %v\n", msgLen)
    msgData := make([]byte, msgLen)
    if _, err := io.ReadFull(conn, msgData); err != nil {
        fmt.Printf("procData io.ReadFull body fail, err: %v\n", err.Error())
        return nil, err
    }
    fmt.Printf("procData get data, size: %v\n", len(msgData))
    return msgData, nil
}

func (this *session) handleData(data []byte) {
    fmt.Printf("session.handleData remote address: %v, recv data size: %v\n",
        this.conn.RemoteAddr().String(), len(data))
    content := string(data)
    fmt.Println(content)
}

func main() {
    svr := &server{}
    svr.Listen()

    // close
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, os.Kill)
    <-c

    svr.Close()
}

测试代码:

#! /usr/bin/python3
# coding: utf-8
import asyncore, socket,struct
import time
import logging

logging.basicConfig(level=logging.DEBUG,
    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%a, %d %b %Y %H:%M:%S')

msgstr = '''One night---it was on the twentieth of March,1888---I was returning
from a journey to a patient.
'''

class Client( asyncore.dispatcher ):

    def __init__( self, _host, _port ):
        asyncore.dispatcher.__init__( self )
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect( ( _host, _port ) )
        self.recv_buf_ = b''
        self.send_buf_ = b''
        self.post_data(msgstr)

    def post_data(self, _raw_buf):
        #
        # | short      | binary            |
        # | pack size  | encrypt binary    |
        # | binary data|                   |
        # small-endian unsigned int
        # 
        self.send_buf_ = self.send_buf_ + struct.pack("<h",len(_raw_buf))
        # can't concat str to bytes
        self.send_buf_ = self.send_buf_ + _raw_buf.encode()

    def handle_connect( self ):
        logging.debug("on handle_connect establishment")

    def handle_close( self ):
        self.close()
        logging.error("on handle_close socket cannt connect")

    def handle_read( self ):
        self.recv_buf_ += self.recv( 8192 )
        logging.debug('on handle_read read size: {0}'.format(len(self.recv_buf_)))
        #
        # | short      | binary            |
        # | pack size  | encrypt binary    |
        # | binary data|                   |
        # small-endian unsigned int
        # 
        while len(self.recv_buf_) > 2:
            if len(self.recv_buf_) < 2:
                return True
            stream_size = struct.unpack( "<h", self.recv_buf_[:2])[0]
            logging.debug("handle_read stream_size: {0}".format(stream_size))
            self.proc_data(self.recv_buf_[2:2+stream_size])
            self.recv_buf_ = self.recv_buf_[2+stream_size:]

    def writeable( self ):
        return ( len( self.send_buf_ ) > 0 )

    def handle_write( self ):
        if not self.writeable():
            time.sleep(1)
            return
        sent = self.send( self.send_buf_ )
        logging.info("handle_write, sent: %d"%sent)
        self.send_buf_ =  self.send_buf_[ sent: ]
        pass
    
    def proc_data(self,content):
        logging.debug("proc_data content: {}".format(content))

if __name__ == "__main__":
    logging.info("robot is launch")
    clients = []
    for i in range( 0, 100 ):
        clients.append( Client( '127.0.0.1', 8089) )
    asyncore.loop()
    pass

思科/华为设备OSPF DR/BDR配置避坑指南:优先级为0到底有啥用? 本文深入解析思科/华为设备OSPF DR/BDR选举机制,重点探讨优先级为0的特殊应用场景及配置避坑指南。通过实际案例和厂商差异对比,帮助网络工程师在多厂商环境中避免DR角色漂移问题,提升网络稳定性与收敛效率。 阅读详情

相关推荐

TCPsvr TCP/IP 服务器程序

TCP/IP 服务器程序 在VS2005上运行

Linux - 第16节 - 网络基础(应用层二)

网络计算器场景下我们使用socket套接字系列接口、定制协议、编写业务的过程较为麻烦,考虑的也不一定全面。一些常用的场景已经有程序员自定义过成熟的协议,这些协议慢慢成为了应用层特定协议的标准,可以被我们直接使用。在编写网络通信代码时,我们可以自己进行协议的定制,但实际有很多优秀的工程师早就已经写出了许多非常成熟的应用层协议,其中最典型的就是HTTP协议。应用层代码的定制协议部分常见协议标准有http、https、smtp、ftp、DNS等,我们这里以http、https为主进行讲解。

qq_45113223的博客 1239

TCPSvr Class-C#异步Socket标准程序

1.在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架 2. 特点:(1).使用hash表保存所有已连接客户端的状态,收到数据时能实现快速查找.每当 有一个新的客户端连接就会产生一个新的会话(Session).该Session代表了客户端对象. (2).使用异步的Socket事件作为基础,完成网络通讯功能. (3).支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网络环境.

Linux命令学习(一)

linux命令

señoritaw的博客 409

OpenClaw:深度学习驱动的智能编程工具实战解析

智能编程工具通过深度学习技术革新传统开发流程,其核心在于代码语义理解与实时协作能力。这类工具采用AI代码补全、错误预测等技术,显著降低开发者认知负荷,尤其适用于复杂业务逻辑开发和遗留系统维护场景。以OpenClaw为例,其智能填充和上下文感知功能可提升40%编码效率,减少65%调试时间。关键技术实现包含自然语言处理、代码静态分析等,在金融科技、电商系统等领域具有广泛应用前景。热词提示:代码理解引擎、智能工作流整合展现了当前AI辅助开发的最新趋势。

chubaisheng8627的博客 320

STM32进入HardFault_Handler时,如何找到问题所在?

当STM32进入HardFault_Handler时,定位问题发生的点

m0_60831979的博客 2150

TcpSvr中用redis作为缓存管理工具

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 redis提供五种数据类型:string,dict(hash),list,set及zset(sorted set)。 https://redis.io/ Redis is an open source (BSD licensed), in-memory

Power of technology will free your body and spirit 462

Opencv2.4.9源码分析——HoughCircles

图形可以用一些参数进行表示,标准霍夫变换的原理就是把图像空间转换成参数空间(即霍夫空间),例如霍夫变换的直线检测就是在距离-角度空间内进行检测。圆可以表示成: (x-a)2+(y-b)2=r2                   (1) 其中a和b表示圆心坐标,r表示圆半径,因此霍夫变换的圆检测就是在这三个参数组成的三维空间内进行检测。 原则上,霍夫变换可以检测任何形状。但复杂的形状需要的

赵春江的专栏 3万+

简单的TcpServer(英译中)

实现一个简单的英译汉的功能。 下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符; 应用程序可以像读写文件一样用read/write在网络上收发数据; 如果socket()调用出错则返回-1; 对于IPv4, family参数指定为AF_INET; 对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议; protocol参数的就不做介绍了,指定为0即可。 服务

weixin_70056514的博客 1784

手把手教你用示波器抓取MIPI D-PHY波形:从LP11到HS Burst的完整信号解析

本文详细介绍了使用示波器精准捕获MIPI D-PHY信号的关键技巧,包括探头选择、示波器参数设置、信号状态解析及高级触发配置。通过实战案例和波形分析,帮助工程师快速识别从LP11到HS Burst的各种信号状态,解决常见的信号完整性问题,提升调试效率。

weixin_30471561的博客 560

C#菜鸟教程学习笔记

C#菜鸟教程学习笔记 C#简介 C# 是一种编程语言,它是由微软开发的。 C# 编程是基于 C 和 C++ 编程语言的,它与 Java 也非常相似。 C# 是 .Net 框架的一部分,且用于编写 .Net 应用程序。 .Net 框架(.Net Framework)能编写出下面类型的应用程序: ①Windows 应用程序 ②Web 应用程序 ③Web 服务 常用的C# 编程的开发工具:Visual Studio(VS:可视化工作室) C#程序结构 一个 C# 程序(C# 文件的后缀为 .cs)主要包括以下部分

mlc_WTMOTW的博客 4978

基于TCP协议的网络服务器的模拟实现

1

work_hard423的博客 1035

gephi生成网络关系图_路网拓扑重建专题 (3) 网络图生成

城市道路网建模在获得基础路网之后,需要考虑道路交通网络的描述与表达问题。网络是将现实世界中的事物及其关系进行适当的抽象,构造成由节点和边按照一定规则组成的形式。一个实体网络可以由有限个“节点”集合与“边”集合构成的图表示,即网络表示为。对于一个由个节点组成的无权复杂网络,该图有邻接矩阵,这里。根据研究对象不同,建模方法主要包括原始法[1]和对偶法[2]两种,见图1,字母a-j为交叉口编号...

weixin_39761195的博客 3504

【网络篇】第七篇——网络套接字编程(三)(TCP详解)

简单的TCP网络程序服务端服务端创建套接字服务端绑定服务端监听服务端获取连接服务端处理请求客户端客户端创建套接字客户端连接服务器客户端发起请求服务器测试单执行流服务器的弊端 TCP服务器在调用socket函数创建套接字时,参数设置如下:如果创建套接字后获得的文件描述符是小于0的,说明套接字创建失败,此时也就没必要进行后续操作了,直接终止程序即可。 注意: 绑定步骤如下:由于TCP服务器初始化时需要服务器的端口号,因此在服务器类当中需要引入端口号,当实例化服务器对象时就需要给传入一个端口号。而由于我当前使

m0_58367586的博客 1949

锂电池SOC估计 | 基于LSTM长短期记忆神经网络的锂电池SOC估计(附matlab完整代码)

锂电池SOC估计 | 基于LSTM长短期记忆神经网络的锂电池SOC估计(附matlab完整代码)

前程算法屋的博客 1022

【PostgreSQL教程】PostgreSQL 高级篇之子查询

子查询也可称为内部查询、嵌套查询,指的是在 PostgreSQL 查询中的 WHERE 子句中嵌入查询语句。一个 SELECT 语句的查询结果能够作为另一个语句的输入值。子查询可以与 SELECT、INSERT、UPDATE 和 DELETE 语句一起使用,并可使用运算符如 =、、>=、

No8g攻城狮的博客 1206

编写tcp_server

背景知识: 1.在TCP/IP协议中,IP地址+TCP/UCP端口号 —-》唯一标识网络通讯中的一个进程, 并且IP地址+端口号被称为socket。 2.内存中的多字节数据相对于内存地址有大端和小端之分, 而网络数据流同样有大端和小端之分。 TCP/IP协议规定,网络数据流应采用大端字节序,即就是低地址高字节。 所以:发送端把数据发送到发送缓冲区之前需要做字节序的转换。 而接收主机在

小葱的博客 8215

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

多线程、多进程版本TCP通信

1.如果只调用一次accept,就只能接受一个客户端的连接 2.如果将accept放到while循环内部,则进行一次收发数据的时候,需要阻塞等待新连接的到来 这两种情况都无法处理多个客户端的情况 原则: 多进程: 父进程进行获取连接 子进程进行收发数据,每来一个新链接创建一个新的进程处理 多线程 主线程进行获取连接 每来一个新连接,则创建一个新的线程进行处理 多进程版本 //tcpsvr.h...

E_kay的博客 319

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

TcpServer

服务端类,一个服务端只需要一个该实例。 TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg, Option option) : loop_(CHECK_NOTNULL(loop)), ipPort_(listenAddr.t

qu1993的博客 2404

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

一、Linux常用命令

终端快捷键 Ctrl + a/Home 切换到命令行开始 Ctrl + e/End 切换到命令行末尾 Ctrl + l 清除屏幕内容,效果等同于clear Ctrl + u 清除剪切光标之前的内容 Ctrl + k 剪切清除光标之后的内容 Ctrl + y 粘贴刚才所删除的字符 Ctrl + r 在历史命令中查找 (这个非常好用,输入关键字就调出以前的命令了) Ctrl + c 终止命令 ctrl + o 重复执行命令 Ctrl + d 退出shell,logout Ctrl + z 转入后台运行,但在当前

qingjiliao的博客 197

LINUX常用操作命令和命令行编辑快捷键

终端快捷键: Ctrl + a/Home 切换到命令行开始 Ctrl + e/End 切换到命令行末尾 Ctrl + l 清除屏幕内容,效果等同于 clear Ctrl + u 清除剪切光标之前的内容 Ctrl + k 剪切清除光标之后的内容 Ctrl + y 粘贴刚才所删除的字符 Ctrl + r 在历史命令中查找 (这个非常好用,输入关键字就调出以前的命令了) Ctrl + c 终止命令 ctrl + o 重复执行命令 Ctrl + d 退出 shell,logout Ctrl + z 转入后台运行,但

qq_45156302的博客 892

go高性能tcp服务器,GO 记一次TcpServer性能优化

库存文章发布写了一个TcpServer,主要功能是做终端的保活信息,保活信息中包含一些额外数据。并转发来自后端的命令至终端。历经数日,各项基础功能完备,准备做一些压力测试。服务器配置:cpu family : 6model : 63model name : Intel(R) Xeon(R) CPU E5-2620 v3 @ 2.40GHzstepping : 2microco...

weixin_42639633的博客 1334

Go:简单的TCP server demo

golang tcp server demo 服务端样例 实例 例子

test1280 1175

udp协议路由服务器,TCP/UDP通信协议详解

一.通信协议概述:1.定义:通信设备双方通过共同遵从一组约定,从而使数据可在网络上进行相互传输。打个比方,不同国家不同母语的两个人,如果要实现交流,就必须以大家都熟悉的一种通用语言来交流,比如英语。同样数据的格式只有一致,通信双方才能正常传输。2.通信协议层次划分:国际上通用的划分有tcp/ip四层模型和osi七层模型两种,TCP/UDP协议在两种模型中对应如下:osi七层模型TCP/IP四层模型...

weixin_42511177的博客 1704
上一篇: go[8]-测试
下一篇: go-micro入门[翻译]
erlang_hell
博客等级 码龄14年 57粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值