内容简介:Java 网络编程之TCP通信和简单的文件上传功能
TCP通信需要明确的几点:
- tcp通信是面向连接的,需要先启动服务端,再启动客户端。
-
客户端和服务端都要创建套接字对象,客户端需要指定服务端套接字(ip+port),而服务端必须指定服务端口。
Socket client_socket = new Socket("192.168.100.17",8888); //客户端套接字(Socket类的套接字为已连接套接字) ServerSocket listen_socket = new ServerSocket(8888); //服务端套接字,此时为监听套接字(已经bind()地址和端口了)
-
服务端需要使用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(); } }
-
客户端需要根据已连接套接字获取输出流,服务端需要根据套接字获取输入流。当然,既然有了已连接套接字,那么获取无论哪一端都可以获取到输入流、输出流。
OutputStream send_stream = client_socket.getOutputStream(); //客户端获取输出流 InputStream recv_stream = server_socket.getInputStream();
- 服务端应主动关闭已连接套接字,至于监听套接字则在合适的地方关闭。
- 服务端应该循环不断地负责接收。
简单的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实现文件上传功能:
- 客户端除了 套接字的输出流 ,还有 读取本地文件的输入流 ,还有 套接字的输入流 来读取来自服务端的反馈信息。
- 服务端也同样有三流:套接字的输入、输出流,写入上传目标文件的输出流。
-
客户端读取本地文件的所有数据后,需要使用套接字的
shutdownOutput()
来通知服务端套接字的输出流已到末尾。 - 服务端为了能为多人提供上传功能,需要使用多线程实现并发连接。
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(); } } } } }
本文永久更新链接地址 : http://www.linuxidc.com/Linux/2018-01/150198.htm
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Seasoned Schemer
Daniel P. Friedman、Matthias Felleisen / The MIT Press / 1995-12-21 / USD 38.00
drawings by Duane Bibbyforeword and afterword by Guy L. Steele Jr.The notion that "thinking about computing is one of the most exciting things the human mind can do" sets both The Little Schemer (form......一起来看看 《The Seasoned Schemer》 这本书的介绍吧!