Java中的网络编程、TCP、UDP学习笔记

Java中的网络编程,UDP,TCP,发送和接收数据


学习目标:

  • 了解网络的概念
  • 端口
  • IP划分
  • 子网掩码划分
  • 套接字(Socket)
  • UDP
  • TCP

1、什么是网络

网络是由节点和连线构成,表示诸多对象及其相互联系。在计算机世界里,⽹络就是⼀种辅助双⽅或者多⽅能够连接在⼀起的⼯具。

2、为什么使用网络

1、 使⽤⽹络能够把多⽅连接在⼀起,然后可以进⾏数据传递。
2、 所谓的⽹络编程就是,让在不同的电脑上的软件能够进⾏数据传递,即网络进程之间的通信


什么是端口

端口就是计算机和外界进行数据交换和通信的出入口,操作系统有有65535个端口,大致可以分为两类,一类是固定端口,另一类是动态端口,固定端口一般被系统服务使用


IP划分

A类IP段  0.0.0.0 到127.255.255.255
B类IP段  128.0.0.0 到191.255.255.255
C类IP段  192.0.0.0 到223.255.255.255


子网掩码划分

A类的默认子网掩码 255.0.0.0     一个子网最多可以容纳1677万多台电脑
B类的默认子网掩码 255.255.0.0    一个子网最多可以容纳6万台电脑
C类的默认子网掩码 255.255.255.0   一个子网最多可以容纳254台电脑


套接字(Socket)

应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要 通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)的接口,区分不同应用程序进程间的网络通信和连接。


UDP

UDP全称User Datagram Protocol,中文名是用户数据报协议。它的特点是无连接,不安全,不可靠,但是速度快。通俗的说就是,它不管你接收到没接收到,只管发送。
UDP发送数据测试(由于测试工具版本较老,所以代码中使用gbk格式,测试地址为127.0.0.1)


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

public class UDPSend {
    public static final String CHARSET = "gbk";
    public static final String IP = "127.0.0.1";
    public static final int DES_PORT = 8888;
    public static final int SELFPORT = 9999;

    public static void main(String[] args) {
        DatagramSocket ds = null;
        try {
//          ds = new DatagramSocket(new InetSocketAddress(IPAddress,port));
			//创建DatagramSocket对象
            ds = new DatagramSocket(SELFPORT);
            System.out.println("服务已启动");
            Scanner scanner = new Scanner(System.in);
            //死循环来进行多次数据发送
            while (true){
                System.out.println("请输入要发送的数据:");
                byte[] ms = scanner.nextLine().getBytes(CHARSET);
                System.out.println("等待发送数据");
                //获取UDP的报文对象
                DatagramPacket datagramPacket = new DatagramPacket(ms, 0, ms.length,new InetSocketAddress(IP, DES_PORT));
                //发送数据
                ds.send(datagramPacket);
                System.out.println("发送成功!!!");
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ds!=null){
                ds.close();
            }
        }
    }
}

运行结果如下:

在这里插入图片描述
在这里插入图片描述

这里只测试了UDP的数据是如何进行发送的,下面我们来看看如何使用UDP协议来进行数据的接收

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

public class UDPReceive {
    public static final String CHARSET = "gbk";
    public static final int SELFPORT = 9999;

    public static void main(String[] args) {
        DatagramSocket ds = null;

        try{

            //获取UDP协议对象
            ds = new DatagramSocket(SELFPORT);

            //创建一个字节流数组保存发送过来的数据
            byte[] bf = new byte[1024];

            //报文头对象
            DatagramPacket dp = new DatagramPacket(bf,0,bf.length);

            while (true){

                //接收数据
                ds.receive(dp);
                //输入信息 通过new String()来转换为字符串
                String s = new String(bf,0,bf.length,CHARSET);
                System.out.println("UDP发送了"+s);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ds!=null){
                ds.close();
            }
        }
    }
}

结果如下:

在这里插入图片描述
在这里插入图片描述


那么如何使用UDP来同时进行数据的发送和接收呢

我们不妨这样去思考,当通过UDP发送数据的时候,同时要进行数据的接收,等同于接收和发送算同时在运行,前一个专题我们学了多线程和多任务,那么我们是不是可以通过线程来进行数据的接收,设置一个子线程来用作接收数据。主线程用来发送数据。


测试代码UDPSendReceive.java:

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

public class UDPSendReceive {

