• Socket网络编程


    Socket

    1 环境查看

    1. 通过cmd窗口的命令:ipconfig查看本机IP地址
      查看IP地址
    2. 查看网络情况是否正常:ping百度官网
      查看网络情况

    用来进行本地测试的地址 127.0.0.1,回环测试地址,默认代表的就是本机的IP

    2 Socket概述

    socket编程也叫套接字编程,应用程序可以通过它发送或者接受数据,可对其像打开文件一样打开/关闭/读写等操作.
    套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.
    网络套接字是IP地址与端口号TCP协议的组合
    Socket就是为网络编程提供的一种机制,通信的两端都有Socket
    网络通信其实就是Socket之间的通信,数据在两个Socket之间通过I/O进行传输.
    
    • 1
    • 2
    • 3
    • 4
    • 5

    通信模式
    TCP网络通信模型

    3 服务器端

    3.1 ServerSocket

    在服务器端选择一个端口号,在指定端口上等待客户端发起连接

    构造方法:
    ServerSocket(int port) 创建绑定到特定端口的服务器套接字
    常用方法:
    Socket accept() 侦听并接收到此套接字的连接
    void close() 关闭此套接字
    启动服务:
    ServerSocket ss = new ServerSocket(端口号);
    等待客户端发起连接,连接后会建立起通道:Socket socket = ss.accept();

    4 客户端Socket

    我们经常使用的就是客户端

    构造方法:
    Socket(String host,int port) 创建一个流套接字,并将其连接到指定主机上的指定端口号
    常用方法:
    InputStream getInputStream() 返回此套接字的输入流
    OutputStream getOutputStream() 返回此套接字的输出流
    void close() 关闭此套接字
    新建Socket对象,连接指定IP指定的端口
    Socket s = new Socket(IP,port);
    从Socket获取双向的流:
    InputStream in = s.getInputStream();
    OutputStream out = s.getOutputStream();

    5 入门案例

    5.1服务器端代码编写

    需求: 服务器端接收客户端发来的数据”hello”,并且给客户端响应数据”world”

    创建包: cn.tedu.net
    创建类: Server.java

    package cn.tedu.net;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**本类用来表示Socket网络编程案例的服务器端
     * 完成步骤分析:
     * 1.启动服务器
     * 2.接收客户端的连接请求
     * 3.接收客户端发来的数据
     * */
    /**测试注意事项:先启动服务器端,再启动客户端*/
    public class Server {
    	public static void main(String[] args) throws Exception {
    		//1.启动服务器,指定端口号为8888,等待客户端的连接
    		/**注意:
    		 * 1.使用ServerSocket需要导包java.net.ServerSocket
    		 * 2.此操作会抛出异常
    		 * 3.指定的端口号范围是:0-655535,而0-1024是系统端口号,不能指定
    		 * */
    		ServerSocket ss = new ServerSocket(8888);
    		//2.接收客户端的连接请求,并建立数据通信通道
    		Socket socket = ss.accept();
    		//3.获取到读取流,接收并读取客户端发来的数据
    		InputStream in = socket.getInputStream();
    		//通过循环挨个读取显示读到的内容
    		for(int i = 0;i < 5;i++) {
    			//int b = in.read();//此方法读取的结果是把字符转成数字
    			char c = (char) in.read();//为了直接显示读取到的字符,需要强制类型转换(大转小,int转char)
    			System.out.print(c);//print()同行输出,注意细节哦
    		}
    		//5.给客户端发送数据
    		OutputStream out = socket.getOutputStream();
    		out.write("world".getBytes());
    		out.flush();
    		
    		//4.释放资源
    		/**注意关流的顺序,后出现的先关闭*/
    		in.close();
    		ss.close();
    	}
    }
    
    • 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

    5.2 客户端代码编写

    创建包: cn.tedu.net
    创建类: Client.java

    package cn.tedu.net;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /**本类用来表示Socket网络编程案例的客户端
     * 完成步骤分析:
     * 1.指定要连接的服务器
     * 2.给服务器发送hello
     * */
    public class Client {
    	public static void main(String[] args) throws Exception {
    		//1.指定要连接的服务器,需要同时指定服务器的IP & Port
    		/**注意:
    		 * 1.使用Socket需要导包java.net.Socket
    		 * 2.此操作会抛出异常
    		 * 3.如果使用的是本机的IP,地址是固定值,用来测试时使用127.0.0.1
    		 * */
    		Socket socket = new Socket("127.0.0.1",8888);
    		//2.给服务器端发送hello
    		OutputStream out = socket.getOutputStream();
    		//把要输出的数据hello字符串转变成byte[]的形式进行输出
    		out.write("hello".getBytes());
    		out.flush();
    		
    		//4.读取从服务器端返回的数据
    		InputStream in = socket.getInputStream();
    		for (int i = 0; i < 5; i++) {
    			char c = (char) in.read();//为了显示字符而不是数字,强制类型转换成char
    			System.out.print(c);//不换行展示获取到的数据
    		}
    		
    		//3.释放资源
    		//out.close();
    		socket.close();
    		
    	}
    }
    
    • 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

    5.3 测试注意事项:

    1.测试顺序:先启动服务器端,再启动客户端
    2.可以打开多个控制台显示窗口,方便查看
    3.可以将其中一个控制台显示窗口固定,否则多个窗口的显示内容是一样的
    4.注意每次测试完毕后,都需要将之前的控制台窗口都关闭后再次进行新一轮的测试
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    注意:要先启动服务器端再启动客户端,否则会报错,如下所示:

    在这里插入图片描述

    注意,下次测试的时候,把上次测试的窗口都关掉,重复启动多次,正在使用,新次测试就无法使用

    在这里插入图片描述

    6 服务器端线程模型

    入门案例中,存在两种阻塞状态:

    1. 服务器端的accept()一直在等待客户端的连接
    2. 通信中的read()死等数据
      在这里插入图片描述

    7 读一行写一行案例

    创建包: cn.tedu.net
    创建类: TestIO.java

    package cn.tedu.net;
    
    import java.io.BufferedReader;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    /**这个类用来测试一行一行读取,一行一行写出*/
    public class TestIO {
    	public static void main(String[] args) {
    		//method();//初步测试readLine()
    		method2();//改进
    	}
    	public static void method2() {
    		BufferedReader in = null;
    		PrintWriter out = null;
    		try {
    			//1.读取一行数据,先拿到读取数据的流,注意文件需要自己在win创建
    			in = new BufferedReader(new FileReader("D:\b.txt"));
    			//2.定义变量,记录每行读取到的数据
    			String line;
    			//3.设置循环读取数据,只要有数据就一直读取
    			while( (line=in.readLine()) != null) {
    				System.out.println(line);
    			}
    			
    			//4.一行行写出数据:PrintWriter
    			out = new PrintWriter(new FileWriter("D:\b.txt"));
    			out.println("java");
    			out.println("hello");
    			out.flush();//为了防止有数据没有发送过去,可以刷一下
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally {
    			try {
    				//5.释放资源
    				out.close();
    				in.close();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	public static void method() {
    		try {
    			//1.读取一行数据,先拿到读取数据的流,注意文件需要自己在win创建
    			BufferedReader in = new BufferedReader(new FileReader("D:\b.txt"));
    			//2.调用readLine方法进行测试
    			String line = in.readLine();
    			String line2 = in.readLine();
    			String line3 = in.readLine();
    			//测试1:b.txt没有数据时,readLine()返回null
    			//测试2:b.txt有数据,而且是1整行数据,readLine()可以返回整行全部数据
    			//测试3:b.txt有数据,而且是多行数据,readLine()只可以返回第1行全部数据
    			/**原因:readLine()在读取数据时,会读取特殊标记,换行符
    ,读到换行符就结束
    			 * 结论:readLine()只可以读取1整行数据,如果是多行数据,需要多次调用
    			 * */
    			//3.打印测试结果
    			System.out.println(line);
    			System.out.println(line2);
    			System.out.println(line3);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    • 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
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    8 拓展 : 回声案例

    需求:接收客户端输入的每行数据,服务器再把回应给到用户

    8.1 服务器端

    接收客户端输入的每行数据,服务器再把回应给到用户

    创建包: cn.tedu.net
    创建类: Server2.java

    package cn.tedu.net;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**这个类用来当做回声案例的服务器端
     * 1.启动服务器
     * 2.接收客户端的连接请求
     * 3.给每个用户分配对应的话务员
     * 4.话务员:主要负责和对应的客户端对象进行沟通I/O
     * */
    /**1.创建优化后的Server2类,充当服务器端*/
    public class Server2 {
    	/**2.创建方法,负责服务多个客户*/
    	public void service() {
    		/**3.匿名对象+匿名内部类(重写run方法)*/
    		new Thread() {
    			/**5.把业务写在run()中*/
    			@Override
    			public void run() {
    				try {
    					//5.1 启动服务器,设置端口号为8000并等待客户端连接
    					ServerSocket ss = new ServerSocket(8000);
    					System.out.println("服务器启动成功!");
    					while(true) {//死循环,一直accept,也就是接受客户端的连接请求
    						//5.2 一直接受所有客户端的连接请求
    						Socket socket = ss.accept();
    						System.out.println("客户端连接成功!");
    						//5.3 给每个客户分配自己对应的话务员,1v1
    						HuaWuThread t = new HuaWuThread(socket);
    						t.start();
    					}
    				} catch (IOException e) {
    					e.printStackTrace();
    				}
    				
    			};
    		}.start();/**4.启动线程*/
    	}
    	
    	/**6.创建话务员内部类,主要负责和客户端沟通 I/O*/
    	class HuaWuThread extends Thread{
    		//6.1 定义本类中的成员变量socket,用来保持通话
    		Socket socket;
    		//6.2含参构造,接受当前的连接信息,保持通话,为谁服务就保存谁的数据
    		public HuaWuThread(Socket socket) {
    			this.socket = socket;
    		}
    		//6.3把话务员的业务放在run(),一直读取客户端发来的数据,并作出回应
    		@Override
    		public void run() {
    			try {
    				//7.1读取一行BufferedReader,并且写出一行PrintWriter --双向流
    				BufferedReader in = new BufferedReader(
    						new InputStreamReader(socket.getInputStream()));
    				PrintWriter out = new PrintWriter(
    						new OutputStreamWriter(socket.getOutputStream()));
    				//7.2读取客户端发来的一行数据
    				String line;//定义变量,记录读取到的一行数据
    				while((line = in.readLine()) != null) {//只要有数据就一直读
    					System.out.println("客户端发来的数据:"+line);
    					//7.1可以给客户端作出响应-接收键盘输入的响应
    					System.out.println("请输入您的回应:");
    					String input = new Scanner(System.in).nextLine();
    					//7.2发出作为服务器的响应
    					out.println(input);
    					out.flush();//把数据刷出去
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	public static void main(String[] args) {
    		Server2 s = new Server2();
    		s.service();
    	}
    }
    
    • 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
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    8.2 客户端

    接收客户端输入的每行数据,服务器再把回应给到用户

    创建包: cn.tedu.net
    创建类:Client2.java

    package cn.tedu.net;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Scanner;
    
    /**本类用来做回声案例的客户端
     * 1.连接指定的服务器
     * 2.给服务器发送数据
     * 3.接收服务器响应的数据
     * */
    public class Client2 {
    	public static void main(String[] args) {
    		try {
    			//1.连接指定的服务器,同时指定服务器的IP和端口号
    			Socket socket = new Socket("127.0.0.1",8000);
    			//2.给服务器发送数据
    			while(true) {
    				//向服务器写出一行数据,并且读取服务器响应回来的数据
    				PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
    				BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    				//3.通过流对象进行发送和读取的操作
    				System.out.println("请输入您想给服务器发送的数据:");
    				String input = new Scanner(System.in).nextLine();
    				out.println(input);//向服务端发送指定数据
    				out.flush();//把数据刷出去
    				String line = in.readLine();//读取回声数据
    				System.out.println("服务器端响应的数据是:"+line);
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    • 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
  • 相关阅读:
    关于Elasticsearch(es)中使用sql返回结果只有1000条
    vue + axios + mock
    机器学习基础算法--回归类型和评价分析
    【IPC 通信】信号处理接口 Signal API(5)
    【数据库】Redis基础知识全解
    MyBatis中的ResultMap有什么作用
    盒子模型详解
    Java -- 每日一问:谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
    LeetCode每日一题:面试题 01.08. 零矩阵(中等)
    HashMap、LinkedHashMap、ConcurrentHashMap、ArrayList、LinkedList的底层实现。
  • 原文地址:https://blog.csdn.net/m0_67390969/article/details/126496542