网络编程_TCP通信综合练习:

1

image.png

//client::
public class Client {
    public static void main(String[] args) throws IOException {
        //多次发送数据

        //创建socket对象,填写服务器的ip以及端口
        Socket s=new Socket("127.0.0.1",10000);

        //获取输出流
        OutputStream op = s.getOutputStream();

        //因为要不断发送数据,所以可以使用Sacnner录入数据,结合循环将数据发出去
        Scanner sc=new Scanner(System.in);
        while (true) {
            //键盘录入
            System.out.println("请输入一个数据");
            String str = sc.next();
            //如果输入886就退出发送
            if ("886".equals(str)){
                break;
            }
            op.write(str.getBytes());
        }
        //关流
        s.close();
    }
}
//Server:
public class Server {
    public static void main(String[] args) throws IOException {
        //不断接收
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10000);

        //等待客户端来连接
        Socket socket = ss.accept();
        //创建一个输入流对象
        InputStream is = socket.getInputStream();
        //转换流:字节转字符
        InputStreamReader isr = new InputStreamReader(is);

        int b;
        while ((b = isr.read()) != -1) {
            System.out.print((char) b);
        }
        //关流
        socket.close();//关闭通道
        ss.close();//关闭服务器

    }
}


先启动服务器,再启动客户端
控制台:
image.pngimage.png

2

image.png

//client
public class Client {
    public static void main(String[] args) throws IOException {
        //客户端:发送一条数据,接收服务端反馈的消息并打印
        //服务器:接收数据并打印,再给客户端反馈消息



        //1.创建Socket对象并连接服务端
        Socket socket = new Socket("127.0.0.1",10000);


        //2.写出数据
        String str = "见到你很高兴!";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

        //写出一个结束标记,(结束输出流)
        socket.shutdownOutput();


        //3.接收服务端回写的数据
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        while ((b = isr.read()) != -1){
            System.out.print((char)b);
        }


        //释放资源
        socket.close();


    }
}

//Server
public class Server {
    public static void main(String[] args) throws IOException {
        //客户端:发送一条数据,接收服务端反馈的消息并打印
        //服务器:接收数据并打印,再给客户端反馈消息


        //1.创建对象并绑定10000端口
        ServerSocket ss = new ServerSocket(10000);

        //2.等待客户端连接
        Socket socket = ss.accept();

        //3.socket中获取输入流读取数据
        InputStream is = socket.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        int b;
        //细节:
        //read方法会从连接通道中读取数据
        //但是,需要有一个结束标记,此处的循环才会停止
        //否则,程序就会一直停在read方法这里,等待读取下面的数据
        while ((b = isr.read()) != -1){
            System.out.println((char)b);
        }

        //4.回写数据
        String str = "到底有多开心?";
        OutputStream os = socket.getOutputStream();
        os.write(str.getBytes());

        //释放资源
        socket.close();
        ss.close();


    }
}

image.pngimage.png

3

image.png
下面的例子是传输图片,所以使用字节流

//Client
public class Client {
    public static void main(String[] args) throws IOException {

        //创建Socket对象连接服务器
        Socket s = new Socket("127.0.0.1", 10000);

        //读取图片文件:使用字节流
        //图片较大:使用缓冲流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("..\\netcode\\clientdir\\a.jpg"));

        //在通道中获取字节流,输入数据到服务端
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        //边读边写
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.flush();

        //结束标识
        s.shutdownOutput();

        //接受服务端回馈:字节流-->字符流---->缓冲流
        BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));

        int b;
        while((b=br.read())!=-1){
            System.out.print((char) b);
        }
        //关流
        s.close();
    }
}

//server
public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10000);
        //等待客户端连接
        Socket socket = ss.accept();

        //获取通道中传来的数据
        //字节流读取字节
        //缓冲流提升效率
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        //输出到本地
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\a.jpg"));

        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.flush();

        //返回信息:字节流-->字符流---->缓冲流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        //关流
        socket.close();
        ss.close();
    }

}

