C# Socket

Stella981
• 阅读 1055

目录

目录
异步原理
主要方法
源码
Server源码:
Client源码
实验效果(广播为例)
参考博客

TOC

异步原理

套接字编程原理:延续文件作用思想,打开-读写-关闭的模式。
C/S编程模式如下:
Ø 服务器端:
打开通信通道,告诉本地机器,愿意在该通道上接受客户请求——监听,等待客户请求——接受请求,创建专用链接进行读写——处理完毕,关闭专用链接——关闭通信通道(当然其中监听到关闭专用链接可以重复循环)
Ø 客户端:打开通信通道,连接服务器——数据交互——关闭信道。
Socket通信方式:
Ø 同步:客户端在发送请求之后必须等到服务器回应之后才可以发送下一条请求。串行运行
Ø 异步:客户端请求之后,不必等到服务器回应之后就可以发送下一条请求。并行运行
套接字模式:
Ø 阻塞:执行此套接字调用时,所有调用函数只有在得到返回结果之后才会返回。在调用结果返回之前,当前进程会被挂起。即此套接字一直被阻塞在网络调用上。
Ø 非阻塞:执行此套接字调用时,调用函数即使得不到得到返回结果也会返回。
套接字工作步骤:
Ø 服务器监听:监听时服务器端套接字并不定位具体客户端套接字,而是处于等待链接的状态,实时监控网络状态
Ø 客户端链接:客户端发出链接请求,要连接的目标是服务器端的套接字。为此客户端套接字必须描述服务器端套接字的服务器地址与端口号。
Ø 链接确认:是指服务器端套接字监听到客户端套接字的链接请求时,它响应客户端链接请求,建立一个新的线程,把服务器端套接字的描述发送给客户端,一旦客户端确认此描述,则链接建立好。而服务器端的套接字继续处于监听状态,继续接受其他客户端套接字请求。

主要方法

C# Socket
C# Socket
C# Socket

源码

Server源码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    public class Conn
    { //定义数据最大长度
        public const int data = 1024;
        //Socket
        public Socket socket;
        //是否使用
        public bool isUse = false;
        //Buff
        public byte[] readBuff = new byte[data];
        public int buffCount = 0;
        //构造函数
        public Conn()
        {
            readBuff = new byte[data];
        }
        //初始化
        public void Init(Socket socket)
        {
            this.socket = socket;
            isUse = true;
            buffCount = 0;
        }
        //缓冲区剩余的字节数
        public int BuffRemain()
        {
            return data - buffCount;
        }

        //获取客户端地址
        public string GetAdress()
        {
            if (!isUse)
                return "无法获取地址";
            return socket.RemoteEndPoint.ToString();
        }
        //关闭
        public void Close()
        {
            if (!isUse)
                return;
            Console.WriteLine("[断开链接]" + GetAdress());
            socket.Close();
            isUse = false;
        }
    }
    public class Program
    {
        /// <summary>
        /// 创建多个Conn管理客户端的连接
        /// </summary>
        public static Conn[] conns;

        /// <summary>
        /// 最大连接数
        /// </summary>
        public static int maxConn = 50;

        /// <summary>
        /// 将Socket定义为全局变量
        /// </summary>
        private static Socket serverSocket;

        /// <summary>
        /// 获取链接池索引,返回负数表示获取失败
        /// </summary>
        /// <returns></returns>
        public static int NewIndex()
        {
            if (conns == null)
                return -1;
            for (int i = 0; i < conns.Length; i++)
            {
                if (conns[i] == null)

                {
                    conns[i] = new Conn();
                    return i;
                }
                else if (conns[i].isUse == false)
                {
                    return i;
                }
            }
            return -1;
        }
        static void Main(string[] args)
        {
            //创建多个链接池,表示创建maxConn最大客户端
            conns = new Conn[maxConn];
            for (int i = 0; i < maxConn; i++)
            {
                conns[i] = new Conn();
            }
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000);
            serverSocket.Bind(ipEndPoint);//绑定IP和端口号
            serverSocket.Listen(maxConn);//开始监听端口,0为监听无限个客户端
            Console.WriteLine("[服务器]启动成功");
            //开始调用异步连接
            serverSocket.BeginAccept(AcceptCb, null);
            //按下quit退出程序
            while (true)
            {
                if (Console.ReadLine() == "quit") return;
            }
        }
        /// <summary>
        /// Accept回调
        /// </summary>
        /// <param name="ar"></param>
        static void AcceptCb(IAsyncResult ar)
        {
            try
            {
                Socket socket = serverSocket.EndAccept(ar);//尝试进行异步连接
                int index = NewIndex();
                if (index < 0)
                {
                    socket.Close();
                    Console.Write("[警告]链接已满");
                }
                else
                {
                    Conn conn = conns[index];
                    conn.Init(socket);
                    string adr = conn.GetAdress();
                    Console.WriteLine("客户端连接 [" + adr + "] conn池ID:" + index);
                    conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
                }
                serverSocket.BeginAccept(AcceptCb, null);
            }
            catch (Exception e)
            {
                Console.WriteLine("AcceptCb失败:" + e.Message);
            }
        }
        /// <summary>
        /// 接收回调
        /// </summary>
        /// <param name="ar"></param>
        static void ReceiveCb(IAsyncResult ar)
        {
            Conn conn = (Conn)ar.AsyncState;
            try
            {
                int count = conn.socket.EndReceive(ar);
                //关闭信号
                if (count <= 0)
                {
                    Console.WriteLine("收到 [" + conn.GetAdress() + "] 断开链接");
                    conn.Close();
                    return;
                }
                //数据处理
                string str = Encoding.UTF8.GetString(conn.readBuff, 0, count);
                Console.WriteLine("收到 [" + conn.GetAdress() + "] 数据:" + str);
                str = conn.GetAdress() + "发送的:" + str;
                byte[] bytes = System.Text.Encoding.UTF8.GetBytes("接收到" + str);
                //广播
                /*
                for (int i = 0; i < conns.Length; i++)
                {
                    if (conns[i] == null)
                        continue;
                    if (!conns[i].isUse)
                        continue;
                    Console.WriteLine("将消息转播给 " + conns[i].GetAdress());
                    conns[i].socket.Send(bytes);
                }*/
                //点播
                for (int i = 0; i<=0;i++)
                {
                    if (conns[i] == null)
                        continue;
                    if (!conns[i].isUse)
                        continue;
                    Console.WriteLine("将消息转播给 " + conns[i].GetAdress());
                    conns[i].socket.Send(bytes);
                }
                //继续接收
                conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(),SocketFlags.None, ReceiveCb, conn);
            }
            catch (Exception e)
            {
                Console.WriteLine("收到 [" + conn.GetAdress() + "] 断开链接");
                conn.Close();
            }
        }
    }
}

