网络编程

网络编程入门

网络编程三要素--概述



网络编程三要素---IP

在网络中我们使用ip来标识设备并通过ip来访问相应的设备。实际上我们访问网页也是依据ip,因为ip难以记忆,于是通过DNS协议
将域名转化为ip来访问相应的网页服务器



网络编程--常见命令

ping命令:1.检测你现在的这台电脑和你需要连接的电脑网络是否畅通2.确定连接电脑的ip

  • 特殊的ip地址

三要素---InetAddress类的使用(对ip地址的描述)


这个类没有向外提供构造方法,但是提供了静态方法来创建对象

package com.interet;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressTest {
   public static void main(String[] args) throws UnknownHostException {
       /*static InetAddress getByName(String host) 确定主机名称的IP地址。
       String getHostAddress() 返回文本显示中的IP地址字符串。
       String getHostName() 获取此IP地址的主机名。
*/
       final InetAddress address = InetAddress.getByName("LAPTOP-7KNA7AM8");//我的电脑的主机名
       System.out.println(address.getHostName());//返回主机名

       System.out.println(address.getHostAddress());//返回主机ip
   }

}

三要素--端口

网络编程三要素--协议


  • 在建立连接后才会进行发送

UDP--发送端

package com.udp;

import java.io.IOException;
import java.net.*;

//发送端
public class Client {
    public static void main(String[] args) throws IOException {
        //1.寻找码头
        DatagramSocket ds = new DatagramSocket();//无参构造将会随机使用一个端口进行发送数据
        // 2.打包数据
        //参数一:数据
        String data = "你好 世界!";
        byte[] bytes = data.getBytes();//将字符串转化为字节数组
        //参数二:接收端ip
        InetAddress address = InetAddress.getByName("127.0.0.1");
        //参数三:接收机的端口号
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        //3.由码头发送数据
        ds.send(dp);
        //4.关闭资源
        ds.close();
    }

}

UDP-接收端

package com.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Server {
   public static void main(String[] args) throws IOException {
       //1.找码头
       DatagramSocket ds = new DatagramSocket(10000);
       //2.创建新的箱子
       byte[] bytes = new byte[1024];//字节数组用来接收传输的数据
       DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

       //3.将数据放到新的箱子中
       ds.receive(dp);//如果此时还没有接收到发送端的时候,程序会阻塞在此处 
       //4.从箱子中获取礼物
       String data = new String(dp.getData(),0,dp.getLength());
       System.out.println(data);
       //5.关闭资源
       ds.close();
   }
}
  • 注意点:
    1.要先运行接收端再运行发送端
    2.如果接收端没有接收到数据,会进行死等
    3.再接收数据时,需要调用一个getLength方法,表示接收到了多少字节(因为用于接收的数组没有接收满出现大量的空格)

UDP练习

package com.udppractice;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

//客户端
/*
要求:
数据来自键盘录入,直到输入的数据是886,发送数据结束
 */
public class Client {
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        DatagramSocket ds = null;
        //1.准备码头
        ds = new DatagramSocket();//由随机端口发出数据
        //2.打包数据
        while (true){
            System.out.println("请输入要发送的数据:");
            String data = sc.nextLine();
            if("886".equals(data)){
                break;
            }
            byte[] bytes = data.getBytes();
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length,InetAddress.getByName("127.0.0.1"),10005);
            //3.发送数据
            ds.send(dp);
        }

        //4.关闭资源
        ds.close();

    }
}
package com.udppractice;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Server {
    public static void main(String[] args) throws IOException {
        //1.准备码头
        DatagramSocket ds =  new DatagramSocket(10005);//在10005端口中接收数据
        while (true) {

            //2.准备包裹
            byte[] bytes = new byte[1024];//接收数据的数组
            DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

            //3.码头接收数据并放入到包裹中
            ds.receive(dp);
            //4.打开包裹获取礼物
            System.out.println(new String(dp.getData(),0,dp.getLength()));
        }
        //5.关闭资源
        //ds.close();
    }

}

注意:报BindException
在之前的代码中我们将 DatagramSocket ds = new DatagramSocket(10005);放在了循环中,这样在接收了第一句话后就爆出了
异常

当我们接收了第一句话使用了10005这个端口,这个端口已经被占用了,循环第二次他将认为这是地外一个程序,将会显示端口绑定异常

UDP三种通讯方式



组播代码的实现



package com.udp;

import java.io.IOException;
import java.net.*;

//组播客户端
public class MulClient {
    public static void main(String[] args) throws IOException {
        //1.寻找码头
        DatagramSocket ds = new DatagramSocket();
        //2.将数据添加到箱子里面
        String data = "hello world";
        final byte[] bytes = data.getBytes();
        InetAddress ip = InetAddress.getByName("224.0.1.0");//地址要换成组播地址
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, ip, port);
        //3.发送数据
        ds.send(dp);
        ds.close();
    }
}
package com.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class MulServer {
    public static void main(String[] args) throws IOException {
        //1.创建码头
        MulticastSocket ms = new MulticastSocket(10000);
        //2.创建箱子
        DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
        //3."把当前计算机绑定一个组播地址,表示添加到这一组中"
        ms.joinGroup(InetAddress.getByName("224.0.1.0"));
        ms.receive(dp);
        System.out.println(new String(dp.getData(), 0, dp.getLength()));
        ms.close();
    }
}

UDp-广播代码实现



广播的接收端和单播一样

package com.udp;

import java.io.IOException;
import java.net.*;

//广播-客户端
public class BroClient {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket();
        String data = "hello 广播";
        byte[] buf = data.getBytes();
        InetAddress ip = InetAddress.getByName("255.255.255.255");//地址为广播地址
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(buf, buf.length, ip, port);
        ds.send(dp);
        ds.close();
    }
}
package com.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

