Socket的基本了解

注:参考整理了一些网上的资源,合在了一起。

1、Socket 简介

(1)协议简介

协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构、交换方式、包含的意义以及怎样对报文所包含的信息进行解析。

TCP/IP 协议族有 IP 协议、TCP 协议和 UDP 协议。

TCP 协议和 UDP 协议使用的地址叫做端口号,用来区分同一主机上的不同应用程序。TCP 协议和 UDP 协议也叫端到端传输协议,因为他们将数据从一个应用程序传输到另一个应用程序,而IP 协议只是将数据从一个主机传输到另一个主机。

在 TCP/IP 协议中,有两部分信息用来确定一个指定的程序:互联网地址和端口号:其中互联网地址由 IP 协议使用,而附加的端口地址信息则由传输协议(TCP 或 UDP 协议)对其进行解析。

现在 TCP/IP 协议族中的主要 socket 类型为流套接字(使用 TCP 协议)和数据报套接字(使用 UDP 协议),其中通过数据报套接字,应用程序一次只能发送最长 65507 个字节长度的信息。

一个 TCP/IP 套接字由一个互联网地址,一个端对端协议(TCP 协议或 UDP 协议)以及一个端口号唯一确定。

每个端口都标识了一台主机上的一个应用程序,实际上,一个端口确定了一个主机上的一个套接字。主机中的多个程序可以同时访问同一个套接字,在实际应用中,访问相同套接字的不同程序通常都属于一个应用(如 web 服务程序的多个副本),但从理论上讲,它们可以属于不同的应用。

(2)基本套接字

编写 TCP 客户端程序,在实例化 Socket 类时,要注意,底层的 TCP 协议只能处理 IP 协议,如果传递的第一个参数是主机名字而不是你 IP 地址,Socket类具体实现的时候会将其解析成相应的地址,若因为某些原因连接失败,构造函数会抛出一个 IOException 异常。

TCP 协议读写数据时,read()方法在没有可读数据时会阻塞等待,直到有新的数据可读。另外,TCP 协议并不能确定在 read()和 write()方法中所发送信息的界限,接收或发送的数据可能被 TCP 协议分割成了多个部分。

编写 TCP 服务器端的程序将在 accept()方法处阻塞,以等待客户端的连接请求,一旦取得连接,便要为每个客户端的连接建立一个 Socket 实例来进行数据通信。

在 UDP 程序中,创建 DatagramPacket 实例时,如果没有指定远程主机地址和端口,则该实例用来接收数据(尽管可以调用 setXXX()等方法指定),如果指定了远程主机地址和端口,则该实例用来发送数据。

UDP 程序在 receive()方法处阻塞,直到收到一个数据报文或等待超时。由于 UDP 协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在 receive()方法处,这对客户端来说是肯定不行的,为了避免这个问题,我们在客户端使用 DatagramSocket 类的 setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。

UDP 服务器为所有通信使用同一套接字,这点与 TCP 服务器不同,TCP 服务器则为每个成功返回的 accept()方法创建一个新的套接字。

在 UDP 程序中,DatagramSocket 的每一次 receive()调用最多只能接收调用一次 send()方法所发送的数据,而且,不同的 receive()方法调用绝对不会返回同一个 send()方法所发送的额数据。

在 UDP 套接字编程中,如果 receive()方法在一个缓冲区大小为 n 的 DatagramPscket 实例中调用,而接受队列中的第一个消息长度大于 n,则receive()方法只返回这条消息的前 n 个字节,超出的其他字节部分将自动被丢弃,而且也没有任何消息丢失的提示。因此,接受者应该提供一个足够大的缓存空间的 DatagramPacket 实例,以完整地存放调用 receive() 方法时应用程序协议所允许的最大长度的消息。一个 DatagramPacket 实例中所运行传输的最大数据量为 65507 个字节,即 UDP 数据报文所能负载的最多数据,因此,使用一个有 65600 字节左右缓存数组的数据总是安全的。

在 UDP 套接字编程中,每一个 DatagramPacket 实例都包含一个内部消息长度值,而该实例一接收到新消息,这个长度值便可能改变(以反映实际接收的消息的字节数)。如果一个应用程序使用同一个 DatagramPacket 实例多次调用 receive()方法,每次调用前就必须显式地将消息的内部长度重置为缓冲区的实际长度。

