Socket 入坑

什么是Socket

Socket(套接字)是在计算机网络中实现通信的一种机制。它提供了一种应用程序编程接口(API),允许应用程序通过网络进行数据传输和通信。

在网络通信中,Socket 可以被看作是提供网络连接的一种抽象。它可以用于在不同的计算机上的应用程序之间建立双向的通信链路。通过 Socket,应用程序可以发送和接收数据,与其他应用程序进行实时的、可靠的通信。

Socket 位于传输层和应用层之间,负责处理网络的底层细节。它封装了底层的网络协议(如 TCP 或 UDP),提供了一组简单而有效的函数和方法,使得开发者能够方便地使用网络进行通信。
image

Socket通信模型

使用 Socket 进行通信的一般过程如下:

  1. 创建 Socket:应用程序通过调用系统提供的函数或类库来创建一个 Socket 实例,指定通信协议、地址和端口等参数。
  2. 建立连接:对于客户端,它会主动发起连接请求,指定服务器的 IP 地址和端口;对于服务器端,它会监听指定的端口,并在有连接请求时接受连接。
  3. 数据传输:连接建立成功后,应用程序可以通过 Socket 发送和接收数据。发送数据时,将数据写入 Socket 的输出缓冲区;接收数据时,从 Socket 的输入缓冲区读取数据。
  4. 断开连接:通信完成后,可以通过关闭 Socket 来断开连接,释放资源。

Socket 提供了不同的通信协议,最常用的是 TCP(传输控制协议)和 UDP(用户数据报协议)。TCP 提供可靠的连接,并确保数据的有序传输;而 UDP 则是一种无连接的协议,只提供数据的不可靠传输。

总结来说,Socket 是一种用于在计算机网络中实现通信的编程接口。它提供了创建连接、发送和接收数据的函数和方法,使得应用程序能够方便地进行网络通信。
image

消息缓冲区

Socket 消息缓冲区是指 Socket 对象内部用于存放发送和接收数据的缓冲区。消息缓冲区允许应用程序在发送和接收数据时进行数据的缓存和处理,以提高效率和性能。

对于发送数据,应用程序将要发送的数据写入到 Socket 的输出缓冲区中。这些数据并不立即发送到网络,而是在缓冲区中等待适当的发送时机。例如,当缓冲区满了或者应用程序调用了发送数据的方法时,缓冲区中的数据会被发送出去。

对于接收数据,Socket 会将从网络中接收到的数据存放在输入缓冲区中,等待应用程序读取。应用程序可以通过读取输入缓冲区中的数据来获取接收到的消息。如果输入缓冲区为空,应用程序可能会阻塞,直到有新的数据到达。

消息缓冲区的大小可以根据需要进行配置。较小的缓冲区可能会导致频繁的发送和接收操作,而较大的缓冲区可能会增加延迟和内存消耗。因此,在实际应用中,需要根据数据的传输量和性能需求来合理地配置消息缓冲区的大小。

需要注意的是,消息缓冲区只是作为临时存储数据的中介,数据的传输仍然是通过网络进行的。Socket API 提供了相应的方法用于操作消息缓冲区,如发送数据、接收数据和清空缓冲区等。

综上所述,Socket 消息缓冲区是 Socket 对象内部用于存放发送和接收数据的缓冲区,它在数据发送和接收过程中起到缓存和临时存储的作用。
image

如何理解“套接字”

在计算机编程中,套接字是用于在网络上进行数据传输的编程接口。每个打开的套接字都会被操作系统分配一个唯一的套接字句柄,也就是 fd。这个句柄可以看作是对打开套接字的引用,通过它可以进行读取、写入、关闭等操作。

入门demo

服务端

//1 创建Socket对象
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//2 绑定ip和端口
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEndPoint = new IPEndPoint(ip, 50001);
socketServer.Bind(ipEndPoint);

//3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为10
socketServer.Listen(10);

//4、【阻塞】,等待客户端连接
Socket newSocket = socketServer.Accept();

//5、【阻塞】,等待读取客户端发送过来的数据
byte[] data = new byte[1024 * 1024];
int readLeng = newSocket.Receive(data, 0, data.Length, SocketFlags.None);

//6、读取数据
var msg = Encoding.UTF8.GetString(data, 0, readLeng);

客服端

//1 创建Socket对象
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

//2 连接到服务端
IPAddress ip = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEndPoint = new IPEndPoint(ip, 50001);
socketClient.Connect(ipEndPoint);

//3 发送消息到服务端
socketClient.Send(Encoding.UTF8.GetBytes("hello,word"));

不过,这里有个很大的问题,服务端只能建立一个客户端连接和接受一次客户端发来的消息。如果想要连接更多的客户端和接受无数次的消息,服务端代码两处阻塞的地方需要另外开一个线程然后包到循环里面去。

修改后的服务端代码如下:

void .... ()
{
    //1 创建Socket对象
    socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    //2 绑定ip和端口
    IPAddress ip = IPAddress.Parse("127.0.0.1");
    IPEndPoint ipEndPoint = new IPEndPoint(ip, 50001);
    socketServer.Bind(ipEndPoint);

    //3、开启侦听(等待客户机发出的连接),并设置最大客户端连接数为10
    socketServer.Listen(10);

    //开启新的线程,循环等待新的客户端连接
    Task.Run(() => { Accept(socketServer); });
}

void Accept(Socket socket)
{
    while (true)
    {
        //4、【阻塞】,等待客户端连接
        Socket newSocket = socket.Accept();
        //开启新的线程,循环等待接收新的数据
        Task.Run(() => { Receive(newSocket); });
    }
}

void Receive(Socket newSocket)
{
    while (true)
    {
        //5、【阻塞】,等待读取客户端发送过来的数据
        byte[] data = new byte[1024 * 1024];
        int readLeng = newSocket.Receive(data, 0, data.Length, SocketFlags.None);
        //6、读取数据
        var msg = Encoding.UTF8.GetString(data, 0, readLeng);
    }
}

参考

【农码一生】
https://www.cnblogs.com/zhaopei/p/Socket1.html

posted @ 2023-07-25 01:25  广州大雄  阅读(635)  评论(0编辑  收藏  举报