网络通信【UDP】【TCP】

网络编程

网络编程可以让程序与网络上的其他设备中的程序进行数据交互

网络通信的基本模式

常见的通信模式有两种:Client-Server(CS),Browser/Server(BS)

 网络通信的三要素

IP地址:设备在网络中的地址,是唯一的标识

端口:应用程序在设备中的唯一的标识

协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议

IP地址

        IP(Internet Protocal);全称“互联网协议地址”,是分配给上网设备的唯一标志

        常见的IP分类为:IPv4和IPv6

 IP地址基本寻路

本地没有,会发至运行商的dns服务器,由域名返回IP地址

 IP地址形式 

 IP的常用命令

 特殊的IP地址

 IP地址操作类——InetAddress

        此类表示Internet协议(IP)地址

InetAddress API:

package com.itxue.d1_inetAddress;

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

public class InetAddressDemo1 {
    public static void main(String[] args) throws Exception {
//        获取本机的地址对象
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());
        System.out.println(ip1.getHostAddress());


//      获取域名的ip对象
        InetAddress ip2 = InetAddress.getByName("www.baidu.com");
        System.out.println(ip2.getHostName());
        System.out.println(ip2.getHostAddress());

//      获取公网的IP对象
        InetAddress ip3 = InetAddress.getByName("14.215.177.38");
        System.out.println(ip3.getHostName());
        System.out.println(ip3.getHostAddress());

//        判断是否连通;ping 5s之前测试是否可通
        System.out.println(ip3.isReachable(5000));
    }
}

端口号

端口号:标识正在计算机设备上运行的进程(程序),被规定为一个16位的二进制,范围是0-65535

端口类型

 注意:我们自己开发的程序选择注册端口,且一个设备中不能出现连个程序的端口号一样,否则出错

通信协议

连接和通信数据的规则被称为网络通信协议

 网络通信协议的两套参考模型

TCP 

 TCP三次握手确立关系

TCP四次挥手断开连接

 UDP协议

 UDP通信

1.

 2.

 

package com.itxue.d2_udp;

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

/*
        接收端
 */
public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        DatagramSocket socket = new DatagramSocket(9999);

        byte[] buffer = new byte[64*1024];
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
//等待接收数据
        socket.receive(packet);
//取数数据
        String rs = new String(buffer);
        System.out.println(rs);

        socket.close();
    }
}

package com.itxue.d2_udp;

import sun.security.pkcs11.wrapper.CK_SSL3_KEY_MAT_OUT;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/*
        发送端
 */
public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //        创建发送端对象,发送端自带默认的端口号(人)
        DatagramSocket socket = new DatagramSocket();
//          创建一个数据包对象包装数据(韭菜盘子)
        /*
        public DatagramPacket(byte buf[], int length,
                          InetAddress address, int port)
         */
        byte[] buffer = "我是小学生".getBytes();
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(),9999);

//        发送数据出去
        socket.send(packet);

        socket.close();
    }
}

  

案例:使用UDP通信实现:多发多收消息

 

 

package com.itxue.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/*
        发送端:多发 多收
 */
public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //        创建发送端对象,发送端自带默认的端口号(人)
        DatagramSocket socket = new DatagramSocket();

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入;");
            String s = sc.nextLine();
            if("exit".equals(s)){
                System.out.println("离线成功");
                socket.close();
                break;
            }
            //          创建一个数据包对象包装数据(韭菜盘子)
            byte[] buffer = s.getBytes();



            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 8888);

//        发送数据出去
            socket.send(packet);
        }

    }
}
package com.itxue.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
        接收端
 */
public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        DatagramSocket socket = new DatagramSocket(8888);

        byte[] buffer = new byte[64*1024];
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
//等待接收数据
        while (true) {
            socket.receive(packet);
//取数数据
            String rs = new String(buffer);
            System.out.println(rs);
        }

    }
}

UDP通信——广播,组播

 UDP实现广播

package com.itxue.d4_udp3_broadcast.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/*
        发送端:多发 多收
 */
