zoukankan      html  css  js  c++  java
  • java 网络编程之TCP通信和简单的文件上传功能

    TCP通信需要明确的几点:

    1. tcp通信是面向连接的,需要先启动服务端,再启动客户端。
    2. 客户端和服务端都要创建套接字对象,客户端需要指定服务端套接字(ip+port),而服务端必须指定服务端口。
      Socket client_socket = new Socket("192.168.100.17",8888); //客户端套接字(Socket类的套接字为已连接套接字)
      ServerSocket listen_socket = new ServerSocket(8888);      //服务端套接字,此时为监听套接字(已经bind()地址和端口了)
      
    3. 服务端需要使用accept()方法将监听套接字转变为已连接套接字。这个监听套接字可以生成多个已连接套接字,这样连接后还能监听其他客户端的请求。因此,这里应该使用多线程实现并发访问。获得了已连接套接字,就可以获取很多客户端的信息,例如客户端的ip地址,发送请求的端口等。

      Socket server_scoket = socket.accept();
      Socket server_scoket2 = socket.accept();
      Socket server_scoket3 = socket.accept();
      

      服务端要实现并发连接,大致使用如下代码:其中ThreadTask是线程任务对象。

      public static void main(String[] args) throws IOException {
       ServerSocket listen_sock = new ServerSocket(8888);   //监听套接字只需创建一个,因此在任务之外
      
       while (true) {   //每建立一个连接,就开启一个线程
           Socket conn_sock = listen_sock.accept();  //没有新连接进来时,main主线程阻塞在此
           new Thread(new ThreadTask(conn_sock)).start();
       }
      }
      
    4. 客户端需要根据已连接套接字获取输出流,服务端需要根据套接字获取输入流。当然,既然有了已连接套接字,那么获取无论哪一端都可以获取到输入流、输出流。
      OutputStream send_stream = client_socket.getOutputStream();   //客户端获取输出流
      InputStream recv_stream = server_socket.getInputStream();
      
    5. 服务端应主动关闭已连接套接字,至于监听套接字则在合适的地方关闭。
    6. 服务端应该循环不断地负责接收。

    简单的Client端:

    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class TCPClient {
    
        public static void main(String[] args) {
            // 1.创建客户端套接字
            Socket c_sock = null;
            OutputStream client_outstream = null;
            try {
                c_sock = new Socket("192.168.0.124",8888);
    
                // 2.获取输出流
                client_outstream = c_sock.getOutputStream();
    
                // 3.输出数据
                client_outstream.write("Hello,i'm coming".getBytes());
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(c_sock != null){
                    try{
                        c_sock.close();
                    } catch(IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    简单的Server端:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class TCPServer {
    
        public static void main(String[] args) {
    
            // 1.创建监听套接字
            ServerSocket listen_sock = null;
            try {
                listen_sock = new ServerSocket(8888);
    
            } catch(IOException i) {
                i.printStackTrace();
            }
    
            Socket server_sock = null;
            InputStream in_sock = null;
            while (true) {
                try {
                    // 2.和客户端建立连接,生成已连接套接字,并获取客户端ip地址
                    server_sock = listen_sock.accept();
                    String client_ip = server_sock.getInetAddress().getHostAddress();
                    System.out.println("Client:  " + client_ip + "  connected");
    
                    // 3.根据已连接套接字,获取输入流,读取客户端发送的数据
                    in_sock = server_sock.getInputStream();
                    BufferedReader bufr = new BufferedReader(new InputStreamReader(in_sock));
                    String line = null;
                    while ((line = bufr.readLine()) != null) {
                        System.out.println(line);
                    }
    
                    // 4.关闭已连接套接字
                    server_sock.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    以下是tcp实现文件上传功能:

    1. 客户端除了套接字的输出流,还有读取本地文件的输入流,还有套接字的输入流来读取来自服务端的反馈信息。
    2. 服务端也同样有三流:套接字的输入、输出流,写入上传目标文件的输出流。
    3. 客户端读取本地文件的所有数据后,需要使用套接字的shutdownOutput()来通知服务端套接字的输出流已到末尾。
    4. 服务端为了能为多人提供上传功能,需要使用多线程实现并发连接。

    Client端:

    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class UploadClient {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            String server_addr = "192.168.0.124";
            int server_port = 8888;
    
            Socket send_sock = null;
            FileInputStream local_read = null;
            try {
                // 1.客户端套接字
                send_sock = new Socket(server_addr, server_port);
    
                // 2.获取连接管道的输出流
                OutputStream send_stream = send_sock.getOutputStream();
    
                // 3.字节输入流读取本地文件数据,并使用套接字的输出流发送出去
                local_read = new FileInputStream("d:/myjava/net/SQL.docx");
    
                byte[] buf = new byte[1024];
                int len = 0;
                while ((len = local_read.read(buf)) != -1) {
                    send_stream.write(buf, 0, len);
                }
    
                // 4.标记输出流到结尾
                send_sock.shutdownOutput();
    
                // 5.接收服务端的反馈数据,如上传成功,上传失败等
                InputStream recv_stream = send_sock.getInputStream();
                BufferedReader ack_recv = new BufferedReader(new InputStreamReader(recv_stream));
                String line = null;
                while ((line = ack_recv.readLine()) != null) {
                    System.out.println(line);
                }
    
            } catch (IOException i) {
                i.printStackTrace();
            } finally {
                if (send_sock != null) {
                    try {
                        send_sock.close();
                        local_read.close();
                    } catch (IOException i1) {
                        i1.printStackTrace();
                    }
                }
    
                if (local_read != null) {
                    try {
                        local_read.close();
                    } catch (IOException i2) {
                        i2.printStackTrace();
                    }
                }
            }
        }
    }
    

    Server端:

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class UploadServer {
        public static void main(String[] args) throws IOException {
            ServerSocket listen_sock = new ServerSocket(8888);   //监听套接字只需创建一个,因此在任务之外
    
            while (true) {   //每建立一个连接,就开启一个线程
                Socket conn_sock = listen_sock.accept();  //没有新连接进来时,main主线程阻塞在此
                new Thread(new Uploader(conn_sock)).start();
            }
        }
    }
    
    class Uploader implements Runnable {
        private File dest_dir = new File("d:/temp"); // 上传目录
        private Socket conn_sock = null; // 连接套接字
        InputStream recv_stream = null;
        FileOutputStream dest_stream = null;
    
        Uploader(Socket conn_sock) throws IOException {
            this.conn_sock = conn_sock;
        }
    
        public void run() {
            try {
                if (!dest_dir.exists()) {
                    dest_dir.mkdirs();
                }
                // 1.获取连接管道的输入流
                recv_stream = conn_sock.getInputStream();
    
                // 客户端ip
                String client_ip = conn_sock.getInetAddress().getHostAddress();
                System.out.println(client_ip + ".....connected");
    
                // 2.文件的上传位置,即输出目标,以ip命名。如果文件已存在,则使用括号加数字新建文件,如"192.168.100.23(1).txt"
                File dest_file = new File(dest_dir, client_ip + ".docx");
                int count = 1;
                while (dest_file.exists()) {
                    dest_file = new File(dest_dir, client_ip + "(" + count + ")" + ".docx");
                    count++;
                }
    
                // 3.读取数据并写入目标文件
                dest_stream = new FileOutputStream(dest_file);
                byte[] buf = new byte[1024];
                int len = 0;
                while ((len = recv_stream.read(buf)) != -1) {
                    dest_stream.write(buf, 0, len);
                }
    
                // 4. 向客户端反馈信息
                OutputStream ack_send = conn_sock.getOutputStream();
                byte[] text = "upload successful!".getBytes();
                ack_send.write(text);
    
            } catch (IOException e1) {
                e1.printStackTrace();
            } finally {
                if (dest_stream != null) {
                    try {
                        dest_stream.close();
                    } catch (IOException i) {
                        i.printStackTrace();
                    }
                }
                if (conn_sock != null) {
                    try {
                        conn_sock.close();
                    } catch (IOException i) {
                        i.printStackTrace();
                    }
                }
            }
        }
    }
    
  • 相关阅读:
    Servlet
    Web服务器和Tomcat
    DOM文档对象模型
    JavaScript总结
    CSS总结
    商城——购物车模块
    用户注册登录认证模块
    P2P技术之STUN、TURN、ICE详解
    P2P中的NAT穿越(打洞)方案详解
    NAT技术详解
  • 原文地址:https://www.cnblogs.com/f-ck-need-u/p/8250952.html
Copyright © 2011-2022 走看看