socket相关的小总结

本文介绍了Socket的基本概念,包括服务器监听、客户端请求和连接确认。详细阐述了Socket的两种模式:面向连接和无连接,分别对应TCP和UDP协议。并以Java为例,逐步展示了简化版、改进版的Socket实现,以及如何实现服务端多监听来处理多个客户端请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 什么是Socket

Socket的概念很简单,它是网络上运行的两个程序间双向通讯的一端,既可以接收请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递。

所以简而言之,Socket就是进程通信的端点,Socket之间的连接过程可以分为几步:

1、服务器监听

服务器端Socket并不定位具体的客户端Socket,而是处于等待连接的状态,实时监控网络状态

2、客户端请求

客户端Socket发出连接请求,要连接的目标是服务端Socket。为此,客户端Socket必须首先描述它要连接的服务端Socket,指出服务端Socket的地址和端口号,然后就向服务端Socket提出连接请求

3、连接确认

当服务端Socket监听到或者说是接收到客户端Socket的连接请求,它就响 应客户端Socket的请求,建立一个新的线程,把服务端Socket的描述发给客户端,一旦客户端确认了此描述,连接就好了。而服务端Socket继续 处于监听状态,继续接收其他客户端套接字的连接请求

 

 

二 Socket的两种模式

Socket有两种主要的操作方式:面向连接和无连接的

 

2.1 面向连接的Socket

操作就像一部电话,必须建立一个连接和一人呼叫,所有事情在达到时的顺序与它们出发时的顺序一样,但可靠性,正确性和有序性有所保障

面向连接的操作使用TCP协议。一个这个模式下的Socket必须在发送数据之前 与目的地的Socket取得一个连接,一旦连接建立了,Socket就可以使用一个流接口:打开-->读-->写-->关闭,所有发送 的信息都会在另一端以同样的顺序被接收。面向连接的操作比无连接的操作效率更低,但是数据的安全性更高。

2.2 无连接的Socket

操作就像是一个邮件投递,没有什么 保证,多个邮件可能在达到时的顺序与出发时的顺序不一样。快速、高效,但是数据安全性不佳

无连接的操作使用数据报协议。一个数据报是一个独立的单元,它包含了所有这次投递的信息,就像一个信封,它有目的地址和要发送的内容,这个模式下的Socket并不需要连接一个目的Socket,它只是简单地透出数据报,无连接的操作是快速、高效的,但是数据安全性不佳。

      到底使用哪种模式是由应用程序的需要决定的。如果可靠性更重要的话,用面向连接的 操作会好一些,比如文件服务器需要数据的正确性和有序性,如果一些数据丢失了,系统的有效性将会失去;比如一些服务器间歇性地发送一些数据块,如果数据丢 失了的话,服务器并不想要再重新发送一次,因为当数据到达的时候,它可能已经过时了。确保数据的有序性和正确性需要额外的操作的内存消耗,额外的消耗将会 降低系统的回应速率。

 

 

 

 

 

三 利用Java开发Socket

在Java中面向连接的类有两种形式,它们分别是客户端和服务器端,先看一下

3.1 简化版

服务器端:

public class HelloServer
{
    public static void main(String[] args) throws IOException
    {
        ServerSocket serverSocket = null;
      
        try
        {
            // 实例化一个服务器端的Socket连接
            serverSocket = new ServerSocket(9999);
        }
        catch (IOException e)
        {
            System.err.print("Could not listen on port:9999");
            System.exit(1);
        }
        
        Socket clientSocket = null;
        try
        {
            // 用于接收来自客户端的连接
            clientSocket = serverSocket.accept();
        }
        catch (IOException e)
        {
            System.err.println("Accept failed");
            System.exit(1);
        }
        
        // 客户端有数据了就向屏幕打印Hello World
        System.out.print("Hello World");
        clientSocket.close();
        serverSocket.close();
    }
}

此代码的作用就是构造出服务端Socket,并等待来自客户端的消息。当然,此时运行代码是没有任何反应的,因为服务端在等待客户端的连接。

客户端: 

 public class HelloClient
  {
      public static void main(String[] args) throws IOException
      {
          Socket socket = null;
          BufferedReader br = null;
        
          // 下面这段程序,用于将输入输出流和Socket相关联
          try
         {
             socket = new Socket("localhost", 9999);
             br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         }
         catch (UnknownHostException e)
         {
             System.err.println("Don't know about host:localhost");
             System.exit(1);
         }
         catch (IOException e)
         {
             System.err.println("Could not get I/O for the connection");
             System.exit(1);
         }
         
         System.out.print(br.readLine());
         br.close();
         socket.close();
     }
 }

