Java网络编程

Java网络编程

一、网络编程的三个核心要素

1、IP地址:InetAddress

2、port端口号

3、通信协议 —— 七层模型

二、Java实现网络编程 —— 案例分析

Java底层封装了网络层和物理链路层的协议,如果想通过Java实现网络编程,我们只需要考虑应应用层和传输层的操作方式即可。

  • TCP传输数据的方式

    • Socket:客户端
      绑定IP地址和端口号,采用TCP协议连接ServerSocket服务端

    • ServerSocket:服务端
      绑定端口号,然后再当前主机上开启一个应用程序

  • UDP传输数据的方法

version 1

package com.kang.v1;

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

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        /**
         * ServerSocket中的accept方法是用来接收客户端的请求,这个方法在执行的时候会进入线程阻塞的状态
         * 直到客户端连接上服务端,这个代码才会向下继续执行
         * 这个方法每调用一次,就可以接收一个客户端的请求
         *
         * 这个方法执行成功返回一个Socket,这个Socket是服务端创建的一个专门用来和客户端socket之间进行数据传输的网络套接字
         */
        Socket socket = ss.accept();
        System.out.println("服务端接收到了一个客户端的连接。客户端的IP地址为:" + Arrays.toString(socket.getInetAddress().getAddress()));
        InputStream inputStream = socket.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        String line = null;
        while((line = br.readLine()) != null){
            System.out.println("客户端给服务端发送了:" + line);
        }
    }
}
package com.kang.v1;

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