另一个潜在问题的根源是DatagramPacket 类的 getData()方法,该方法总是返回缓冲区的原始大小,忽略了实际数据的内部偏移量和长度信息。

 2、TCP——Socket通信的实现

(1)客户端发送请求:Socket client=new Socket("目标IP地址",端口);

(2)服务端在端口建立监听:ServerSocket serverSocket=new ServerSocket(端口);

     等待客户端连接:Socket socket=serverSocket.accept();

(3)连接成功后,客户端就可以向服务端发送信息:通过流:

     Stringmessage=(new BufferReader(newInputStreamReader(System.in))).rendLine();--用户输入信息并读取

     PrintStreamout=newPrintStream(client.getOutputStream());//获取Socket的输出流,用来发送数据到服务端

     out.println("客户端向服务端发送信息" +message);

(4)服务端接受客户端发送过来的消息:获取Socket的输入流,用来接收从服务端发送过来的数据。

    BufferedReader br =  new BufferedReader(newInputStreamReader(client.getInputStream()));

     String message=br.readLine();//接受从客户端发送过来的数据

(5)服务端将接收到的数据在重新发送到客户端上面显示:获取Socket的输出流,用来向客户端发送数据 

     PrintStreamout = newPrintStream(client.getOutputStream());

    out.println("服务端向客户端发送信息" +message);

(6)客户端接收到服务端发送过来的数据并显示到控制台

    BufferedReader br =  new BufferedReader(newInputStreamReader(client.getInputStream()));

     String message=br.readLine();

(7)注意点:

     1、从服务器端接收数据有个时间限制(系统自设,也可以自己设置),超过了这个时间,便会抛出该异常。

     2、通讯是个多线程并发问题,可以在服务端建立一个多线程,将连接的客户端全部放入线程处理。

3、UDP——Socket通信的实现

(1)客户端创建DatagramSocket,用来在端口监听接收到的数据

    DatagramSocket client = newDatagramSocket(9000);

(2)客户端创建DatagramPacket,用来将数据与地址信息设置进去,打包

     Stringmassage="要发送的数据";

    DatagramPacket dp = newDatagramPacket(message.getBytes(),message.length(),address,3000);

(3)客户端通过DatagramSocket将包发送至服务端;若需要再次发送数据,重复2-5(可以设置最大发送次数)

     client.send(dp);

(4)服务端创建DatagramSocket用来监听客户端发送过来的连接请求:

    DatagramSocket socket= newDatagramSocket(8088)

(5)服务端创建一个接收数据用的包,包创建时要设置一个字节数组,接收的数据就存放在这个数组中,数组应当足够大。、

    byte[] data= new byte[100];

   DatagramPacket packet= new DatagramPacket(data,data.length);

(6) 服务端建立一个阻塞方法,直到远程计算机发送数据过来为止,该方法才会解除阻塞,并将数据等信息设置到包中。可以通过包来获取包中的相应信息(数据和地址信息);

   socket.receive(packet);

    Stringmassage=new String(data,0,package.getLenth(),"UTF-8");

(7)服务端向客户端发送消息。先获取客户端发送来过来的地址信息和端口信息,再发送信息

     Stringdata=message.getBytes("UTF-8");

     InetAddressaddress= packet.getAddress();

     int port =packet.getPort();

     packet =new DatagramPacket(data, data.length,address, port);

    socket.send(package);

(8)客户端创建一个接收数据用的包,包创建时要设置一个字节数组,接收的数据就存放在这个数组中,数组应当足够大。、

    byte[] data= new byte[100];

   DatagramPacket packet= new DatagramPacket(data,data.length);

(9) 客户端端建立一个阻塞方法,直到远程计算机发送数据过来为止,该方法才会解除阻塞,并将数据等信息设置到包中。

   client.receive(packet);

    Stringmassage=new String(data,0,package.getLenth(),"UTF-8");

(10)注意点:

     1、可以设置阻塞时间,ds.setSoTimeout(TIMEOUT);超过了这个时间,便会抛出该异常。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值