Client源码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace ConsoleApp1
{
    public class Program
    {
        private static int datacount = 1024;//设置数组数据长度
        private static Socket socket;
        private static byte[] dataBuff = new byte[datacount];
        private static string recvStr;
        static void Main(string[] args)
        {
            //获取到Socket协议
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //绑定IP与端口
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000);
            socket.Connect(ipEndPoint);//与服务端进行连接      
            Console.WriteLine("客户端地址 " + socket.LocalEndPoint.ToString());//获取客户端地址
            //开启异步接收模式
            socket.BeginReceive(dataBuff, 0, datacount, SocketFlags.None, ReceiveCb, null);
            socket.Send(Encoding.UTF8.GetBytes("123"));  //封装好的一个Send方法,能够在方法中操作,比如说:释放资源
            Send(socket, "456");  //简单的发送
            while (true)
            {
                //向服务器发送数据
                byte[] data = Encoding.UTF8.GetBytes(Console.ReadLine());//输入字符
                //按下回车键发送数据
                ConsoleKey inpt = Console.ReadKey().Key;
                if (inpt == ConsoleKey.Enter)
                {
                    socket.Send(data);
                    Console.WriteLine("发送成功");
                }
            }
        }
        private static void Send(Socket handler, String data)
        {
            byte[] byteData = Encoding.ASCII.GetBytes(data);
            handler.BeginSend(byteData, 0, byteData.Length, 0,
            new AsyncCallback(SendCallback), handler);
        }
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                int bytesSent = handler.EndSend(ar);
                Console.WriteLine("Sent {0} bytes to client.", bytesSent);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void ReceiveCb(IAsyncResult ar)
        {
            //接收数据的长度
            //数据处理
            //将dataBuffer数组转码成字符的形式输出
            //dataBuffer:需要转码的数组
            //0:从数组的长度,0开始读取。
            //count :读取数组的最大长度
            int count = socket.EndReceive(ar);
            if(count>0)  //当接受数据长度大于0时处理
            {
                string str = System.Text.Encoding.UTF8.GetString(dataBuff, 0, count);
                //获取服务器端的数据
                Console.WriteLine("获取服务端的数据:" + str);
                //继续接收
                socket.BeginReceive(dataBuff, 0, datacount, SocketFlags.None, ReceiveCb, null);
            }
        }
    }
}

实验效果(广播为例)

C# Socket

参考博客

C# Socket编程 同步及异步通信:https://blog.csdn.net/bemodesty/article/details/84396658
C# Socket之异步连接(一):https://blog.csdn.net/u010511043/article/details/86435701
C# Socket之异步连接(二):https://blog.csdn.net/u010511043/article/details/86437192
C# Socket之客户端异步连接:https://blog.csdn.net/u010511043/article/details/86441724

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Java修道之路,问鼎巅峰,我辈代码修仙法力齐天
<center<fontcolor00FF7Fsize5face"黑体"代码尽头谁为峰,一见秃头道成空。</font<center<fontcolor00FF00size5face"黑体"编程修真路破折,一步一劫渡飞升。</font众所周知,编程修真有八大境界:1.Javase练气筑基2.数据库结丹3.web前端元婴4.Jav
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这