public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //        创建发送端对象,发送端自带默认的端口号(人)
        DatagramSocket socket = new DatagramSocket();

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入;");
            String s = sc.nextLine();
            if("exit".equals(s)){
                System.out.println("离线成功");
                socket.close();
                break;
            }
            //          创建一个数据包对象包装数据(韭菜盘子)
            byte[] buffer = s.getBytes();



            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("255.255.255.255"), 9999);

//        发送数据出去
            socket.send(packet);
        }

    }
}

 UDP实现组播

package com.itxue.d5_udp4_multicast.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;

/*
        接收端
 */
public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端启动");
        MulticastSocket socket = new MulticastSocket(9999);
        socket.joinGroup(InetAddress.getByName("224.0.1.1"));
        byte[] buffer = new byte[64*1024];
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
//等待接收数据
        while (true) {
            socket.receive(packet);
//取数数据
            String rs = new String(buffer);
            System.out.println(rs);
        }

    }
}
package com.itxue.d5_udp4_multicast.d3_udp2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/*
        发送端:多发 多收
 */
public class ClientDemo1 {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        //        创建发送端对象,发送端自带默认的端口号(人)
        DatagramSocket socket = new DatagramSocket();

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入;");
            String s = sc.nextLine();
            if("exit".equals(s)){
                System.out.println("离线成功");
                socket.close();
                break;
            }
            //          创建一个数据包对象包装数据(韭菜盘子)
            byte[] buffer = s.getBytes();



            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("224.0.1.1"), 9999);

//        发送数据出去
            socket.send(packet);
        }

    }
}

 TCP通信

 客户端发送消息

package com.itxue.d6_socket1;

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

