Java 网络编程详解(二)

35 篇文章 11 订阅

Java 网络编程详解系列文章目录:

在上一篇关于 Java 网络编程的文章中【Java网络编程详解(一)】,我们介绍网络通信的一些基础概念,如:OSI网络模型、TCP/IP 网络模型、UDP 协议、TCP 协议以及 Java 中如何实现网络编程。

Java 网络编程实现图片上传

这一次我们来学习下,如何通过 Java 网络编程实现图片上传功能。

下面来完成一个上传图片的功能,这和前面的上传文件没有什么区别。只不过操作文件的时候使用的流对象是字节流而已!

//服务器端
public class TcpImage {
 
    public static void main(String[] args) throws Exception {
 
        ServerSocket server = new ServerSocket(10909);
        Socket client = server.accept();
        // 获取客户端的输入流
        InputStream is = client.getInputStream();
        // 获取客户端的输出流
        OutputStream out = client.getOutputStream();
        // 文件存放的位置
        OutputStream os = new FileOutputStream("server.jpg");
 
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = is.read(buffer)) != -1) {
            os.write(buffer, 0, len);
        }
        // 反馈信息给客户端
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
        bw.write("图片上传完成");
        bw.flush();
        os.close();
        client.close();
        server.close();
    }
}



// 客户端
class Client {
    public static void main(String[] args) throws Exception {
        // 创建客户端服务对象
        Socket socket = new Socket("192.168.1.102", 10909);
        InputStream in = new FileInputStream("D:\\imagetest\\desk.jpg");
        // 获取客户端的输出流对象
        OutputStream os = socket.getOutputStream();
        // 获取客户端的输入流对象
        InputStream is = socket.getInputStream();
        byte[] buffIn = new byte[1024];
        int len = 0;
        while ((len = in.read(buffIn)) != -1) {
            os.write(buffIn, 0, len);
        }
        // 告诉服务器我已经发送完毕
        socket.shutdownOutput();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String message = br.readLine();
        System.out.println(message);
        in.close();
        socket.close();
    }
}

程序运行结果如下图所示:

我们知道这是单线程,一次只能服务一个客户端。当客户端 A 连接后,被服务器端获取到,服务器执行具体流程,这时B客户端连接,只能等待,因为服务器的端还没有处理完客户端 A 的请求,如果很多客户端都要和我这个服务器通讯的话,这个时候怎么办呢?最好服务器端将每个客户端封装到不同的线程里,这样就可以处理多个客户端请求了。

那么现在我们来实现一下,多个客户端实现文件上传:

public class TcpThreads implements Runnable {
    private Socket socket;
 
    public TcpThreads(Socket socket) {
        this.socket = socket;
    }
    public void run() {
        int count = 1;
        String ip = socket.getInetAddress().getHostAddress();
        try {
            System.out.println(ip + "............connected");
            // 获取客户端的输入流
 
            File file = new File(ip + ".jpg");
            // 如果文件已经存在,则修改名称使之加一,直到名称不等
            while (file.exists())
                file = new File(ip + "(" + (count++) + ")" + ".jpg");
            InputStream is = socket.getInputStream();
            // 获取客户端的输出流
            OutputStream out = socket.getOutputStream();
            // 文件存放的位置
            OutputStream os = new FileOutputStream(file);
 
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            // 反馈信息给客户端
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
            bw.write("图片上传完成");
            bw.flush();
            os.close();
            socket.close();
        } catch (Exception e) {
            throw new RuntimeException(ip + "上传失败");
        }
 
    }
}



