网络编程(2)

网络编程(2)

Soket类

-1-综述:

网络上具有唯一标识的IP地址和端口号组合在一起构成唯一能识别的标识符套接字(Socket)。

通信的两端都要有Socket,是两台机器间通信的端点

Socket允许程序把网络连接当成一个流数据在两个Socket间通过IO传输。

一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。

-2- Socket分类:

    • 流套接字(stream socket):使用TCP提供可依赖的字节流服务
      • ServerSocket:此类实现TCP服务器套接字。服务器套接字等待请求通过网络传入。
      • Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
    • 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
      • DatagramSocket:此类表示用来发送和接收UDP数据报包的套接字。

-3-  Socket相关类API

1 ServerSocket类

ServerSocket类的构造方法:

  • ServerSocket(int port) :创建绑定到特定端口的服务器套接字。

ServerSocket类的常用方法:

  • Socket accept():侦听并接受到此套接字的连接。
2 Socket类

Socket类的常用构造方法

  • public Socket(InetAddress address,int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
  • public Socket(String host,int port):创建一个流套接字并将其连接到指定主机上的指定端口号。

Socket类的常用方法

  • public InputStream getInputStream():返回此套接字的输入流,可以用于接收消息
  • public OutputStream getOutputStream():返回此套接字的输出流,可以用于发送消息
  • public InetAddress getInetAddress():此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null。
  • public InetAddress getLocalAddress():获取套接字绑定的本地地址。
  • public int getPort():此套接字连接到的远程端口号;如果尚未连接套接字,则返回 0。
  • public int getLocalPort():返回此套接字绑定到的本地端口。如果尚未绑定套接字,则返回 -1。
  • public void close():关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和 OutputStream。
  • public void shutdownInput():如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将返回 EOF(文件结束符)。 即不能在从此套接字的输入流中接收任何数据。
  • public void shutdownOutput():禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。

注意:先后调用Socket的shutdownInput()和shutdownOutput()方法,仅仅关闭了输入流和输出流,并不等于调用Socket的close()方法。在通信结束后,仍然要调用Scoket的close()方法,因为只有该方法才会释放Socket占用的资源,比如占用的本地端口号等。

public class TCPtest {
    @Test
    public void client(){
        //客户端
        //1、创建Socket实例(需要声明对方的端口号,创建对方的ip地址的实例
        //2、通过数据流的方式传输数据
        //3、关闭数据流和socket
        OutputStream os = null;
        Socket socket = null;
        try {
            InetAddress inetAddress = InetAddress.getByName("127.0.0.1");//创建对方的ip实例
            int port = 8989;//声明对方的端口号
            socket = new Socket(inetAddress,port);//输入实参,ip实例与port端口号,实现socket实例的创建

            //传输数据流
            os = socket.getOutputStream();
            os.write("hello,i'm client,please give me more advice in the future.".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (socket!=null)
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (os!=null)
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void server(){
        //创建一个SeverSocket
        //1、创建ServerSocket实例(需要声明端口号和ip地址
        //2、通过数据流的方式接收数据,调用accept()方法
        //3、关闭数据流和ServerSocket、Socket
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        try {
            int port = 8989;//创建端口号
            serverSocket = new ServerSocket(port);
            socket = serverSocket.accept();//调用accept()方法
            System.out.println("Server port is open.");

            System.out.println("receive the connection from"+socket.getInetAddress());

            //通过io流的方式接收数据流
            is = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = is.read(buffer))!=-1){
                String str = new String(buffer,0,len);
                System.out.println(str);
            }

            System.out.println("the data has been accepted.");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (socket!=null)
                socket.close();//关闭Socket、SeverSocket
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (serverSocket!=null)
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is!=null)
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(先开启客户端,再开启服务端。)

传输层协议:TCP与UDP协议

通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。

java.net 包中提供了两种常见的网络协议的支持:

UDP:用户数据报协议(User Datagram Protocol)。

TCP:传输控制协议 (Transmission Control Protocol)。

1TCP协议:

  • TCP协议进行通信的两个应用进程:客户端、服务端。

使用TCP协议前,须先建立TCP连接,形成基于字节流的传输数据通道

  • 传输前,采用“三次握手”方式,点对点通信,是可靠
    • TCP协议使用重发机制,当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体确认信息,如果没有收到另一个通信实体确认信息,则会再次重复刚才发送的消息。

在连接中可进行大数据量的传输

传输完毕,需释放已建立的连接,效率低

 三次握手

TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

  • 第一次握手,客户端向服务器端发起TCP连接的请求
  • 第二次握手,服务器端发送针对客户端TCP连接请求的确认
  • 第三次握手,客户端发送确认的确认

1、客户端会随机一个初始序列号seq=x,设置SYN=1 ,表示这是SYN握手报文。然后就可以把这个 SYN 报文发送给服务端了,表示向服务端发起连接,之后客户端处于同步已发送状态。

2、服务端收到客户端的 SYN 报文后,也随机一个初始序列号(seq=y),设置ack=x+1,表示收到了客户端的x之前的数据,希望客户端下次发送的数据从x+1开始。 设置 SYN=1 和 ACK=1。表示这是一个SYN握手和ACK确认应答报文。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于同步已接收状态。

3、客户端收到服务端报文后,还要向服务端回应最后一个应答报文,将ACK置为 1 ,表示这是一个应答报文 ack=y+1 ,表示收到了服务器的y之前的数据,希望服务器下次发送的数据从y+1开始。 最后把报文发送给服务端,这次报文可以携带数据,之后客户端处于 连接已建立 状态。服务器收到客户端的应答报文后,也进入连接已建立状态。

完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。

四次挥手

TCP协议中,在发送数据结束后,释放连接时需要经过四次挥手。

  • 第一次挥手:客户端向服务器端提出结束连接,让服务器做最后的准备工作。此时,客户端处于半关闭状态,即表示不再向服务器发送数据了,但是还可以接受数据。
  • 第二次挥手:服务器接收到客户端释放连接的请求后,会将最后的数据发给客户端。并告知上层的应用进程不再接收数据。
  • 第三次挥手:服务器发送完数据后,会给客户端发送一个释放连接的报文。那么客户端接收后就知道可以正式释放连接了。
  • 第四次挥手:客户端接收到服务器最后的释放连接报文后,要回复一个彻底断开的报文。这样服务器收到后才会彻底释放连接。这里客户端,发送完最后的报文后,会等待2MSL,因为有可能服务器没有收到最后的报文,那么服务器迟迟没收到,就会再次给客户端发送释放连接的报文,此时客户端在等待时间范围内接收到,会重新发送最后的报文,并重新计时。如果等待2MSL后,没有收到,那么彻底断开。

2UDP协议:

  • UDP协议进行通信的两个应用进程:发送端、接收端。

将数据、源、目的封装成数据包(传输的基本单位),不需要建立连接

发送不管对方是否准备好,接收方收到也不确认,不能保证数据的完整性,故是不可靠的

每个数据报的大小限制在64K

发送数据结束时无需释放资源,开销小,通信效率高

发送端程序包含以下四个基本的步骤:

  • 创建DatagramSocket :默认使用系统随机分配端口号。
  • 创建DatagramPacket:将要发送的数据用字节数组表示,并指定要发送的数据长度,接收方的IP地址和端口号。
  • 调用 该DatagramSocket 类对象的 send方法 :发送数据报DatagramPacket对象。
  • 关闭DatagramSocket 对象:发送端程序结束,关闭通信套接字。

接收端程序包含以下四个基本的步骤 :

  • 创建DatagramSocket :指定监听的端口号。
  • 创建DatagramPacket:指定接收数据用的字节数组,起到临时数据缓冲区的效果,并指定最大可以接收的数据长度。
  • 调用 该DatagramSocket 类对象的receive方法 :接收数据报DatagramPacket对象。。
  • 关闭DatagramSocket :接收端程序结束,关闭通信套接字。

  • 适用场景:音频、视频和普通数据的传输。例如视频会议

TCP生活案例:打电话

UDP生活案例:发送短信、发电报

public class UDPTest {
    @Test
    public void sender()throws Exception{
        //1、创建DatagramSocket的实例
        DatagramSocket ds = new DatagramSocket();

        //将目的地的数据和端口号封装再DatagramPocket中
        InetAddress inet = InetAddress.getByName("127.0.0.1");
        int port = 9090;
        byte[] bytes = "i'm sending".getBytes("utf-8");
        DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,inet,port);
        //发送数据
        ds.send(packet);

        ds.close();
    }

    @Test
    public void receiver()throws Exception{
        int port = 9090;
        DatagramSocket ds = new DatagramSocket(port);

        byte[] buffer=  new byte[1024*64];
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

        ds.receive(packet);
        String str = new String(packet.getData(),0,packet.getLength());

        ds.close();
    }
}

补充:URL  Uniform Resouce Locator(统一资源定位服务)

1、作用:应该具体的url对应着的互联网上的某一资源的地址

2、URL的格式:

http://192.168.21.107:8080/example/abcd.jpg

应用层协议  ip地址  端口号  资源地址  参数列表

需要用Tomcat访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值