记得之前我去培训Android时,有一个Socket编程面试题目是:
使用Socket编程实现文件传输
当时的自己很单纯,想着什么都是自己做,不去查怎么做。记得文件内容传输的实现很简单,但是文件名称该怎么传却难到了我,在难也要自己做,后面多建立了一条Socket专门用来传输文件名。
Socket编程好久没用到过了,了解NIO的时候,看到了Socket,所以特地回忆下:

Socket
Socket编程根据通信协议的不同可以分为UDP(数据通信协议),TCP(流通信协议)/IP两种;
UDP协议用到的API: DatagramSocket
TCP/IP协议用到的API :ServerSocket
DatagramSocket
UDP是一种无连接的协议,这就意味着我们每次发送数据报时,需要同时发送本机的socket描述符和接收端的socket描述符,而且其在大小上有64KB的限制,是一种不可靠的协议,发送的数据报不一定会按照其发送顺序被接收端的socket接受;
这里主要是想总结TCP/IP,这里就直接给贴出网上找的代码
服务器端代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 
 |   public static void main(String[] args) throws Exception{int serverPort = 9999;
 DatagramSocket ds = null;
 DatagramPacket sendDp;
 DatagramPacket receiveDp;
 
 ds = new DatagramSocket(serverPort);
 System.out.println("服务器创建成功!端口号为: "+ds.getLocalPort());
 
 byte[] buf = new byte[1024];
 receiveDp = new DatagramPacket(buf,buf.length);
 ds.receive(receiveDp);
 System.out.println("收到: "+ receiveDp.getSocketAddress());
 System.out.println("Data is "+ new String(receiveDp.getData(),0,receiveDp.getLength()));
 
 InetAddress clientIp = receiveDp.getAddress();
 int clientPort = receiveDp.getPort();
 
 String respose = "OK,收到来自星星的你的祝福";
 byte[] bData = respose.getBytes();
 sendDp = new DatagramPacket(bData,bData.length,clientIp,clientPort);
 ds.send(sendDp);
 ds.close();
 }
 
 
 | 
客户器端代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | public static void main(String[] args) throws Exception{DatagramSocket ds = null;//
 DatagramPacket sendDp;
 DatagramPacket receiveDp;
 String serverHost = "127.0.0.1";
 int serverPort = 9999;
 ds = new DatagramSocket();
 byte[] buf = "hello,UDP协议!来自星星的问候……".getBytes();
 sendDp = new DatagramPacket(buf,buf.length,InetAddress.getByName(serverHost),serverPort);
 
 ds.send(sendDp);
 
 byte[] bufr = new byte[1024];
 receiveDp = new DatagramPacket(bufr,bufr.length);
 ds.receive(receiveDp);
 
 byte[] response = receiveDp.getData();
 int len = receiveDp.getLength();
 String s = new String(response,0,len);
 System.out.println("服务器端反馈为: "+s);
 ds.close();
 }
 
 
 | 
ServerSocket
直接使用线程BIO
如果使用Socket编程,编写服务端,如果客户端有多个,那么编程模型就是1:N的关系;
服务端代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 
 | public class Server {
 public void start() throws IOException {
 
 ServerSocket serverSocket = new ServerSocket(9091);
 System.out.println("∂ 服务真在等待客户端接入请求");
 for (; ; ) {
 System.out.println("∂ for accept 之前");
 Socket socket = serverSocket.accept();
 new Thread(new ScoketChanil(socket)).start();
 }
 }
 
 public static void main(String[] args) throws IOException {
 new Server().start();
 }
 }
 
 
 public class ScoketChanil implements Runnable {
 
 public Socket socket;
 public ScoketChanil(Socket socket) {
 this.socket = socket;
 }
 
 @Override
 public void run() {
 InputStream inputStream = null;
 OutputStream outputStream = null;
 try {
 inputStream = socket.getInputStream();
 outputStream = socket.getOutputStream();
 byte[] bytes = new byte[1024];
 
 int len;
 while ((len = inputStream.read(bytes)) != -1) {
 String requestStr = new String(bytes, 0, len);
 System.out.println(requestStr);
 outputStream.write("yeah im copy".getBytes("UTF-8"));
 }
 } catch (IOException e) {
 e.printStackTrace();
 } finally {
 try {
 inputStream.close();
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 }
 }
 
 
 
 | 
客户端代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 
 | public class MultipleClientA {public static void main(String[] args) throws IOException {
 new Client("AAAA").start();
 }
 }
 
 public class Client {
 
 private String clientName;
 
 public Client(String clientName) {
 this.clientName = clientName;
 }
 
 public void start() throws IOException {
 Socket socket = new Socket("127.0.0.1", 9091);
 OutputStream outputStream = socket.getOutputStream();
 InputStream inputStream = socket.getInputStream();
 
 new Thread(new Runnable() {
 @Override
 public void run() {
 byte[] bytes = new byte[1024];
 int len;
 try {
 while ((len = inputStream.read(bytes)) != -1) {
 String s = new String(bytes, 0, len);
 System.out.println("接受到服务器端的相应    " + s);
 }
 } catch (IOException e) {
 e.printStackTrace();
 } finally {
 
 }
 }
 }).start();
 
 Scanner scanner = new Scanner(System.in);
 while (scanner.hasNextLine()) {
 String inputStr = scanner.next();
 if ("bye".equals(inputStr)) {
 break;
 }
 outputStream.write((clientName + inputStr).getBytes("UTF-8"));
 }
 outputStream.close();
 scanner.close();
 }
 }
 
 
 | 
使用线程池的伪BIO
如果使用Socket编程,编写服务端,如果客户端有多个,那么编程模型就是1:N的关系,服务器端很可能会出现线程太多导致CPU消耗殆尽,导致服务卡死或者宕机;所以出现了使用线程池来限制管理线程的伪BIO;
服务端代码
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | public class SupportThreadPoolServer {
 public void start() throws IOException {
 
 ServerSocket serverSocket = new ServerSocket(9091);
 ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 2, 100000, TimeUnit.MILLISECONDS, queue);
 
 for (; ; ) {
 Socket socket = serverSocket.accept();
 poolExecutor.execute(new ScoketChanil(socket));
 }
 
 }
 
 public static void main(String[] args) throws IOException {
 new SupportThreadPoolServer().start();
 }
 }
 
 | 
客户端代码
客户端的代码没变化
ServerSocket阻塞方法
 ServerSocket的accept()是一个阻塞方法,如果没有Socket接入会一直阻塞,直到有Socket建立连接。
文件传输问题解决
到了今天我还是没能想清楚怎么传输名字,但是我学会了查资料,把查到的内容学会了,也是自己的。
传输文件名客户端可以用DataOutputStream.writeUTF(“XXX”)将名字写出,服务器端使用DataInputStream.readUTF()得到名字;
自己的Java基础是很功利的看了一段时间视频学习的,清晰的记得学习路线是从最简单的语法到对象,再到集合,线程,IO,Socket编程,最终是泛型和反射;
今天自己也不那么较真了,不会的,虽然网上找到的不是自己的,但是学会了就是自己的。
GitHub登录不了?信任该网站之后再登录。