image.pngimage.png

4.

image.png

//client
public class Client {
    public static void main(String[] args) throws IOException {

        //创建Socket对象连接服务器
        Socket s = new Socket("127.0.0.1", 10000);

        //读取图片文件:使用字节流
        //图片较大:使用缓冲流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("..\\netcode\\clientdir\\a.jpg"));

        //在通道中获取字节流,输入数据到服务端
        BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
        //边读边写
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.flush();

        //结束标识
        s.shutdownOutput();

        //接受服务端回馈:字节流-->字符流---->缓冲流
        BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));

        int b;
        while((b=br.read())!=-1){
            System.out.print((char) b);
        }
        //关流
        s.close();
    }
}

//Server
public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10000);
        //等待客户端连接
        Socket socket = ss.accept();

        //获取通道中传来的数据
        //字节流读取字节
        //缓冲流提升效率
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

        //随机uuid、
        String str = UUID.randomUUID().toString().replace("-", "");
        //输出到本地
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));

        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }
        bos.flush();

        //返回信息:字节流-->字符流---->缓冲流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        bw.write("上传成功");
        bw.newLine();
        bw.flush();

        //关流
        socket.close();
        ss.close();
    }

}

image.png

5.

image.png
服务端不停止,用户端一直传
思路:可以在服务端使用循环嵌套:
如下:

public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10000);



        while (true) {
            //等待客户端连接
            Socket socket = ss.accept();

            //获取通道中传来的数据
            //字节流读取字节
            //缓冲流提升效率
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

            //随机uuid、
            String str = UUID.randomUUID().toString().replace("-", "");
            //输出到本地
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));

            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);//当第一个用户还在传输时,服务端代码还会在这不断循环
            }
            bos.flush();

            //返回信息:字节流-->字符流---->缓冲流
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();
            //关流
            socket.close();//断开当前通道
            //ss.close();//关闭服务器    ----这里不可以让服务器关闭
        }



    }
}

但是循环有弊端,它是一种单线程,如果说此时要传输的文件很大,当第一个用户还在传输时,服务端代码还会停止在第25行,此时就无法和第二个用户产生连接(只有完成一次循环后,才能和下一个用户进行数据连接),所以我们可以用多线程来改进。使多个用户可以同时传输数据,服务端也可以同时读取多个用户的数据

//多线程
public class MyThread  extends Thread{

    Socket socket;
     public MyThread (Socket socket){
         this.socket=socket;
     }


    @Override
    public void run() {
        //获取通道中传来的数据
        //字节流读取字节
        //缓冲流提升效率
        try {
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            //随机uuid、
            String str = UUID.randomUUID().toString().replace("-", "");
            //输出到本地
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));

            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            bos.flush();

            //返回信息:字节流-->字符流---->缓冲流
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            //5.释放资源
            if (socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

//Server
public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10000);

        while (true) {
            //等待客户端连接
            Socket socket = ss.accept();
            new MyThread(socket).start();
            //ss.close();
        }

    }
}

//client....

6.

image.png
使用自定义线程池

//Server
public class Server {
    public static void main(String[] args) throws IOException {


        //1创建线程池对象
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量
                16,//线程池总大小
                60,//空闲时间
                TimeUnit.SECONDS,//单位
                new ArrayBlockingQueue<>(2),//队列
                Executors.defaultThreadFactory(),//线程工厂,让线程池如何创建线程对象
                new ThreadPoolExecutor.AbortPolicy()//拒绝策略
        );

        //2创建ServerSocket对象
        ServerSocket ss = new ServerSocket(10000);

        while (true) {
            //3等待客户端连接
            Socket socket = ss.accept();
            //开启一条线程
            //一个用户就对应服务端的一条线程

            //调用submit方法传入myRunnable对象
            pool.submit((new MyRunnable(socket)));
            //ss.close();
        }

    }
}
//MyRunnable
public class MyRunnable implements Runnable {

