用代码介绍java的网络编程(DatagramSocket+Socket)加基于控制台的聊天室小项目

java网络编程的介绍

主要讲解网络编程的实现步骤。基于控制台的聊天室小项目是为了加深对网络编程的理解。学网络编程要对java线程和IO的基本操作有一定的了解。不了解的可以先看下面的两篇文章
用代码介绍多线程(java基础篇).
用代码介绍java的-io常见类.


现在直接上代码

InetAddress

代码说明其作用

public class Main {

    public static void main(String[] args)  {
        try {
            //获取本机对象
            InetAddress localHost = InetAddress.getLocalHost();
            //获取本机的ip地址
            String address = localHost.getHostAddress();
            //获取本机名
            String hostName = localHost.getHostName();
            //根据域名获取信息
            InetAddress baiduHost = InetAddress.getByName("www.baidu.com");
            //百度的ip地址
            String baiduAddress = baiduHost.getHostAddress();
            //百度的域名
            String baiduName = baiduHost.getHostName();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

InetSocketAddress

InetSocketAddress包含了InetAddress。InetAddress不能指定端口号。ip地址只是指定主机,并没有详细到某个应用(app)上。端口是同一台主机是不同应用的区分。端口范围0-65535。

import java.net.InetAddress;
import java.net.InetSocketAddress;

public class Main {

    public static void main(String[] args)  {
        //获取InetSocketAddress对象。域名加端口号。指定的地址详细了
        InetSocketAddress localhost = new InetSocketAddress("www.baidu.com",80);
        //获取到了InetAddress对象。可对照上一标题InetAddress的介绍
        InetAddress inetAddress = localhost.getAddress();
        //inetAddress不能获取端口
        int port = localhost.getPort();
        System.out.println(port);
        //结果
        //80
        //这里是构造方法指定的。
    }
}

URL的介绍

每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL。看代码了解一下URL这个类

import java.net.MalformedURLException;
import java.net.URL;

public class Main {
    public static void main(String[] args)  {
        try {
            URL url = new URL("http://www.baidu.com:80/index.html?uname=shsxt&age=18#a");
            //获取协议
            String protocol = url.getProtocol();
            System.out.println("协议:"+protocol);
            //域名
            String host = url.getHost();
            System.out.println("域名:"+host);
            //获取资源路径包括请求参数
            System.out.println("请求资源1:"+url.getFile());
            //仅仅获取路径不包括请求参数
            System.out.println("请求资源2:"+url.getPath());
            //参数
            System.out.println("参数:"+url.getQuery());
            //可以看出url.getpath()+url.getQuery()=url.getFile
            //锚点
            System.out.println("锚点:"+url.getRef());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通过URL下载对应的资源(爬虫的必备技能)

普通的下载,当网上设了反爬虫会失效

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;

public class Main {
    public static void main(String[] args)  {
        try {
            //网上资源地址.这里是图片
            URL url = new URL("http://img.coocaa.com/www/attachment/forum/201602/16/085938u86ewu4l8z6flr6w.jpg");
            //下载
            InputStream inputStream = url.openStream();
            //输出到本地。这是IO的知识点。可以看开头推荐的文章
            FileOutputStream out = new FileOutputStream("src/url.jpg");
            byte[] bytes = new byte[1024];
            int len=-1;
            while ((len=inputStream.read(bytes))!=-1){
                out.write(bytes,0,len);
                out.flush();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

通过URL下载对应的资源(简单破解一下反爬虫)

这里只是简单的提一下。加深对URL的理解。重点不是讲解爬虫。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class Main {
    public static void main(String[] args)  {
        try {
            //https://www.dianping.com有反爬虫。通过上面的方法会报错
            URL url = new URL("https://www.dianping.com");
            //获取连接
            HttpURLConnection con = (HttpURLConnection) url.openConnection();
            //请求方式。get或post。
            con.setRequestMethod("GET");
            //这里是去浏览器访问。按F12选netWork,点击www.dianping.com找到User-Agent(如下图)
            con.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36");
            //获取输入流
            InputStream inputStream = con.getInputStream();
            BufferedInputStream in = new BufferedInputStream(inputStream);
            //输出。这里下载的是一个网页
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/dianping.html"));
            byte[] bytes = new byte[1024];
            int len;
            while ((len=in.read(bytes))!=-1){
                out.write(bytes,0,len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

获取setRequestProperty参数值
在这里插入图片描述

UDP的介绍

Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。UDP协议要求包小于64K。属于不可靠传输。优点效率高

用UDP实现一对一聊天。(半成品,基础较好的可跳)

发起端

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

public class UdpClient {
    public static void main(String[] args) {
        System.out.println("发起端");
        try {
            while(true){
                //============发送端==============================
                // 使用DatagramSocket  指定端口 创建当前应用
                DatagramSocket client = new DatagramSocket(8081);
                //输入要发送的内容
                Scanner scanner = new Scanner(System.in);
                String next = scanner.next();
                //转成字节数组
                //只能传字节数组。如果想传一个对象过去。DataOutputStream+ByteArrayOutputStream去实现。可观看本文开头推荐的用代码介绍java的-io常见类
                byte[] bytes = next.getBytes();
                //  封装成DatagramPacket 包裹,需要指定目的地.使用InetSocketAddress指定地址(发送到哪里去)
                //注意文件不能超过64K
                //8080是接收方的端口
                //"localhost"是接端的域名或ip地址
                DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,new InetSocketAddress("192.168.142.1",8080));
                //开始发送
                client.send(packet);
                //====================接收端=====================
                //接收消息
                byte[] bytes1 = new byte[1024];
                //以包的形式接收
                DatagramPacket packet1 = new DatagramPacket(bytes1, 0, bytes1.length);
                //接收(当有人发消息来是执行)
                client.receive(packet1);
                //通过包获取字节数组
                byte[] data = packet1.getData();
                //输出收到的信息
                System.out.println(new String(data));
                //释放资源
                client.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

接收端

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

public class UdpServer {
    public static void main(String[] args) {
        System.out.println("接收端");
        try {
            while(true){
                // 使用DatagramSocket  指定端口 创建当前应用
                DatagramSocket datagramSocket = new DatagramSocket(8080);
                byte[] bytes = new byte[1024];
                DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
                //接收(等待有人发送消息是执行)
                datagramSocket.receive(packet);
                byte[] data = packet.getData();
                System.out.println(new String(data,0,data.length));
                //输入要回复的内容(这里需要接收到消息才能回消息)
                Scanner scanner = new Scanner(System.in);
                String next = scanner.next();
                byte[] bytes1 = next.getBytes();
                //发送到localhost的8081的应用上(这里发收到前面的->发起端)
                DatagramPacket packet1 = new DatagramPacket(bytes1, 0, bytes1.length, new InetSocketAddress("localhost", 8081));

                datagramSocket.send(packet1);
                datagramSocket.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}


运行发起端再运行接收端。在发起端输入内容。接收端就会收到。然后接收端输入内容回复给发起端。这样一来一回。有说话才有回话。就发送过后要接收到回复才可以发送了。一次只能发送一句话,如果对方不回就不能发送了显然不符合逻辑

用UDP实现一对一聊天。(最终版)

加入多线程使得聊天一次可以接收多条信息。也可以发送多条信息。而且不规定谁是发起者。

线程发送类

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 UdpSend implements Runnable {
    private  DatagramSocket client;
    private String toIP;
    private int toPort;
    private boolean isRunning;
    private String name;
    UdpSend(int port,String toIP,int toPort,String name){
        //初始化
        this.name = name;
        isRunning=true;
        this.toIP = toIP;
        this.toPort=toPort;
        try {
            client = new DatagramSocket(port);
        } catch (SocketException e) {
            e.printStackTrace();
        }

    }
    @Override
    public void run() {
          while (isRunning){

              byte[] bytes = new byte[1024];
              Scanner scanner = new Scanner(System.in);
              String next = scanner.next();

              if ("q".equals(next)){
                  //如果输入了q就发送下线了
                  bytes = "我下线了,拜".getBytes();
              }else {
                  //正常内容
                  next = this.name+":"+next;
                  bytes = next.getBytes();
              }
              //发送包。bytes是要发送是的内容,用InetSocketAddress指定端口地址
              DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,new InetSocketAddress(toIP,toPort));
              try {
                  //开始发送
                  client.send(packet);
              } catch (IOException e) {
                  //退出循环
                  isRunning=false;
                  e.printStackTrace();
                  if(client!=null){
                      //释放资源
                      client.close();
                  }
              }
              //检测是否要退出。输入q退出
              if ("q".equals(next)){
                  break;
              }
          }
          //释放资源
          if (client!=null){
              client.close();
          }
    }
}

线程接收类

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

public class UdpReceive implements Runnable {
     private  DatagramSocket server;
     private boolean isRunning;
    UdpReceive(int port){
        isRunning=true;
        try {
            server = new DatagramSocket(port);
        } catch (SocketException e) {
            if (server!=null)
                server.close();
            isRunning=false;
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
      while (isRunning){
          byte[] bytes = new byte[1024];
          DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
          try {
              //等待接收
              server.receive(packet);
              byte[] data = packet.getData();
              System.out.println(new String(data,0,data.length));
          } catch (Exception e) {
              isRunning=false;
              if (server!=null)
              server.close();
              e.printStackTrace();
          }
      }
        if (server!=null)
            server.close();
    }
}

教师端

import java.util.Scanner;

public class UdpTeacherThread {
    public static void main(String[] args) {
        System.out.println("输入q退出");
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入姓名");
        String name = scanner.next();
        //8087是发送的端口。localhost是发送的目的地址。8081是发送的目的端口。name是你的名字
        UdpSend send = new UdpSend(8087, "localhost", 8081, name);
        //8080是接收的目的端口。地址是本机
        UdpReceive receive = new UdpReceive(8080);
        //开启发送线程
        new Thread(send).start();
        //开启接收线程
        Thread thread = new Thread(receive);
        thread.setDaemon(true);
        thread.start();
        
        //发送和接收不是同一个线程就不会阻塞了。
    }
}

学生端

教师端和学生端基本一致就端口指定对就行了

import java.util.Scanner;

public class UdpStudentThread {
    public static void main(String[] args) {
        System.out.println("输入q退出");
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入姓名");
        String name = scanner.next();
        new Thread(new UdpSend(8088,"localhost",8080,name)).start();
        Thread thread = new Thread(new UdpReceive(8081));
        thread.setDaemon(true);
        thread.start();
    }
}

好了到这里就结束了UDP的介绍。

TCP的介绍

TCP 是面向连接的传输控制协议,而UDP是无连接的数据报服务;TCP 具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;TCP对包的大小无限制又称传输流。
TCP是通过服务器的形式转发的。就请求和响应式。客户端只和服务器对接。客户和客户无法对接,必须通过服务器这个中转站

TCP服务端编写(易理解)模拟图片上传


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class FileServer {
    public static void main(String[] args) {
        System.out.println("server");
        try {
            //指定端口。地址是本机
            ServerSocket server = new ServerSocket(8080);
            //阻塞式连接(有连接才会往下执行)
            Socket client = server.accept();
            System.out.println("连接成功");
            //获取客户端发送的内容
            BufferedInputStream in = new BufferedInputStream(client.getInputStream());
            //将内容保存到src/IO3.png
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/IO3.png"));
            byte[] bytes = new byte[1024];
            int len=-1;
            while ((len=in.read(bytes))!=-1){
                out.write(bytes,0,len);
                out.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

TCP客户端编写(易理解)模拟图片上传

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.net.Socket;

public class FileClient {

    public static void main(String[] args) {
        System.out.println("-----Client-----");
        //1、建立连接: 使用Socket创建客户端 +服务的地址和端口
        //8080是服务去的端口,localhost是服务去的地址
        try ( Socket client =new Socket("localhost",8080);
              //client.getOutputStream()把内容输出到Socket里
              BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());
              //客户要上传的图片
              BufferedInputStream in = new BufferedInputStream(new FileInputStream("src/IO.png"));){
            int len;
            byte[] bytes = new byte[1024];
            while ((len=in.read(bytes))!=-1){
                //开始写入
                out.write(bytes,0,len);
                out.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

先启动服务端再启动客户端

TCP模式的基于控制台的群聊

Socket +ServerSocket +多线程+IO实现

ServerSocket (服务端)

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

//实现群聊
class CharServerRun03 implements Runnable{
    //
    private Socket client;
    private DataInputStream dis;
    private DataOutputStream dos;
    private boolean isRunning;
    private String name;
    //用来记录在线人
    private CopyOnWriteArrayList<CharServerRun03> allClient;
    CharServerRun03(Socket client,CopyOnWriteArrayList<CharServerRun03> allClient){
        //赋值
        this.allClient = allClient;
        isRunning=true;
        this.client=client;
        try {
            //接收客户端的信息
            dis = new DataInputStream(client.getInputStream());
            //给客户端发送信息
            dos = new DataOutputStream(client.getOutputStream());
            //获取客户端发来的名字
            this.name=receive();
            //发送给上线的用户
            startSend("欢迎来到xxxx系统");
            //告诉其他人name上线了
            sendOthers("系统消息:"+name+"上线了");
        } catch (Exception e) {
            try {
                release();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }
    //释放资源
    private void release() throws Exception {
       sendOthers("系统消息:"+name+"下线了");
       //下线了
       allClient.remove(this);
        this.isRunning=false;
        if(client!=null){
            client.close();
        }
        if(dis!=null){
            dis.close();
        }
        if (dos!=null){
            dos.close();
        }
    }
    //收集数据
    private String receive(){
        String datas=null;
        try {
            datas = dis.readUTF();
        } catch (IOException e) {
            e.printStackTrace();
            try {
                release();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return datas;
    }
    public void startSend(String msg){
        try {
            dos.writeUTF(msg);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sendOthers(String msg){

        System.out.println("发送"+msg);
        //给所以人发信息(除了自己)
        for (CharServerRun03 client : allClient) {
            if(client!=this){
                client.startSend(msg);
                System.out.println("发送成功");
            }
        }
    }
    @Override
    public void run() {
        while(isRunning){
            //获取客户发来的信息
            String msg = receive();
            if(msg!=null){
                //开始发送
                sendOthers(name+":"+msg);
            }

        }
    }
}

public class CharServer03 {
    //记录在线人的对象
    //CopyOnWriteArrayList<CharServerRun03>和List<?>用法一一样
    //CopyOnWriteArrayList<CharServerRun03>线程安全。List<?>多线程不安全
    private static CopyOnWriteArrayList<CharServerRun03> allClient = new CopyOnWriteArrayList<>();
    public static void main(String[] args) {
        try (ServerSocket server = new ServerSocket(8080);  ){
            int sum=0;
            //实现多个用户连接
            while(true){
                //阻塞式
                Socket client = server.accept();
                System.out.println(++sum+":个用户连接");
                CharServerRun03 c = new CharServerRun03(client, allClient);
                //添加到在线集合中
                allClient.add(c);
                //启动线程
                new Thread(c).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Socket (客户端)

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

class ClientSendMsg implements Runnable{
     private BufferedReader scanner;
     //写出要发送的内容。
     private  DataOutputStream dataOutputStream;
     private Socket client;
     //客户的姓名
     private String name;
     private boolean isRunning;
     ClientSendMsg(Socket client,String name){

         this.name = name;
         isRunning = true;
         this.client=client;
         try {
             scanner = new BufferedReader(new InputStreamReader(System.in));
             //client.getOutputStream();把内容输出到Socket待发送
             dataOutputStream = new DataOutputStream(client.getOutputStream());
                 //告诉服务器客户的名字
                 dataOutputStream.writeUTF(name);
                 dataOutputStream.flush();
         } catch (IOException e) {
             e.printStackTrace();
             //出异常释放资源
             release();
         }
     }
     //释放资源
    private void  release(){
         //结束循环
         isRunning=false;
         CharUtil.close(client,scanner,dataOutputStream);
    }
    @Override
    public void run() {
         //多次发送
        while (isRunning){
            String msg = null;
            try {
                //System.out.println("输入内容:");
                msg = scanner.readLine();
                dataOutputStream.writeUTF(msg);
                dataOutputStream.flush();
            } catch (Exception e) {
                e.printStackTrace();
                release();
            }
        }
    }
}
//接收信息
class ClientGetMsg implements Runnable{
    private Socket client;
    private DataInputStream dataInputStream;
    private boolean isRunning;
    ClientGetMsg(Socket client){
        isRunning=true;
        this.client=client;
        try {
            //client.getInputStream();服务器发来的
            dataInputStream = new DataInputStream(client.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
            release();
        }
    }
    private void  release(){

        isRunning=false;
        CharUtil.close(client,dataInputStream);
    }
    @Override
    public void run() {
        while (isRunning){
           // System.out.print("输出");
            try {
                //获取
                String msg = dataInputStream.readUTF();
                System.out.println(msg);
            } catch (IOException e) {
                e.printStackTrace();
                release();
            }
        }
    }
}
public class CharClient03 {
    public static void main(String[] args) {
        try {
            //"localhost", 8080是服务器的地址和端口
            Socket client = new Socket("localhost", 8080);
            System.out.print("输入用户名:");
            Scanner scanner = new Scanner(System.in);
            String name = scanner.next();
            //开启发信息的线程
            new Thread(new ClientSendMsg(client,name)).start();
            //开启收信息的线程
            new Thread(new ClientGetMsg(client)).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

启动服务器后。启动多个客户端就可以实现群聊。实在看不懂可以加我微信讨论讨论。微信号:hzclovehxr。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值