此时只需要先运行HelloServer,再运行HelloClient,保证服务器先监听,客户端后发送,就可以在控制台上看到"Hello World"了。

 

 

 

3.2 改进版本的Socket

上面的Socket演示的效果是,服务器端Socket收到了来自客户端Socket的数据,但是并没有真正地体现服务器端Socket和客户端Socket的交互,下面演示一下利用Socket进行服务器端和客户端的交互

服务器端

  public class EchoServer
  {
      public static void main(String[] args) throws IOException
      {
          ServerSocket ss = null;
          PrintWriter pw = null;
          BufferedReader br = null;
          
          try
         {
             // 实例化监听端口
             ss = new ServerSocket(1111);
         }
         catch (IOException e)
         {
             System.err.println("Could not listen on port:1111");
             System.exit(1);
         }
         Socket incoming = null;
         while (true)
         {
             incoming = ss.accept();
             pw = new PrintWriter(incoming.getOutputStream(), true);
             // 先将字节流通过InputStreamReader转换为字符流,之后将字符流放入缓冲之中
             br = new BufferedReader(new InputStreamReader(incoming.getInputStream()));
             // 提示信息
             pw.println("Hello!...");
             pw.println("Enter BYE to exit");
             pw.flush();
             // 没有异常则不断循环
             while (true)
             {
                 // 只有当用户输入时才返回数据
                 String str = br.readLine();
                 // 当用户连接断掉时会返回空值null
                 if (str == null)
                 {
                     // 退出循环
                     break;
                }
                 else
                 {
                     // 对用户输入字符串加前缀Echo并将此信息打印到客户端
                     pw.println("Echo:" + str);
                     pw.flush();
                     // 退出命令,equalsIgnoreCase()是不区分大小写的
                     if ("BYE".equalsIgnoreCase(str.trim()))
                     {
                         break;
                     }
                 }
             }
             // 该close的资源都close掉
             pw.close();
             br.close();
             incoming.close();
             ss.close();
         }
     }
 }

客户端

  public class EchoClient
  {
      public static void main(String[] args) throws IOException
      {
          Socket socket = null;
          PrintWriter pw = null;
         BufferedReader br = null;
          
          try
         {
             socket = new Socket("localhost", 1111);
             pw = new PrintWriter(socket.getOutputStream(), true);
             br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         }
         catch (UnknownHostException e)
         {
             System.err.println("Don't know abount host:localhost");
             System.exit(1);
         }
         System.out.println(br.readLine());
         System.out.println(br.readLine());
         BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
         String userInput;
         // 将客户端Socket输入流(即服务器端Socket的输出流)输出到标准输出上
         while ((userInput = stdIn.readLine()) != null)6         {
             pw.println(userInput);
             System.out.println(br.readLine());
         }
         // 同样的,将该关闭的资源给关闭掉
         pw.close();
         br.close();
         socket.close();
     }
 }

 

3.3 服务端多监听

程序写到上面,已经基本成型了,不过还有一个问题:现实情况中,一个服务器端的Socket不可能只对应一个客户端的Socket,必然一个服务器端的Socket可以接收来自多个客户端的Socket的请求

解决上述问题的办法就是多线程。大致代码是这样的:

public class HandleThread extends Thread
{
    private Socket socket;
    
    public HandleThread(Socket socket)
    {
        this.socket = socket;
    }
    

    public void run()
    {
        // Socket处理代码
    }
}



public static void main(String[] args) throws IOException
{
    ServerSocket serverSocket = null;
    
    try
    {
        // 实例化一个服务器端的Socket连接
        serverSocket = new ServerSocket(9999);
    }
    catch (IOException e)
    {
        System.err.print("Could not listen on port:9999");
        System.exit(1);
    }

       
    Socket clientSocket = null;
    try
    {
        while (true)
        {
           // 用于接收来自客户端的连接
            clientSocket = serverSocket.accept();
            new HandleThread(clientSocket).start();
        }
    }
    catch (IOException e)
    {
        System.err.println("Accept failed");
        System.exit(1);
    }
}    

即,服务器端启动一个永远运行的线程,监听来自客户端的Socket,一旦客户端有Socket到来,即开启一个新的线程将Socket交给线程处理。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值