数据互发
- Send
package com.atguigu.ct.producer.Test.AA; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; /* *发送端 * 1,创建DatagramSocket对象 * 2,创建DatagramPacket对象,并封装数据 * 3,发送数据 * 4,释放流资源 */ public class UDPSend { @SuppressWarnings("resource") public static void main(String[] args) throws Exception { //这个是发送的线程 new Thread(()->{ Scanner sc = new Scanner(System.in); boolean b = true; DatagramSocket ds= null; while(b){ String next = sc.next(); byte[] data = next.getBytes(); InetAddress inet; try { inet = InetAddress.getByName("10.0.142.9"); DatagramPacket dp = new DatagramPacket(data, data.length,inet,6666); ds = new DatagramSocket(); ds.send(dp); } catch (Exception e) { e.printStackTrace(); } }}).start(); //这个是接收的线程 new Thread(new Runnable() { @Override public void run() { // 接受 DatagramSocket ds; try { ds = new DatagramSocket(6667); byte[] bytes = new byte[1024 * 64]; boolean b = true; while (b) { DatagramPacket dp = new DatagramPacket(bytes, bytes.length); ds.receive(dp); // 拆包 // ip , 端口号 , 数据 String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); int len = dp.getLength(); System.out.println(ip + " : " + port + "\t" + new String(bytes, 0, len)); } // ds.close(); } catch (Exception e) { e.printStackTrace(); } } }).start(); } } 复制代码
- Receive
package com.atguigu.ct.producer.Test.AA; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; /* * UDP接收端 * * 1,创建DatagramSocket对象 * 2,创建DatagramPacket对象 * 3,接收数据存储到DatagramPacket对象中 * 4,获取DatagramPacket对象的内容 * 5,释放流资源 */ public class UDPReceive { @SuppressWarnings("resource") public static void main(String[] args) throws Exception { //接收线程 new Thread(new Runnable() { @Override public void run() { // 接受 DatagramSocket ds; try { ds = new DatagramSocket(6666); byte[] bytes = new byte[1024*64]; boolean b = true; while(b){ DatagramPacket dp = new DatagramPacket(bytes, bytes.length); ds.receive(dp); // 拆包 // ip , 端口号 , 数据 String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); int len = dp.getLength(); System.out.println(ip +" : "+port +"\t"+ new String(bytes, 0, len)); } // ds.close(); } catch (Exception e) { e.printStackTrace(); } } }).start(); //发送线程 new Thread(new Runnable() { @Override public void run() { Scanner sc = new Scanner(System.in); boolean b = true; DatagramSocket ds= null; while(b){ String next = sc.next(); byte[] data = next.getBytes(); InetAddress inet; try { inet = InetAddress.getByName("10.0.142.9"); DatagramPacket dp = new DatagramPacket(data, data.length,inet,6667); ds = new DatagramSocket(); ds.send(dp); } catch (Exception e) { e.printStackTrace(); } } } }).start(); } } 复制代码
一个就是用while实现了多个线程的数据互发,还有一个就是发送方的端口是随机的,如果不指定的话
TCP通信
TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象
区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据
而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接
在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端
通信时,首先创建代表服务器端的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信
ServerSocket
在开发TCP程序时,首先需要创建服务器端程序。JDK的java.net包中提供了一个ServerSocket类,该类的实例对象可以实现一个服务器段的程序。通过查阅API文档可知,ServerSocket类提供了多种构造方法,接下来就对ServerSocket的构造方法进行逐一地讲解
使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)
接下来学习一下ServerSocket的常用方法,如表所示
ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行
Socket
讲解了ServerSocket对象可以实现服务端程序,但只实现服务器端程序还不能完成通信,此时还需要一个客户端程序与之交互,为此JDK提供了一个Socket类,用于实现TCP客户端程序
Socket类同样提供了多种构造方法,接下来就对Socket的常用构造方法进行详细讲解
使用该构造方法在创建Socket对象时,会根据参数去连接在指定地址和端口上运行的服务器程序,其中参数host接收的是一个字符串类型的IP地址
该方法在使用上与第二个构造方法类似,参数address用于接收一个InetAddress类型的对象,该对象用于封装一个IP地址
在以上Socket的构造方法中,最常用的是第一个构造方法
接下来学习一下Socket的常用方法,如表所示
在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信
接下来通过一张图来描述服务器端和客户端的数据传输,如下图所示
简单的TCP网络程序
了解了ServerSocket、Socket类的基本用法,为了让大家更好地掌握这两个类的使用,接下来通过一个TCP通信的案例来进一步学习
要实现TCP通信需要创建一个服务器端程序和一个客户端程序,为了保证数据传输的安全性,首先需要实现服务器端程序
- TCP 服务器端
/* * TCP 服务器端 * * 1,创建服务器ServerSocket对象(指定服务器端口号) * 2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象 * 3,给客户端反馈信息 * 4,关闭流资源 */ public class TCPServer { public static void main(String[] args) throws IOException { //1,创建服务器ServerSocket对象(指定服务器端口号) ServerSocket ss = new ServerSocket(6789); //2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象 Socket s = ss.accept(); //3,给客户端反馈信息 /* * a,获取客户端的输出流 * b,在服务端端,通过客户端的输出流写数据给客户端 */ //a,获取客户端的输出流 OutputStream out = s.getOutputStream(); //b,在服务端端,通过客户端的输出流写数据给客户端 out.write("你已经连接上了服务器".getBytes()); //4,关闭流资源 out.close(); s.close(); //ss.close(); 服务器流 通常都是不关闭的 } } 复制代码
- TCP 客户端
/* * TCP 客户端 * * 1,创建客户端Socket对象,(指定要连接的服务器地址与端口号) * 2,获取服务器端的反馈回来的信息 * 3,关闭流资源 */ public class TCPClient { public static void main(String[] args) throws IOException { //1,创建客户端Socket对象,(指定要连接的服务器地址与端口号) Socket s = new Socket("10.0.142.9", 6789); //2,获取服务器端的反馈回来的信息 InputStream in = s.getInputStream(); //获取获取流中的数据 byte[] buffer = new byte[1024]; //把流中的数据存储到数组中,并记录读取字节的个数 int length = in.read(buffer); //显示数据 System.out.println( new String(buffer, 0 , length) ); //3,关闭流资源 in.close(); s.close(); } } 复制代码
需要注意的是 首先必須开启服务端,不然会连不上的。
IO流和网络编程综合练习(聊天室)
- 服务器
public class Server2 { Socket socket = null; boolean flag = true; public void run() throws Exception{ //创建服务器socket对象 ServerSocket ss = new ServerSocket(12343); while(flag){ //获取socket socket = ss.accept(); //写 new Thread(new Runnable() { @Override public void run() { //获取输出流 try { Scanner sc = new Scanner(System.in); OutputStream os = socket.getOutputStream(); while(true){ String data = sc.nextLine(); os.write(data.getBytes()); } } catch (IOException e) { e.printStackTrace(); } } }).start(); //读 new Thread(new Runnable() { @Override public void run() { // 获取输入流 try { InputStream is = socket.getInputStream(); //一行一行读,设置编码 BufferedReader bd = new BufferedReader(new InputStreamReader(is, "utf-8")); String data = null; while((data = bd.readLine())!= null){ //明确是谁发送过来的(获取ip) System.out.println(socket.getInetAddress().getHostAddress()+"客户端发送到服务器 : "+data); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } ss.close(); } public static void main(String[] args) throws Exception { Server2 server2 = new Server2(); server2.run(); } } 复制代码
- 客户端
public class Client2 { public static void main(String[] args) throws Exception { //创建客户端socket对象 Socket socket = new Socket("127.0.0.1",12343); //获取输入流 final InputStream is = socket.getInputStream(); //一行一行读。设置编码格式 new Thread(new Runnable() { @Override public void run() { try { while(true){ byte[] by = new byte[1024]; int len = is.read(by); System.out.println("服务器返回数据 : "+new String(by,0,len)); } } catch (Exception e) { e.printStackTrace(); } } }).start(); //获取输出流 OutputStream os = socket.getOutputStream(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os,"utf-8")); Scanner sc = new Scanner(System.in); boolean flag = true; while(flag){ String data = sc.nextLine(); bw.write(data); bw.newLine(); bw.flush(); } is.close(); os.close(); socket.close(); } } 复制代码
知识点总结
- IP地址:用来唯一表示我们自己的电脑的,是一个网络标示
- 端口号: 用来区别当前电脑中的应用程序的
- UDP: 传送速度快,但是容易丢数据,如视频聊天
- TCP: 传送稳定,不会丢失数据,如文件的上传、下载
- UDP程序交互的流程
- 发送端
1,创建DatagramSocket对象 2,创建DatagramPacket对象,并封装数据 3,发送数据 4,释放流资源 复制代码
- 接收端
1,创建DatagramSocket对象 2,创建DatagramPacket对象 3,接收数据存储到DatagramPacket对象中 4,获取DatagramPacket对象的内容 5,释放流资源 复制代码
- TCP程序交互的流程
- 客户端
1,创建客户端的Socket对象 2,获取Socket的输出流对象 3,写数据给服务器 4,获取Socket的输入流对象 5,使用输入流,读反馈信息 6,关闭流资源 复制代码
- 服务器端
1,创建服务器端ServerSocket对象,指定服务器端端口号 2,开启服务器,等待着客户端Socket对象的连接,如有客户端连接,返回客户端的Socket对象 3,通过客户端的Socket对象,获取客户端的输入流,为了实现获取客户端发来的数据 4,通过客户端的输入流,获取流中的数据 5,通过客户端的Socket对象,获取客户端的输出流,为了实现给客户端反馈信息 6,通过客户端的输出流,写数据到流中 7,关闭流资源 复制代码
结尾
网络编程基础就讲这么多了,大家可以操作一下试试,下面的打算讲讲多线程,锁咯