class ClientImage {
    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.out.println("请输入一个文件有效路径");
            return;
        }
        File file = new File(args[0]);
 
        if (!(file.exists() && file.isFile())) {
            System.out.println("该文件已经存在,或者不是一个文件");
            return;
        }
        long maxSize = 1024 * 1024 * 5;
        if (file.length() > maxSize) {
            System.out.println("文件不能大于5M");
            return;
        }
 
        if(!file.getName().endsWith(".jpg")){
            System.out.println("文件只能是JPG");
            return;
        }
        // 创建客户端服务对象
        Socket socket = new Socket("192.168.1.102", 12345);
        InputStream in = new FileInputStream(file);
        // 获取客户端的输出流对象
        OutputStream os = socket.getOutputStream();
        // 获取客户端的输入流对象
        InputStream is = socket.getInputStream();
        byte[] buffIn = new byte[1024];
        int len = 0;
        while ((len = in.read(buffIn)) != -1) {
            os.write(buffIn, 0, len);
        }
        // 告诉服务器我已经发送完毕
        socket.shutdownOutput();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String message = br.readLine();
        System.out.println(message);
        in.close();
        socket.close();
    }
}


// 对上面的客户端、服务端程序进行测试

class ServerImage {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(12345);
        while (true) {
            Socket socket = server.accept();// 阻塞式方法,当获取到客户端对象,就创建线程
            new Thread(new TcpThreads(socket)).start();
        }
    }
}

 

现在就可以对多个客户端服务了,从上面的服务端的代码可以发现:while(true)  说明服务器是一直开着的,可以不断的接收客户端的请求。

程序运行结果,如下图所示:

 

又因为 accept() 是阻塞式方法,当没有客户端请求服务器时,服务器一直处于阻塞状态,直到有客户端请求。如果有多个客户端访问,也会创建一个新线程为该客户端服务,因为服务器处理代码已经封装到了 run() 方法里面去了!

Java 网络编程实现并发登陆

接下来我们实现一个与上面程序类似的小程序:TCP 客户端并发登录。

// 服务器端程序
class ServerLogon implements Runnable {
 
    private Socket socket;
 
    public ServerLogon(Socket socket) {
        this.socket = socket;
    }
 
    public void run() {
    
        String ip = socket.getInetAddress().getHostAddress();
        try {
            //打印客户端的ip
            System.out.println(ip + "............. connected!");
            BufferedReader br = new BufferedReader(new InputStreamReader(socket
                    .getInputStream()));
 
            PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
            
            // 只有三次尝试的机会
            for (int i = 0; i < 3; i++) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        new FileInputStream("user.txt")));
                String username = br.readLine();
                //如果用户直接退出
                if (username == null) {
                    break;
                }
                
                String line = null;
                boolean flag = false;
                while ((line = reader.readLine()) != null) {
                    if (line.equals(username)) {
                        flag = true;
                        break;
                    }
                }
                //如果用户名相同
                if (flag) {
                    System.out.println(username + "登录成功");
                    pw.println("欢迎登录");
                    
                }else {
                    System.out.println(username + "尝试登录");
                    pw.println("无效的用户名");
                }
                reader.close();
            }
            socket.close();
            
 
        } catch (IOException e) {
            throw new RuntimeException(ip + "连接失败");
        }
    }
}


// 客户端程序
class UserClient {
    public static void main(String[] args) throws Exception {
        
        Socket socket = new Socket("192.168.1.102", 11111);
        PrintWriter bw = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(new InputStreamReader(socket
                .getInputStream()));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        for (int i = 0; i < 3; i++) {
            String name = br.readLine();
            System.out.println(name);
            //如果客户端按Ctrl+c
            if (name == null)
                break;
            bw.println(name);
            String message = in.readLine();
            System.out.println(message);
            if (message.contains("欢迎")) {
                break;
            }
        }
        br.close();
        socket.close();
 
    }
 
}

 
// 测试程序
public class TcpLogon {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(11111);
        while (true) {
            Socket socket = server.accept();
            new Thread(new ServerLogon(socket)).start();
        }
    }
}

 
用户信息如下所示:

 

其实,我们写的客户端与服务端,程序不管是多线程还是单线程,它的总体思想是不变的,它们都是和 I/O 流一起使用的,管服务器多么复杂,基本的原理还是这样的。学好这些,有助于我们以后理解学习和使用服务器 如 Tomcat 等 Web 服务器。


 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Chiclaim

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

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

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

打赏作者

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

抵扣说明:

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

余额充值