Socket编程(JavaSE结束)

209 阅读7分钟

1.网络有什么好处

网络可以实现多个计算机相互通信、数据传递、资源共享... 如何多个计算机相互通信?

发送请求(URL(网址)): 统一资源定位符

一个完整URL主要包含:

协议://ip地址:端口号/目标资源

比如: http://192.168.1.222:9999/XXX

  • 如果访问目标资源是首页 index.html index.jsp App.vue 是可以省略的
  • 如果访问程序端口号是80 也可以省略
  • 如果协议是Http协议 一般浏览器都会支持 也可以省略
  • 所以剩下ip地址就可以正常 访问目标服务器
  • 由于IP是路由器动态分配的 所以容易改变
  • 由于IP地址是由几个数字构成的 也不好记忆
  • 所以可以将几组IP地址 绑定一个域名 比如baidu.com

2.网络通信协议

协议:表示网络传输中 大家都要一起遵循的规范 无论是发送数据还是接收数据都有统一的标准 这样更加便于计算机的通信

  • http协议:底层实现是基于TCP协议,超文本传输协议
  • https协议:基于Http协议的加密版本 在网络传输时底层实现加密算法 防止外部攻击的
  • ftp协议:用于上传文件的服务器 后期传递文件 不是在本地磁盘(数据库)而是通过文件服务器 或者云服务器
  • TCP协议:网络传输协议
  • UDP协议:用户数据报协议

2.1 TCP和UDP协议区别 (面试常考)

  • TCP面向连接的,UDP协议没有连接
  • TCP是可靠的(发送数据和接收数据都要负责),UDP是不可靠的(类似于广播,只管发送 不管你是否接收到了)
  • TCP是面向字节流传输的可以传输任何内容(底层还实现了缓冲区 等缓冲区存储满了 才一口气以流的形式进行发送) 而UDP是面向报文传输(交给UDP无论是多长的报文 无需等待以数据包的形式进行发送)

2.2 TCP三次握手 ---面试题(具体可参考图解网络)

注:SYN:请求连接数据包,ACK:确认数据包

  • 第一次握手:浏览器向服务器发送一个建立连接的请求(SYN请求连接数据包)
  • 第二次握手:服务器告诉浏览器 同意连接请求 同时服务器也向浏览器建立连接的请求(ACK确认数据包+SYN请求连接数据包)
  • 第三次握手:浏览器也要告诉服务器 同意连接(ACK确认数据包)

2.3 TCP四次挥手 ---面试题(具体可参考图解网络)

注:FIN断开连接数据包,ACK:确认数据包

  • 第一次挥手:浏览器打算断开连接,向服务器发送一个FIN数据包
  • 第二次挥手:服务器接收到了对方发送的FIN数据包 就向浏览器发送ACK确认数据包
  • 第三次挥手:服务器也打算断开连接,向浏览器发送一个FIN数据包
  • 第四次挥手:浏览器接收了对方发送的FIN数据包 向服务器发送ACK确认包

3.IP地址

ip地址目的就是快速找到是哪一台计算机(类似于家庭地址),是由32位二进制数构成,会分成四组 每组8位 由于不好记忆 通常会转换成十进制,由于IP地址也不好记忆,一般公司会把自己公司的项目绑定一个域名

4.端口

端口目的是用来区分同一台计算机不用的应用程序,所以说每个应用程序一定会有不同的端口号,端口号取值范围:165535 一般来说11024是系统默认端口号 一定不要使用 否则会出现端口被占用的错误

常用程序端口号:http默认端口80 ftp默认端口21 mysql默认端口3306 oracle默认端口1521 redis默认端口6379 tomcat默认端口8080

5.Socket

socket时表示在网络运行过程中可以进行双向通信 基于C/S 是基于客户端(client)和服务端(server)双向通信 可以通过客户端服务端发送数据,反过来也是可以的 通过IP地址+端口来访问服务端 这样就可以创建Socket对象进行通信了

5.1 Socket工作原理 ---选择题

  • 创建服务端对象(ServerSocket)和客户端对象(Socket)其实两个类就可以实现
  • 服务段提供端口号 便于客户端连接
 ServerSocket server=new ServerSocket(端口号)
 Socket client=server.accept();//等待客户端连接(线程阻塞)    
  • 客户端 通过ip地址+端口号 连接服务端
 Socket client=new Socket(ip地址,端口号);
  • 通过IO流(字节流输入和输出)进行通信
 //负责读取对方给我写入的数据
 InputStream is=client.getInputStream();
 //负责向对方写入数据
 OutputStream os=client.getOutputStream();
  • 关闭资源
 is.close();
 out.close();
 client.close();
 server.close();

5.2 Socket应用场景

  • 可以向服务器和客户端传递数据(String 文件 对象)
  • 实现聊天功能
  • 实现多人聊天