    Socket socket;
     public MyRunnable(Socket socket){
         this.socket=socket;
     }


    @Override
    public void run() {
        //获取通道中传来的数据
        //字节流读取字节
        //缓冲流提升效率
        try {
            BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
            //随机uuid、
            String str = UUID.randomUUID().toString().replace("-", "");
            //输出到本地
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("..\\netcode\\serverdir\\" + str + ".jpg"));

            byte[] bytes = new byte[1024];
            int len;
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            bos.flush();

            //返回信息:字节流-->字符流---->缓冲流
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            //5.释放资源
            if (socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//client.....


7.

image.png

public class Server {
    public static void main(String[] args) throws IOException {
        //客户端:多次发送数据
        //服务器:接收多次接收数据,并打印

        //1.创建对象绑定10000端口
        ServerSocket ss = new ServerSocket(10000);

        //2.等待客户端来连接
        Socket socket = ss.accept();

        //3.读取数据
        InputStreamReader isr = new InputStreamReader(socket.getInputStream());
        int b;
        while ((b = isr.read()) != -1){
            System.out.print((char)b);
        }

        //4.释放资源
        socket.close();
        ss.close();

    }
}

首先启动服务端:
再在浏览器输入:
image.png
获得到数据:
image.png

8.

image.png
client

package A;

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

public class Client1 {
    public static void main(String[] args) throws IOException {
        /**
         * 每一个客户端都是一个线程
         */


        while (true) {
            //1.与服务端建立连接
            Socket socket = new Socket("127.0.0.1", 10000);
            System.out.println("服务器已连接");
            //2生成聊天界面
            System.out.println("==============欢迎来到黑马聊天室================");
            System.out.println("1登录");
            System.out.println("2注册");
            System.out.println("请输入您的选择:");
            //3键盘录入
            Scanner sc = new Scanner(System.in);
            String choose = sc.nextLine();
            //判断:
            switch (choose) {
                case "1":
                    //登录逻辑
                    login(socket);
                    break;
                case "2":
                    //注册逻辑

                    break;
                default:
                    System.out.println("没有该选项");

            }
        }
    }

    /**
     * //登录
     *
     * @param socket
     * @throws IOException
     */
    private static void login(Socket socket) throws IOException {
        //1输入账户和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.nextLine();
        System.out.println("请输入密码");
        String password = sc.nextLine();

        //***登录要求使用username=zhangsan&password=123这种格式发给服务端
        //2拼接
        StringBuilder sb = new StringBuilder();
        sb.append("username=").append(username).append("&password=").append(password);

        //3提交给服务器验证
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        //第一次是告诉服务器是此时登陆操作是什么
        bw.write("login");
        bw.newLine();//这两个要配套使用
        bw.flush();
        //第二次告诉服务器用户端输入的账号密码
        bw.write(sb.toString());
        bw.newLine();
        bw.flush();

        //接收服务端的回馈消息
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String message = br.readLine();

        if ("1".equals(message)) {
            System.out.println("登陆成功,可以开始聊天");

            //开一条单独的线程,专门用来接收服务端转发过来的聊天记录
            new Thread(new ClientMyRunnable(socket)).start();


            //将要说的话传给服务器,交给服务器转发给其他客户端
            talk2All(bw);
        } else if ("2".equals(message)) {
            System.out.println("密码不正确");
        } else {
            System.out.println("用户名不存在,请先注册");
        }
    }

    private static void talk2All(BufferedWriter bw) throws IOException {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入你要说的话");
            String message = sc.nextLine();
            bw.write(message);
            bw.newLine();
            bw.flush();
        }


    }
}

class ClientMyRunnable implements Runnable {
    Socket socket;

    //构造
    public ClientMyRunnable(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {

        //循环包裹,不断读取服务端发过来的信息(接受群发消息)
        while (true) {
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String message = br.readLine();
                System.out.println(message);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }
}


server

package A;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.Buffer;
import java.nio.file.attribute.UserPrincipal;
import java.util.ArrayList;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server {
    //成员位置方便调用
  static  ArrayList<Socket>list=new ArrayList<>();

    public static void main(String[] args) throws IOException {
        //1创建ServerSocket对象,并连接10000端口
        ServerSocket ss = new ServerSocket(10000);

        //2把本地文件中的正确用户名和密码都获取到
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("..\\Chat\\account.txt");
        prop.load(fis);
        fis.close();
        //只要来了一个客户端,就开一个条线程
        while (true) {
            //等待服务端连接
            Socket socket = ss.accept();
            System.out.println("有客户端来连接");
            //开始处理线程任务
            new Thread(new MyRunnable(socket, prop)).start();
        }
    }
}

//-----------------------------------------------------------------------
class MyRunnable implements Runnable {
    Socket socket;
    Properties prop;

    //构造
    public MyRunnable(Socket socket, Properties prop) {
        this.socket = socket;
        this.prop = prop;
    }

    @Override
    public void run() {
        /**
         * 每一个线程要做的事情
         */
        try {
            //1读取客户端传来的信息
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //第一次读取的是用户的操作
            String choose = br.readLine();
            while (true) {
                switch (choose) {
                    case "login" -> login(br);
                    case "register" -> register();
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 1.获取用户端输入的帐号、密码
     * 2.与正确的账号密码比较
     * 3.写出不同情况的返回信息
     */
    public void login(BufferedReader br) throws IOException {
        System.out.println("用户执行了登陆操作");
        //第二次读取的是用户端传递过来的拼接信息:username=zhangsan&password=123
        String userInfo = br.readLine();
        //获取真正的账号密码:切割
        String s1 = userInfo.split("&")[0];
        String s2 = userInfo.split("&")[1];
        String usernameInput = s1.split("=")[1];
        String passwordInput = s2.split("=")[1];
        System.out.println("账号是:" + usernameInput);
        System.out.println("密码是:" + passwordInput);

        //比较
        if (prop.containsKey(usernameInput)) {
            //用户名一致,就比较密码
            //先获取当前正确密码
            String rellyPassword = (String) prop.get(usernameInput);
            if (passwordInput.equals(rellyPassword)) {
                //登陆成功,给用户端返回信息
                messageToClient("1");//登陆成功,可以开始聊天
                //登陆成功后需要将当前socket对象存储起来
                Server.list.add(socket);
                //接收客户端发送的信息并打印在控制台
                talk2All(br,usernameInput);
            } else {
                //密码不正确,给用户端返回信息
                messageToClient("2");//密码不正确

            }
        } else {
            //用户名不存在,给用户端返回信息:
            messageToClient("3");//用户名不存在,请先注册

        }
    }

    private void talk2All(BufferedReader br, String usernameInput) throws IOException {
        //聊天死循环
        while(true){
            //接收客户端发送的信息
            String message = br.readLine();
            System.out.println(usernameInput+"发送过来了消息:"+message);

            //群发
            for (Socket s : Server.list) {
                //s以此表示每一个socket对象
                messageToClient(s,usernameInput+"发送过来了消息:"+message);
            }
        }
    }


    /**
     *因为三种情况:登陆成功、密码不正确、用户名不存在都要返回信息给客户端,所以干脆抽取成方法
     * @param message
     */
    public void messageToClient(String message) throws IOException {
        //获取输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(message);
        bw.newLine();
        bw.flush();
    }

    /**
     * 重载的messageToClient
     * @param socket
     * @param message
     */
    public void messageToClient(Socket socket,String message) throws IOException {
        //获取输出流,将数据写给当前的socket通道对象
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        bw.write(message);
        bw.newLine();
        bw.flush();
    }















    /**
     * 注册逻辑
     */
    private static void register() {
        System.out.println("用户执行了注册操作");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

成果、

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

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

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

打赏作者

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

抵扣说明:

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

余额充值