Socket是什么
期末终于考完啦,接下来这段时间的空闲就比较多了,应该会进入一个比较高速的更新期,在上一篇了解了计算机网络的基本架构后,我们今天来看一看如何用Socket实现一个简单的网络通讯
Socket是什么东西?如果你打开翻译。会发现这个单词的大概释义是插头,插座等等,一般在计算机语境下我们将其翻译为套接字(这个翻译真的是难以评价,丝毫不能体现其实际作用),也就是一套事先约定的计算机间进行网络通信的一套协议,支持TCP和UDP(Java中的Socket是基于TCP的)。今天我们将会使用这一套协议实现一个简单的群聊,并通过这个过程来初步了解Socket
建立连接
在实现聊天之前,我们首先要建立一个连接
要建立一个连接,我们需要一个服务端来监听与处理请求,一个客户端来发起请求,先来完成服务端
1 2 3 4 5 6 7 8 9
| public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("Server started"); System.out.println("等待对方加入"); Socket socket = serverSocket.accept(); System.out.println("有用户加入"+socket.getInetAddress().getHostAddress()); Thread.sleep(1000); System.out.println("连接结束"); }
|
我们一个个来解释
1
| ServerSocket serverSocket = new ServerSocket(8888);
|
ServerSocket对象会持续的监听对应端口.即8888端口,检查是否有链接请求(如果不知道端口等相关知识,请阅读走进网络世界 - Soul的小站 (soulmate.org.cn))
1
| Socket socket = serverSocket.accept();
|
这个方法是阻塞的,当监听到连接请求时accept方法会同意连接并将这个连接包装为一个Socket对象,接下来我们的一切通讯都将通过这个对象进行
接下来我们完成客户端
1 2 3 4 5
| public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",8888); System.out.println("成功连接到"+socket.getRemoteSocketAddress()); socket.close(); }
|
1
| Socket socket = new Socket("127.0.0.1",8888);
|
向IP为127.0.0.1的8888端口发送请求建立连接,127.0.0.1是回环地址,即向本地发起连接请求
怎么样是不是很简单(把上面的两个main方法分别放在两个类中就可以运行了)
实现信息传输
要实现信息传输,就要用到IO流,关于IO流(Java中的IO流 - Soul的小站 (soulmate.org.cn))
此外,我们要实现收发消息的的异步,所以最好是有两个独立的线程来负责消息的收发,(Java多线程 - Soul的小站 (soulmate.org.cn))
我们将代码稍加完善就可以得到
服务端
1 2 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 55 56 57 58 59 60 61 62 63
| import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner;
public class Main { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("Server started"); System.out.println("如需退出请输入exit"); System.out.println("等待对方加入"); Socket socket = serverSocket.accept(); System.out.println("有用户加入"+socket.getInetAddress().getHostAddress()); Thread t1=new Thread(()->{ BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { throw new RuntimeException(e); } try {
while(!Thread.currentThread().isInterrupted()) {
String msg= br.readLine(); System.out.println(socket.getInetAddress().getHostAddress()+":"+msg); } } catch (IOException e) { System.out.println("连接异常,可能为对方断开连接"); }
}); Thread t2=new Thread(()->{ BufferedWriter bw=null; try{ bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); while(true){ Scanner scanner = new Scanner(System.in); String input=scanner.nextLine(); if(input.equals("exit")){ t1.interrupt(); break; } bw.write(input); bw.flush(); } }catch (IOException e){ System.out.println("发送异常"); }finally {
try { socket.close(); serverSocket.close(); } catch (IOException e) { System.out.println("未知异常"); } } }); t1.start(); t2.start(); } }
|
客户端
1 2 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 55 56 57
| import java.io.*; import java.net.Socket; import java.util.Scanner;
public class Main { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",8888); System.out.println("成功连接到"+socket.getRemoteSocketAddress()); System.out.println("如需退出请输入exit");
Thread t1=new Thread(()->{ BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { throw new RuntimeException(e); } String msg=null; while(!Thread.currentThread().isInterrupted()){ try { msg= br.readLine(); } catch (IOException e) { System.out.println("连接断开"); } System.out.println(socket.getRemoteSocketAddress()+":"+msg); }
}); Thread t2=new Thread(()->{ BufferedWriter bw=null; try{ bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); Scanner scanner=new Scanner(System.in); while(true){ String input=scanner.nextLine(); if(input.equals("exit")){ t1.interrupt(); break; } bw.write(input+"\n"); bw.flush(); } } catch (IOException e) { throw new RuntimeException(e); }finally { try { socket.close(); } catch (IOException e) { throw new RuntimeException(e); } } }); t1.start(); t2.start();
} }
|
上面的代码中我们使用了缓冲流以提高性能,当然由于没有仔细涉及,这部分代码需要改进的地方还是挺多的
实现群聊
其实写到这里我相信你也可以独立的实现一个群聊了,这里我就不具体的写实现了,你可以试着自己完成(最好完善我上面代码的异常处理机制)
提示:不一定要在我给出的代码的基础上修改,你可以自己从头重新实现
关于UDP
既然Socket默认是使用TCP进行通信的,那么我们有改如何实UDP通信呢,我们可以使用DatagramSocket和
DatagramPacket两个类来实现
修改服务端
1 2 3 4
| DatagramSocket socket = new DatagramSocket(8888); byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); socket.receive(packet);
|
修改客户端
1
| socket = new DatagramSocket("127.0.0.1",8888);
|
剩下的就没什么区别了,完全可以和一般的Socket以同种方法使用
补充
实际上Socket类中的方法还是很丰富的,但是这些方法与我们无关,我们只需要建立连接,然后用IO流实现信息的传输即可。当然,从使用的角度来说了解这些就足够了,但是作为一个有远大志向的码农,我们最好还是学习一下TCP和UDP的具体实现过程,感兴趣的可以自行了解,我在这里就不过多的讲了