    public static void main(String[] args) {
        new UDPSendReceive().begin();
    }

    private void begin() {
        DatagramSocket ds = null;
        DatagramPacket dp = null;
        try {
            ds = new DatagramSocket(8888);

            Scanner sc = new Scanner(System.in);

            //启动一个子线程用来接收数据
            ReceiveThread rt = new ReceiveThread(ds);

            Thread t1 = new Thread(rt);
            t1.start();

            while (true){
//                System.out.println("请输入要发送的内容:");
                String msg = sc.nextLine();

                byte[] buf = msg.getBytes();
                dp = new DatagramPacket(buf,0,buf.length,new InetSocketAddress("127.0.0.1",9999));
                if(msg.equals("")){
                    System.out.println("输入内容不能为空!!!");
                } else {
                    ds.send(dp);
                    System.out.println("发送成功!!!");
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }if(ds!=null){ds.close();}
    }

    class ReceiveThread implements Runnable{
        private DatagramSocket ds;
        private DatagramPacket dp;

        public ReceiveThread(DatagramSocket ds){
            this.ds = ds;
        }


        @Override
        public void run() {
            while (true){
                byte[] bf = new byte[1024];
                this.dp = new DatagramPacket(bf,0,bf.length);
                try {
                    ds.receive(dp);
                    String str = new String(bf,0,bf.length);
                    System.out.println("接收到信息:"+str);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

}

测试代码UDPSendReceive2.java:

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

public class UDPSendReceive2 {
    public static void main(String[] args) {
        new UDPSendReceive2().begin();
    }

    public void begin(){
        DatagramSocket ds = null;
        DatagramPacket dp = null;
        try {
            ds = new DatagramSocket(9999);

            Scanner sc = new Scanner(System.in);

            //启动一个子线程用来接收数据
            ReceiveThread rt = new ReceiveThread(ds);

            Thread t1 = new Thread(rt);
            t1.start();

            while (true){
//                System.out.println("请输入要发送的内容:");
                String msg = sc.nextLine();
                byte[] buf = msg.getBytes();
                dp = new DatagramPacket(buf,0,buf.length,new InetSocketAddress("127.0.0.1",8888));
                if(msg.equals("")){
                    System.out.println("输入内容不能为空!!!");
                } else {
                    ds.send(dp);
                    System.out.println("发送成功!!!");
                }
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }if(ds!=null){ds.close();}
    }


    class ReceiveThread implements Runnable{
        private DatagramSocket ds;
        private DatagramPacket dp;

        public ReceiveThread(DatagramSocket ds){
            this.ds = ds;
        }


        @Override
        public void run() {
            while (true){
                byte[] bf = new byte[1024];
                this.dp = new DatagramPacket(bf,0,bf.length);
                try {
                    ds.receive(dp);
                    String str = new String(bf,0,bf.length);
                    System.out.println("接收到信息:"+str);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

分别运行它们,用来进行收发测试,事实证明,当我们用线程来控制接收数据和发送数据是完全可行的,为了让他们可以一直进行发送和接收,,测试结果如下:

在这里插入图片描述
在这里插入图片描述


TCP

TCP:传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
刚才我们对UDP的收发进行了测试和运行,下面我门对TCP协议的收发做一个测试。
代码如下:


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

public class TestTCP {

    /*
        使用ServerSocket创建一个TCP服务器
     */
    public static void main(String[] args) {
        ServerSocket ss = null;
        try {
            //1、创建ServerSocket对象,注意,一般需要指定端口
            // IP如果不指定,则默认获取本机的IP
            ss = new ServerSocket(8888);
            System.out.println("Server is running  wait connection ! ");
            
            //服务器等待客户端链接上
            Socket ac = ss.accept();
            System.out.println("有客户端连接上了");

            //注意,Socket中如果使用了PrintWriter一定要刷新,
            //如果后面没有写上true那么不会显示出发送的信息
            PrintWriter pw = new PrintWriter(ac.getOutputStream(),true);
            pw.println("Welcome");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ss!=null){
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

在这里插入图片描述


那么如何通过TCP来进行数据的接收呢

  • 通过使用Socket对象进行连接,设置后IP和端口号
  • 然后通过IO流来接收发送的数据
  • 最终对接收的数据进行输出

测试代码

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

public class TestClient {
    /*
        使用Socket对象完成通信
     */
    public static void main(String[] args) {
        Socket s = null;

        try {
            s = new Socket("127.0.0.1",8888);
//            s.bind(new InetSocketAddress(9999));
            System.out.println("已经成功连接!!");

            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

            String str = br.readLine();
            System.out.println("服务器端发送数据"+str);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

运行结果:

在这里插入图片描述
在这里插入图片描述


使用TCP来处理多客户端消息的收发

控制台版的多人聊天,互相发送的消息,大家都可以看到,当用户上线下线时会进行提醒。
Server端代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

public class TestTCPServer {
    private List<ServerThread> users;


    public static void main(String[] args) {
        new TestTCPServer().begin();
    }


    public void begin(){
        ServerSocket ss = null;

        try {
            ss = new ServerSocket(8888);
            System.out.println("Server is running , wait connection......");
            //创建一个集合用来保存信息
            users = new ArrayList<ServerThread>();
            while (true){
                Socket accept = ss.accept();
                //将Socket传进ServerThread中,重写ServerThread的构造
                ServerThread serverThread = new ServerThread(accept);
                //启动线程
                new Thread(serverThread).start();
                //将ServerThread中的信息添加到集合中
                users.add(serverThread);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(ss!=null){
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    class ServerThread implements Runnable{

        private Socket socket;
        private BufferedReader br;
        private PrintWriter pw;
        private String name;

        public ServerThread(Socket socket) throws IOException {
            this.socket = socket;
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            pw = new PrintWriter(socket.getOutputStream(),true);
            name = socket.getInetAddress().getHostAddress()+":"+socket.getPort();
        }

        //将消息遍历使用ServerThread中的PrintWriter推送给所有用户
        public void send(String msg){
            for(ServerThread st:users){
                st.pw.println(name);
                st.pw.println(msg);
            }
        }


        //run()方法
        @Override
        public void run() {
            send(name+" 上线了 ! ");
            this.pw.println("欢迎"+name+" !");
            try {
                //接收信息
                receive();
            }catch (SocketException e){
                System.out.println("客户端"+name+"异常下线");
                //当有用户下线时,从集合中移除它
                users.remove(this);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(this.socket!=null){
                    try {
                        this.socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        private void receive() throws IOException {
            String str = null;
            System.out.println("开始接收数据");
            while ((str= br.readLine())!=null){
                //当输入exit时进行下线处理。
                if(str.trim().equalsIgnoreCase("exit")){
                    //释放资源(在真正的环境下需要对资源进行释放)

                    System.out.println("客户端"+name+"正常下线");
                    pw.println("exit");
                    users.remove(this);
                    break;
                }else {
                    System.out.println("接收到来自于"+name+"的数据: "+str);
                    send(str);
                }
                pw.println(str);
            }
        }
    }
}

Client端代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

public class TestTCPClient {

    public static void main(String[] args) {
        new TestTCPClient().begin();
    }


    public void begin(){
        Socket s = null;
        BufferedReader brr = null;

        try {
            s = new Socket("127.0.0.1",8888);
            ClientThread ct = new ClientThread(s);
            Thread task = new Thread(ct);
            task.start();


            while (true){
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String str = br.readLine();
                if(str.trim().equalsIgnoreCase("exit")){
                    System.out.println("按回车键退出");
                    //不推荐使用stop方法,建议自行进行书写判断结束进程的条件
                    task.stop();
                    break;
                }
                System.out.println(s.getInetAddress().getHostAddress()+":"+s.getPort()+"发送了:"+str);
                System.out.println("输入要发送的数据:");
            }

        }catch (ConnectException e){
            System.out.println("服务器连接失败,请联系网管");
        }
        catch (SocketException e){
            System.out.println("连接中断");
        }
        catch (UnknownHostException e){
            System.out.println("网络环境不稳定,请检查网络环境");
        }
        catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(s!=null){
                try {
                    s.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }




    class ClientThread implements Runnable{
        private Socket socket;
        private PrintWriter pw;
        private BufferedReader br;

        public ClientThread(Socket socket) throws IOException {
            this.socket = socket;
            this.pw = new PrintWriter(socket.getOutputStream(),true);
            this.br = new BufferedReader(new InputStreamReader(System.in));
        }

        @Override
        public void run() {
            try {
                sendMsg();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void sendMsg() throws IOException {
            while (true){
                System.out.print(">>>");
                String str = br.readLine();
                //发送到服务器
                this.pw.println(str);
            }
        }
    }
}

运行结果如图:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

地心美少女

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值