public class ClientDemo1 {
    public static void main(String[] args) {
        Socket socket = null;
        try {
//            创建Socket通信管道请求与服务器连接
//            参数一:服务端的IP地址  参数二:服务端的端口
            socket = new Socket("127.0.0.1",7777);
//          2.从socket通信管道中得到一个字节输出流,负责发送数据
            OutputStream os = socket.getOutputStream();
//          3.把低级字节流包装成打印流
            PrintStream ps = new PrintStream(os);
//          4.发送信息
            ps.print("我是TCP的客户端,我已经与你连接,并发出邀请");
            ps.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

TCP 服务端

package com.itxue.d6_socket1;

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

public class ServerDemo2 {
    public static void main(String[] args) {
//        注册端口
        try {
            ServerSocket serverSocket = new ServerSocket(7777);
//      调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
            Socket socket = serverSocket.accept();
//             从socket管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String msg;
            if((msg = br.readLine())!= null){
                System.out.println(socket.getRemoteSocketAddress()+"说了"+msg);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.itxue.d6_socket1;

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

public class ClientDemo1 {
    public static void main(String[] args) {
        Socket socket = null;
        try {
//            创建Socket通信管道请求与服务器连接
//            参数一:服务端的IP地址  参数二:服务端的端口
            socket = new Socket("127.1.0.0",7777);
//          2.从socket通信管道中得到一个字节输出流,负责发送数据
            OutputStream os = socket.getOutputStream();
//          3.把低级字节流包装成打印流
            PrintStream ps = new PrintStream(os);
//          4.发送信息
            ps.println("我是TCP的客户端,我已经与你连接,并发出邀请");
            ps.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

 总结

 TCP通信实现:多发消息和多收消息

package com.itxue.d6_socket2.d6_socket1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo2 {
    public static void main(String[] args) {
//        注册端口
        try {
            ServerSocket serverSocket = new ServerSocket(7777);
//      调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道
            Socket socket = serverSocket.accept();
//             从socket管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String msg;
            while((msg = br.readLine())!= null){
                System.out.println(socket.getRemoteSocketAddress()+"说了"+msg);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.itxue.d6_socket2.d6_socket1;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo1 {
    public static void main(String[] args) {
        Socket socket = null;
        try {
//            创建Socket通信管道请求与服务器连接
//            参数一:服务端的IP地址  参数二:服务端的端口
            socket = new Socket("127.1.0.0",7777);
//          2.从socket通信管道中得到一个字节输出流,负责发送数据
            OutputStream os = socket.getOutputStream();
//          3.把低级字节流包装成打印流
            PrintStream ps = new PrintStream(os);
//          4.发送信息
            Scanner sc = new Scanner(System.in);
            while(true) {
                System.out.println("请输入消息:");
                String s = sc.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

线程与TPC通信(可以处理多个服务端信息) 

package com.itxue.d7_socket3.d6_socket2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ServerReaderThread extends Thread{

    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    public void run() {
        try {
            //            3. 从socket管道中得到一个字节输入流

            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String msg;
            while ((msg = br.readLine()) != null) {
                System.out.println(socket.getRemoteSocketAddress() + "说了" + msg);
            }
        } catch (IOException e) {
            System.out.println(socket.getRemoteSocketAddress()+"下线了!");
        }

    }
}
package com.itxue.d7_socket3.d6_socket2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo2 {
    public static void main(String[] args) {
//        1.注册端口
        try {
            ServerSocket serverSocket = new ServerSocket(7777);
            while(true) {
//     2. 每接收到一个客户端的Socket管道,交给独立的线程负责读取线程
            Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress()+"他上线了");
//开始创建线程处理socket
            new ServerReaderThread(socket).start();

            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.itxue.d7_socket3.d6_socket2;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo1 {
    public static void main(String[] args) {
        Socket socket = null;
        try {
//            创建Socket通信管道请求与服务器连接
//            参数一:服务端的IP地址  参数二:服务端的端口
            socket = new Socket("127.1.0.0",7777);
//          2.从socket通信管道中得到一个字节输出流,负责发送数据
            OutputStream os = socket.getOutputStream();
//          3.把低级字节流包装成打印流
            PrintStream ps = new PrintStream(os);
//          4.发送信息
            Scanner sc = new Scanner(System.in);
            while(true) {
                System.out.println("请输入消息:");
                String s = sc.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

TCP通信处理:线程池优化

package com.itxue.d7_socket3.d6_socket2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ServerReaderThread extends Thread{

    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    public void run() {
        try {
            //            3. 从socket管道中得到一个字节输入流

            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String msg;
            while ((msg = br.readLine()) != null) {
                System.out.println(socket.getRemoteSocketAddress() + "说了" + msg);
            }
        } catch (IOException e) {
            System.out.println(socket.getRemoteSocketAddress()+"下线了!");
        }

    }
}
package com.itxue.d8_socket4.d6_socket2;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

public class ServerDemo2 {
//    使用静态变量定义一个线程池对象
    private static ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS
                ,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

    public static void main(String[] args) {
//        1.注册端口
        try {
            ServerSocket serverSocket = new ServerSocket(7777);
            while(true) {
//     2. 每接收到一个客户端的Socket管道,交给独立的线程负责读取线程
            Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress()+"他上线了");
//      任务对象读取消息
                Runnable target = new ServerReaderRunnable(socket);
                pool.execute(target);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package com.itxue.d8_socket4.d6_socket2;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo1 {
    public static void main(String[] args) {
        Socket socket = null;
        try {
//            创建Socket通信管道请求与服务器连接
//            参数一:服务端的IP地址  参数二:服务端的端口
            socket = new Socket("127.1.0.0",7777);
//          2.从socket通信管道中得到一个字节输出流,负责发送数据
            OutputStream os = socket.getOutputStream();
//          3.把低级字节流包装成打印流
            PrintStream ps = new PrintStream(os);
//          4.发送信息
            Scanner sc = new Scanner(System.in);
            while(true) {
                System.out.println("请输入消息:");
                String s = sc.nextLine();
                ps.println(s);
                ps.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

优势

服务端可以复用线程处理多个客户端,可以避免系统的瘫痪

适合客户端通信时长较短的场景

TCP通信的实战案例

即时通信

package com.itxue.d9_sms;

import com.itxue.d7_socket3.d6_socket2.ServerReaderThread;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ServerDemo2 {
//    定义一个静态的List集合存储当前全部在线的socket管道
    public static List<Socket> allOnlineSockets = new ArrayList<>();
    public static void main(String[] args) {
//        1.注册端口
        try {
            ServerSocket serverSocket = new ServerSocket(9999);
            while(true) {
//     2. 每接收到一个客户端的Socket管道,交给独立的线程负责读取线程
            Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress()+"他上线了");
                allOnlineSockets.add(socket);//上线完成
    //开始创建线程处理socket
            new ServerReadThread(socket).start();

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class ServerReadThread extends Thread{
    private Socket socket;
    public ServerReadThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));

            String line;
            while ((line = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress()+"收到消息"+line);
                //把这个消息进行端口转发给全部的客户端socket管道
                sendMsgToAll(line);
            }
        } catch (Exception e) {
            System.out.println(socket.getRemoteSocketAddress()+"下线了");
            ServerDemo2.allOnlineSockets.remove(socket);
        }
    }

    private void sendMsgToAll(String msg) throws Exception{

            for (Socket socket : ServerDemo2.allOnlineSockets) {
                PrintStream ps = new PrintStream(socket.getOutputStream());
                ps.println(msg);
                ps.flush();
            }
    }
}
package com.itxue.d9_sms;

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

public class ClientDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("客户端启动");
        Socket socket = new Socket("127.0.0.1",9999);
//        创建一个独立的线程专门负责这个客户端的信息(服务器随时可以转发信息过来!)
        new ClientReaderThread(socket).start();
//         从socket管道中得到一个字节输出流管道
        OutputStream os = socket.getOutputStream();
        PrintStream ps = new PrintStream(os);
        Scanner sc = new Scanner(System.in);
        while (true){
            System.out.println("请说:");
            String msg = sc.nextLine();
            ps.println(msg);
            ps.flush();
        }
    }

}
class ClientReaderThread extends Thread{
    private Socket socket;
    public ClientReaderThread (Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));

            String line;
            while ((line = br.readLine()) != null){
                System.out.println("收到消息"+line);

            }
        } catch (Exception e) {
            System.out.println("服务端把你踢出去了");

        }
    }
}

模拟BS系统【了解】

 

package com.itxue.d10_bs;



import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class bsServerDemo1 {
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(8080);
        while (true){
            Socket socket = ss.accept();
            new ServerReaderThread(socket).start();
        }
    }
}
class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
//            浏览器已经与socket管道建立了Socket管道
//            响应消息给浏览器提示
            PrintStream ps = new PrintStream(socket.getOutputStream());
//            必须响应HTTP协议格式数据,否则浏览器不认识信息
            ps.println("HTTP/1.1 200 OK");//协议类型和版本 响应成功的信息
            ps.println("Content-Type:text/html;charset=UTF-8");//响应数据类型; 文本/网页
            ps.println();//必须发送一个空行
//            才可以相应数据给浏览器
            ps.println("<span style='color:red;font-size:90px'>《最可爱的人》<span>");
            ps.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

 优化为线程池

package com.itxue.d11_bsexpand.d10_bs;



import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

public class bsServerDemo1 {
    private static ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket(8080);
        while (true){
            Socket socket = ss.accept();
            pool.execute(new ServerReaderRunnable(socket));
        }
    }
}
class ServerReaderRunnable implements Runnable{
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
//            浏览器已经与socket管道建立了Socket管道
//            响应消息给浏览器提示
            PrintStream ps = new PrintStream(socket.getOutputStream());
//            必须响应HTTP协议格式数据,否则浏览器不认识信息
            ps.println("HTTP/1.1 200 OK");//协议类型和版本 响应成功的信息
            ps.println("Content-Type:text/html;charset=UTF-8");//响应数据类型; 文本/网页
            ps.println();//必须发送一个空行
//            才可以相应数据给浏览器
            ps.println("<span style='color:red;font-size:90px'>《最可爱的人》<span>");
            ps.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值