6.Socket代码案例

  • 测试服务端和客户端连接
 //测试服务端
 public class TestServer {
     public static void main(String[] args) throws IOException {
         ServerSocket server=new ServerSocket(9999);
         while(true) {
             System.out.println("开始执行一次");
             Socket client = server.accept(); //阻塞
             System.out.println(client);
         }
     }
 }
 //测试客户端
 class TestClient{
     public static void main(String[] args) throws IOException {
         Socket client=new Socket("192.168.0.114",9999);
     }
 }
  • 测试客户端和服务端发送数据
 //实现: socket接收数据
 public class Server1 {
     public static void main(String[] args) throws IOException {
         //1.创建对象
         ServerSocket server=new ServerSocket(9999);
         while(true){
             try {
                 //2.等待客户端连接
                 Socket client = server.accept();
                 System.out.println(client + "连接成功");
                 //3.获取输入输出流
                 InputStream is = client.getInputStream();
                 OutputStream os = client.getOutputStream();
                 String message = "你连接成功了\n";
                 os.write(message.getBytes());
                 BufferedReader br = new BufferedReader(new InputStreamReader(is));
                 String result = br.readLine();//bug 读取一行
                 System.out.println(client.getInetAddress() + "客户端:" + result);
             }catch (Exception e){
             }
         }
     }
 }
 class Client1{
     public static void main(String[] args) throws IOException {
         //1.创建对象
         Socket client=new Socket("192.168.0.114",9999);
         //2.创建输入输出流
         BufferedReader br=new BufferedReader(
                 new InputStreamReader(
                         client.getInputStream()));
         OutputStream os=client.getOutputStream();
         String result=br.readLine();
         System.out.println("服务端:"+result);
         os.write("我是客户端\n".getBytes());
     }
 }
  • 测试多线程使用Socket
 //socket通过多线程接收数据
 public class Server2 {
     public static void main(String[] args) throws IOException {
         ServerSocket server=new ServerSocket(9999);
         while(true) {
             Socket client = server.accept();
             System.out.println(client + "连接成功");
             //通过线程负责每个客户端的读任务  给所有进入服务端的对象进行写入
             new ServerThread(client).start();
         }
     }
 }
 //客户端数据 通过Scanner动态写入
 class Client2{
     public static void main(String[] args) throws IOException {
         Socket client=new Socket("192.168.0.114",9999);
         OutputStream os=client.getOutputStream();
         //System.in 系统输入流  读取控制台的输入的数据
         //Scanner是基于它的封装
         BufferedReader br=new BufferedReader(
           new InputStreamReader(System.in)
         );
         //添加一个客户端线程:负责接收服务端返回的数据
         while(true) {
             System.out.println("请输入你要向服务端写入的内容:");
             String msg=br.readLine();
             os.write((msg+"\n").getBytes());
         }
     }
 }
 //服务端线程: 目的是监控每一个人数据读写
 class ServerThread extends Thread{
     Socket client;
     public ServerThread(Socket client){
         this.client=client;
     }
     public void run() {
         try {
             BufferedReader br = new BufferedReader(
                     new InputStreamReader(
                             client.getInputStream()));
             while(true) {
                 String clientMsg = br.readLine();
                 System.out.println(client.getInetAddress() + ":" + clientMsg);
             }
         }catch (Exception e){
         }
     }
 }
  • 通过Socket模拟多人聊天室
 //模拟多人聊天室
 public class Server3{
     //用于保存进入聊天室客户端对象
     static List<Socket> list=new ArrayList<>();
     public static void main(String[] args) throws IOException {
         //创建服务端对象
         ServerSocket server=new ServerSocket(9999);
         //死循环 等待无数个客户端连接 保存进入聊天室的客户端对象
         System.out.println("****多人聊天室开启成功****");
         while(true){
             Socket client=server.accept();
             list.add(client);//类似于进入群聊
             System.out.println("聊天室"+list.size()+"人");
             //创建每个客户端的线程单独处理每人的任务
             new ServerThread3(client).start();
         }
     }
 }
 class ServerThread3 extends Thread{
     Socket client;
     public ServerThread3(Socket client){
         this.client=client;
     }
     //创建每个客户端的线程 去监听每个客户端写入的数据
     //将读取到的消息 群发其他客户端(除了当前哪个客户端)
     public void run() {
         try {
             BufferedReader br = new BufferedReader(
                     new InputStreamReader(client.getInputStream())
             );
             while(true){
                 String msg=br.readLine();
                 if(msg!=null){
                     System.out.println(msg);
                     if(msg.contains("esc")){ //退出聊天室
                         Server3.list.remove(client);
                         System.out.println("聊天室的人数:"+Server3.list.size());
                         break;
                     }
                     //向其他客户端写入数据
                     for(Socket s:Server3.list){
                         if(s!=client){ //对象只需要比较地址
                             try {
                                 s.getOutputStream().write((msg + "\n").getBytes());
                             }catch (SocketException e){
                                 //System.out.println(s+"退出了");
                             }
                         }
                     }
                 }
             }
         }catch (Exception e){
         }
     }
 }
 //向服务端写入数据(控制台输入),接收服务端返回的数据
 class Client3{
     public static void main(String[] args) throws IOException {
         Socket client=new Socket("localhost",9999);
         //开启客户端线程  监听服务端消息
         new ClientThread3(client).start();
         //1.控制台输入
         BufferedReader br=new BufferedReader(
           new InputStreamReader(System.in)
         );
         //2.向服务端写
         OutputStream os=client.getOutputStream();
         System.out.println("请输入你的姓名");
         String name=br.readLine();
         while(true){
             System.out.println("请输入内容:esc退出聊天室");
             String msg=br.readLine();
             if (msg != null) {
                 if("esc".equals(msg)){
                     os.write((name+"退出聊天室\n").getBytes());
                     break;
                 }else{
                     os.write((name+"说:"+msg+"\n").getBytes());
                 }
             }
         }
         System.out.println("退出聊天室");
     }
 }
 //3.监听服务端写入的数据(单独写个线程处理)
 class ClientThread3 extends Thread{
     Socket client;
     public ClientThread3(Socket client){
         this.client=client;
     }
     public void run() {
         try {
             BufferedReader br = new BufferedReader(
                     new InputStreamReader(client.getInputStream())
             );
             while (true) {
                 String msg = br.readLine();
                 if (msg != null) System.out.println(msg);
             }
         }catch (Exception e){
         }
     }
 }