//广播接收端
public class BroServer {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket(10000);
        byte[] buf = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, buf.length);
        ds.receive(dp);
        System.out.println(new String(dp.getData(), 0, dp.getLength()));
        ds.close();
    }
}

Tcp部分

tcp--客户端

  • 客户端书写步骤
package com.tcp;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

//tcp发送端
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建一个Socket对象
        //参数指明要发送到的主机和端口
        Socket socket = new Socket("127.0.0.1",10006);
        //2.创建io流并传输数据
        final OutputStream outputStream = socket.getOutputStream();
        //写入数据
        outputStream.write("hello".getBytes());
        //3.关闭资源
        outputStream.close();
        socket.close();
    }
}

但是它会出现连接异常,因为tcp在发送数据之前会和服务端进行连接,而我们的服务端还没有写,则无法连接成功,所有出现异常

Tcp-服务器

package com.tcp;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

//tcp发送端
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建一个Socket对象
        //参数指明要发送到的主机和端口
        Socket socket = new Socket("127.0.0.1",10006);
        //2.创建io流并传输数据
        final OutputStream outputStream = socket.getOutputStream();
        //写入数据
        outputStream.write("hello".getBytes());
        //3.关闭资源
        outputStream.close();
        socket.close();
    }
}

Tcp原理分析


  • 对tcp数据传输与接收的理解
    Server先执行,到accept方法处阻塞等待连接,Client执行,Socket的构造方法进行三次握手进行连接。在进行数据传输时,实行一边传输一边接收。当传输和接收都完毕时才进行资源关闭

三次握手

是在客户端床架Socket对象时,通过三次握手去保证和服务端进行连接畅通

四次挥手

是客户端和服务器取消两者连接的时候,用来保证成功终止这个连接的

  • 为什么连接的时候只用3次握手,但是在断开连接的时候却需要四次挥手呢?
    因为在进行断开连接的时候,连接已经存在。在断开连接之前需要保证所有的数据都处理完毕。所有服务器端需要在数据处理完毕
    后发送一个确认断开的信号

Tcp-练习1


  • 理想写法
package com.tcpprictice;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

//发送数据,并接收服务器的反馈
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建Socket对象
        Socket socket = new Socket("127.0.0.1", 10001);
        //2.获取输出流,并写入数据
        OutputStream out = socket.getOutputStream();
        out.write("hello".getBytes());
        //接收反馈
        final InputStream in = socket.getInputStream();
        int b ;
        while ((b = in.read()) != -1) {
            System.out.print((char) b);
        }
        //3.关闭资源
        in.close();
        out.close();
        socket.close();

    }
}
package com.tcpprictice;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

//接收数据,给出反馈+
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取Socket对象
        ServerSocket server = new ServerSocket(10001);
        //2.获取输入流,并读取数据
        //等待连接
        Socket socket = server.accept();//接收数据
        InputStream in = socket.getInputStream();//获取输入流
        int b;
        while ((b = in.read()) != -1) {
            System.out.print((char) b);
        }
        //给出反馈
        final OutputStream outputStream = socket.getOutputStream();
        outputStream.write("你谁啊".getBytes());
        //3.关闭资源
        outputStream.close();
        in.close();
        socket.close();
        server.close();
    }
}
  • 运行结果

    服务端接收到hello,客户端并没有接受到反馈
  • 问题分析
    这个结果本质上还是:
  • 解决方案--提前关流

    我们的关流方法,在关闭流的同时也关闭了Socket的使用。是否有一个方法可以仅仅关闭流,而不关闭SOcket

package com.tcpprictice;

import java.io.*;
import java.net.Socket;

import static java.lang.System.in;

//发送数据,并接收服务器的反馈
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建Socket对象
        Socket socket = new Socket("127.0.0.1", 10001);
        //2.获取输出流,并写入数据
        OutputStream out = socket.getOutputStream();
        out.write("hello".getBytes());
        socket.shutdownOutput();//仅仅关闭流,并写一个结束标记(不会关闭socket)
        //等待反馈
        //接收反馈(接收的数据为中文,用字节流将会乱码)
       /* final InputStream in = socket.getInputStream();
        int b ;
        while ((b = in.read()) != -1) {
            System.out.print((char) b);
        }*/
        //使用转换流接收中文
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String line;
        while (null != (line = br.readLine())) {
            System.out.println(line);
        }
        //3.关闭资源
        br.close();
        out.close();
        socket.close();

    }
}
package com.tcpprictice;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

//接收数据,给出反馈+
public class Server {
    public static void main(String[] args) throws IOException {
        //1.获取Socket对象
        ServerSocket server = new ServerSocket(10001);
        //2.获取输入流,并读取数据
        //等待连接
        Socket socket = server.accept();//接收数据
        InputStream in = socket.getInputStream();//获取输入流
        int b;
        while ((b = in.read()) != -1) {
            System.out.print((char) b);
        }
        //给出反馈(也可以换成转换流)
      final OutputStream outputStream = socket.getOutputStream();
        outputStream.write("你谁啊".getBytes());
        //3.关闭资源
        outputStream.close();
        in.close();
        socket.close();
        server.close();
    }
}

Tcp-练习2


  • 该处的代码见<<java网络编程部分》

    后面多线程优化的部分见<<java网络编程>>部分

  • 总结
    多线程的处理,可以优化多个客户端同时访问的处理,增加处理速度,可以发送数据多次

posted @ 2023-07-28 16:02  一往而深,  阅读(1)  评论(0编辑  收藏  举报