public class Client {
    public static void main(String[] args) throws IOException {
        /**
         * 这一行代码就代表客户端连接服务端 建立TCP网络传输连接
         */
        Socket socket = new Socket(InetAddress.getByName("localhost"),8888);
        OutputStream outputStream = socket.getOutputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        String line = null;
        while ((line = br.readLine()) !=  null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
    }
}

version2

package com.kang.v2;

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

public class Client {
    public static void main(String[] args) throws IOException {
        //客户端连接上服务端
        Socket socket = new Socket(InetAddress.getByName("localhost"),8888);
        /**
         * 首先先开启一个用来去接收服务端回应消息的线程
         */
        Thread receiveMessageThread = new Thread(){
            @Override
            public void run() {
                /**
                 * 客户端接收服务端返回的数据
                 */
                InputStream inputStream = null;
                try {
                    inputStream = socket.getInputStream();
                    BufferedReader br1 = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                    String line1 = null;
                    while((line1 = br1.readLine()) != null){
                        System.out.println("服务端回应了:" + line1);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        receiveMessageThread.start();

        /**
         * 客户端接收键盘输入,将键盘输入的数据发送给服务端
         */
        OutputStream outputStream = socket.getOutputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
        String line = null;
        while((line = br.readLine()) != null){
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

    }
}
package com.kang.v2;

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

/**
 * 版本:
 *      客户端可以给服务端发送消息
 *      服务端收到客户端消息后,给客户端回应一个服务端收到xxxx消息
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8888);
        Socket socket = ss.accept();
        System.out.println("服务端接收到一个客户端的连接,客户端IP地址为:" + Arrays.toString(socket.getInetAddress().getAddress()));

        //socket可以获取输出流 输出流是服务端给客户端发送的消息
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
        //socket可以获取输入流 输入流是客户端给服务端发送的消息
        InputStream inputStream = socket.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));

        String line = null;
        while((line = br.readLine()) != null){
            System.out.println("客户端给服务端发送了:" + line);
            bw.write("服务端收到:" + line);
            bw.newLine();
            bw.flush();
        }
    }
}

version3

package com.kang.v3;

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

public class Client {
    public static void main(String[] args) throws IOException {
        //客户端连接服务端
        Socket socket = new Socket(InetAddress.getByName("localhost"),9999);

        Thread reveiceMessageThread = new Thread(){
            @Override
            public void run() {
                //客户端接收消息
                InputStream inputStream = null;
                try {
                    inputStream = socket.getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                    String line1 = null;
                    while((line1 = br.readLine()) != null){
                        System.out.println("服务端回应了:" + line1);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        reveiceMessageThread.start();
        //客户端发送消息
        BufferedReader br1 = new BufferedReader(new InputStreamReader(System.in));
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
        String line = null;
        while((line = br1.readLine())!= null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
    }
}
package com.kang.v3;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Arrays;

/**
 * v3版本:
 *      服务端可以接收多个客户端的连接,同时可以和多个客户端之间进行通信
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("============================服务端v3版本开始运行============================");
        /**
         * 核心是想让服务端接收多个客户端的请求
         */
        while(true){
            //接收到一个客户端的请求
            Socket socket = ss.accept();
            System.out.println("服务端接收到一个客户端的连接,客户端的IP地址为:" + Arrays.toString(socket.getInetAddress().getAddress()));
            Thread handlerClientThread = new Thread(){
                @Override
                public void run() {
                    //服务端接收消息
                    try {
                        InputStream inputStream = socket.getInputStream();
                        BufferedReader br = null;
                        br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                        //服务端发送消息
                        OutputStream outputStream = socket.getOutputStream();
                        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));

                        String line = null;
                        while ((line = br.readLine()) != null){
                            System.out.println("客户端" +Arrays.toString(socket.getInetAddress().getAddress()) + "给服务端发送的消息:" + line);
                            bw.write("服务端收到:" + line);
                            bw.newLine();
                            bw.flush();
                        }
                    } catch (SocketException e) {
                        System.out.println("有一个客户端下线了,客户端的IP地址为:" + Arrays.toString(socket.getInetAddress().getAddress()));
                    }catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            handlerClientThread.start();
        }
    }
}

version4

package com.kang.v4;

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

public class Client {
    public static void main(String[] args) throws IOException {
        //客户端连接服务端
        Socket socket = new Socket(InetAddress.getByName("localhost"),9999);

        Thread reveiceMessageThread = new Thread(){
            @Override
            public void run() {
                //客户端接收消息
                InputStream inputStream = null;
                try {
                    inputStream = socket.getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                    String line1 = null;
                    while((line1 = br.readLine()) != null){
                        System.out.println(line1);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        reveiceMessageThread.start();
        //客户端发送消息
        BufferedReader br1 = new BufferedReader(new InputStreamReader(System.in));
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
        String line = null;
        while((line = br1.readLine())!= null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
    }
}
package com.kang.v4;

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

/**
 * v4版本:
 *    实现思路:将其中一个socket接收到的消息发送给所有服务端在线的socket
 *    核心逻辑:服务端没接收到一个客户端的连接,需要把客户端对应的socket缓存起来
 *                  缓存池只能保存在线的socket,如果下线了 那么需要从缓存中移除了
 */
public class Server {
    //定义一个静态属性
    public static List<Socket> onlineSockets = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("============================服务端v3版本开始运行============================");
        /**
         * 核心是想让服务端接收多个客户端的请求
         */
        while(true){
            //接收到一个客户端的请求
            Socket socket = ss.accept();
            System.out.println("服务端接收到一个客户端的连接,客户端的IP地址为:" + Arrays.toString(socket.getInetAddress().getAddress()));
            //需要把客户端缓存起来
            onlineSockets.add(socket);
            Thread handlerClientThread = new Thread(){
                @Override
                public void run() {
                    //服务端接收消息
                    try {
                        InputStream inputStream = socket.getInputStream();
                        BufferedReader br = null;
                        br = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
                        //服务端发送消息
//                        OutputStream outputStream = socket.getOutputStream();
//                        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));

                        String line = null;
                        while ((line = br.readLine()) != null){
                            System.out.println("客户端" +Arrays.toString(socket.getInetAddress().getAddress()) + "给服务端发送的消息:" + line);
//                            bw.write("服务端收到:" + line);
//                            bw.newLine();
//                            bw.flush();
                            /**
                             * 同时我们还需要把数据给所有用户广播一份
                             */
                            for (Socket onlineSocket : onlineSockets) {
                                OutputStream outputStream = onlineSocket.getOutputStream();
                                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
                                bw.write("有一个客户端" + Arrays.toString(socket.getInetAddress().getAddress()) + "发送了一个消息:" + new Date());
                                bw.newLine();
                                bw.write("     " + line);
                                bw.newLine();
                                bw.flush();
                            }
                        }
                    } catch (SocketException e) {
                        System.out.println("有一个客户端下线了,客户端的IP地址为:" + Arrays.toString(socket.getInetAddress().getAddress()));
                        //下线移除缓存,保证缓存均是在线用户
                        onlineSockets.remove(socket);
                    }catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            handlerClientThread.start();
        }
    }
}

version5

package com.kang.v5.server;

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

/**
 * v5版本:实现私聊功能
 *      实现思路:
 *          1、要能找到对应用户的socket---服务端找
 *          2、客户端给指定客户端发送消息的时候,你得告诉服务端给谁发送消息-----自定义协议
 *      实现流程:
 *          1、客户端在启动的时候,必须有唯一的账号帮助我们启动,账号先给服务端发送消息,账号要登录,服务端会把账号和账号对应的socket缓存起来--map集合
 *          2、客户端在发送消息的时候,需要加上自定义的协议,协议告诉我们服务端需要做什么样的功能
 *              0,username.password     代表的是注册逻辑
 *              1,username,password     代表的是登录请求
 *              2,123456,1234567,message  代表的就是给123456给1234567账号发送一个message消息
 *              3,123456,message        代表的是123456账号发送了群聊的消息message
 */
public class Server {
    //静态属性集合当作我们的用户信息的存储容器
    public static List<User> users = new ArrayList<>();
    public static Map<String,Socket> onlineUsers = new HashMap<>();
    public static void main(String[] args) throws IOException, InterruptedException {
        ServerSocket ss = new ServerSocket(9999);
        while(true){
            //一旦服务端的接收到客户端的请求,不是立马缓存客户端的socket,而是等到用户登录成功才缓存的。
            Socket socket = ss.accept();
            //自定义一个线程用来让服务端接收客户端的请求,处理客户端的消息
            Thread receiveClientMessageThread = new Thread(){
                @Override
                public void run() {
                    try {
                        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        String line = null;
                        while ((line = br.readLine()) != null){
                            //line就是客户端给服务端发送的消息,消息自定义的协议类型
                            handlerClientMessage(line,socket);
                        }
                    } catch (SocketException e) {
                        //先遍历map集合 找到socket对应的key
                    }catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            };
            receiveClientMessageThread.start();
        }
    }

    /**
     * 根据客户端发送的协议数据进行数据处理
     * @param line
     * @param socket
     */
    private static void handlerClientMessage(String line, Socket socket) throws IOException {
        //这个数组里面放的就是我们所需要的协议数据,其中数组的第一个位置代表的是协议的编号
        String[] data = line.split(",");
        String protocol = data[0];
        switch (protocol){
            case "0":
                //注册协议
                String nickname = data[1];
                String password = data[2];
                String number = ServerUtil.generatorNumber();
                User user = new User(nickname,password,number);
                Server.users.add(user);
                //服务端应该给客户端回应一个信息:0,注册成功,number
                String message = "0,注册成功," + number;
                ServerSendMessageUtil.sendMessage(socket,message);
                break;
            case "1":
                //登录协议
                String loginNumber = data[1];
                String loginPassword = data[2];
                String message1 = handlerLogin(loginNumber,loginPassword);
                ServerSendMessageUtil.sendMessage(socket,message1);
                //根据账号和socket把当前用户缓存起来
                Server.onlineUsers.put(loginNumber,socket);
                break;
            default:
                break;
        }
    }

    private static String handlerLogin(String loginNumber, String loginPassword) {
        String message = "1,";
        boolean flag = false;
        for (User user : Server.users) {
            if(user.getNumber().equals(loginNumber)){
                if (user.getPassword().equals(loginPassword)){
                    message += "ok";
                    flag = true;
                    break;
                }else {
                    message += "fail,password is valid";
                }
            }
        }
        if (!flag){
            message += "fail,user is not exists";
        }
        return message;
    }
}
package com.kang.v5.client;

import sun.nio.cs.ext.ISO2022_CN_CNS;

import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class Client {
    public static void main(String[] args) throws IOException {
        /**
         * 客户端可以正常连接服务端,只不过现在客户端给服务端发送的消息不再是随便的一条数据了,而是我们自定义的协议类型的数据
         * 自定义的协议类型的数据可以告诉服务端需要做什么样的逻辑
         */
        Socket client = new Socket(InetAddress.getByName("localhost"),9999);
        /**
         * 先开启一个线程,专门让客户端接收服务端返回的数据的线程
         */
        Thread receiveServerMessageThread = new Thread(){
            @Override
            public void run() {
                BufferedReader br = null;
                try {
                    br = new BufferedReader(new InputStreamReader(client.getInputStream()));
                    String line = null;
                    while ((line = br.readLine()) != null){
                        //line就是服务端给客户端返回的信息    返回的信息也是自定义的协议
                        handlerServerMessage(line,client);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        receiveServerMessageThread.start();
        a:while(true) {
            System.out.println("=========================================================");
            System.out.println("||            1、注册  2、登录  3、关闭程序                ||");
            System.out.println("=========================================================");
            Scanner scanner = new Scanner(System.in);
            int menu = scanner.nextInt();
            switch (menu) {
                case 1:
                    register(client,scanner);
                    break;
                case 2:
                    login(client,scanner);
                    break;
                case 3:
                    System.out.println("程序关闭成功,还有您的继续使用!");
                    client.close();
                    break a;
                default:
                    System.out.println("没有该菜单选项,请重新选择!");
                    break;
            }
        }
    }

    /**
     * 客户端用来处理服务端发送的消息
     * @param line
     * @param client
     */
    private static void handlerServerMessage(String line, Socket client) {
        String[] data = line.split(",");
        String protocol = data[0];
        switch (protocol){
            case "0":
                //注册信息返回的逻辑
                String number = data[2];
                JOptionPane.showMessageDialog(null,"注册成功,您的账号为:" + number);
                break;
            case "1":
                /**
                 * 登录功能服务端返回的信息
                 * 1,ok
                 * 1,fail,failMessage
                 */
                String flag = data[1];
                if (flag.equals("ok")){
                    enterMainPage();
                }else {
                    JOptionPane.showMessageDialog(null,data[2]);
                }

                break;
            default:
                break;
        }

    }

    private static void enterMainPage() {
        while (true){
            System.out.println("=========================================================");
            System.out.println("|| 1、群聊  2、私聊  3、我的好友列表  4、添加好友  5、退出系统 ||");
            System.out.println("=========================================================");
            Scanner scanner  = new Scanner(System.in);
            int i = scanner.nextInt();
        }
    }

    /**
     * 客户端的登录功能
     */
    private static void login(Socket client,Scanner scanner) throws IOException {
        System.out.println("请输入您的账号:");
        String number = scanner.next();
        System.out.println("请输入您的密码");
        String password = scanner.next();
        /**
         * 封装一个自定义的协议数据给服务端发送校验
         * 1,number,password
         */
        String message = "1," + number + "," + password;
        ClientSendMessageUtils.sendMessage(client,message);

    }
    /**
     * 客户端的注册功能
     */
    private static void register(Socket socket,Scanner scanner) throws IOException {
        System.out.println("请输入您的昵称:");
        String nickname = scanner.next();
        //数据校验
        if (nickname == null || nickname.equals("")){
            System.out.println("七七微聊提示您:昵称不能为空!");
            return;
        }
        System.out.println("请输入您的密码:");
        String password = scanner.next();
        System.out.println("请再次输入您的密码:");
        String repass = scanner.next();
        //数据校验
        if (password == null || repass ==null || password.equals("") || repass.equals("") || password.length() < 6 || repass.length() < 6){
            System.out.println("七七微聊提示您:您输入的密码格式不正确,密码不能为空,密码字段不能小于6位!");
            return;
        }
        if (!password.equals(repass)){
            System.out.println("七七微聊提示您:两次密码输入不一致!");
            return;
        }
        /**
         * 代表用户可以注册,拼接一个自定义协议 让服务端做注册地点逻辑
         * 协议为0代表注册
         * 0,nickname,password
         */
        String message = "0," + nickname + "," + password;
        ClientSendMessageUtils.sendMessage(socket,message);
    }
}
package com.kang.v5.client;

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

/**
 * 客户端发送数据的封装工具类
 */
public class ClientSendMessageUtils {
    private ClientSendMessageUtils(){

    }

    /**
     * 借助某个socket给服务端发送一个消息
     * @param socket
     * @param message
     */
    public static void sendMessage(Socket socket, String message) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
        bw.write(message);
        bw.newLine();
        bw.flush();
    }
}
package com.kang.v5.server;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * User实体类————JavaBean
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    private String nickname;
    private String password;
    private String number;

}
package com.kang.v5.server;

import java.util.List;
import java.util.Random;

public class ServerUtil {
    private ServerUtil(){

    }

    /**
     * 账号必须是唯一的
     * @return
     */
    public static String generatorNumber(){
        //生成一个账号
        Random random = new Random();
        String number = "" + random.nextInt(10) + random.nextInt(10) + random.nextInt(10) + random.nextInt(10) + random.nextInt(10) + random.nextInt(10);
        List<User> users = Server.users;
        boolean flag = false;
        for (User user : users) {
            if (user.getNumber().equals(number)){
                flag = true;
                break;
            }
        }
        if (flag) {
            number = generatorNumber();
        }
        return number;
    }
}
package com.kang.v5.server;

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

public class ServerSendMessageUtil {
    private ServerSendMessageUtil(){

    }
    public static void sendMessage(Socket socket, String message) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream,"UTF-8"));
        bw.write(message);
        bw.newLine();
        bw.flush();
    }
}

你可能感兴趣的:(Java,java,网络,开发语言)