• 网络编程


    网络编程

    概念

      某个区域的多台计算机在某个介质(光纤、光缆、网线)的牵引下,按照指定的规则(HTTP、TCP、FTP、UDP、SMTP)进行数据交换(数据的传输、数据的读写)

    网络通信协议分类:

      HTTP: 超文本(网页中音频、视频、超链接的等非文本元素)传输(HTTP协议必须依赖于TCP或者UDP)协议

      FTP:文件传输协议

      SMTP:简单邮件传输协议


      TCP:传输控制协议

      UDP:数据报协议

      IP :Internet Protocol 网际协议

        客户端和服务器通信服务器的地址

        由32位组成,每8位一组 255.255.255.255

    					      01111111
    

        查看IP地址相关的命令:ipconfig 、ipconfig /all


      IGMP: Internet Group Management Protocol

    网络编程三要素

      协议: 确定客户端和服务器之前数据传输的规则

      IP地址:计算机在网络中的唯一标识,主机和主机进行数据交换,需要知道对方的地址

      端口号:客户端和服务器通行

    使用TCP协议编写程序

      单工:双方建立连接的基础上,服务器向客户端发送响应信息(客户端不需要向服务器发送请求)

    场景:服务器向客户端发送当前时间

    服务器:

    package com.whsxt.day15.socket1;
    
    import java.io.BufferedWriter;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.Date;
    
    /**
     * @author caojie
     * 1.创建服务器端套接字对象
     * 2.接受客户端的连接(服务器确认是哪个客户端发起的请求)
     * 3.一旦服务器接受客户端连接成功,会返回一个客户端套接字
     * 4.打开客户端套接字的输出流对象
     * 5.向客户端发送当前时间
     */
    public class Server {
    	public static void main(String[] args) {
    		try(ServerSocket ss = new ServerSocket(61671)) {
    			Server server = new Server();
    			server.sendMsg2Client(ss);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 服务器发送响应信息给客户端
    	 * 1.接受客户端请求,返回客户端套接字对象
    	 * 2.打开客户端套接字的输出流
    	 * 3.向客户端发送当前时间
    	 * @param ss
    	 * @throws Exception
    	 */
    	public void sendMsg2Client(ServerSocket ss)throws Exception{
    		try(
    				Socket socket =ss.accept();
    				OutputStream out =socket.getOutputStream();
    				BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out));
    				){
    			bw.write("服务器响应信息::::"+new Date());
    			bw.newLine();
    			bw.flush();
    		}
    	}
    }
    
    

    客户端:

    package com.whsxt.day15.socket1;
    
    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.Socket;
    
    /**
     * @author caojie
     * 1 创建客户端套接字Socket(ip,port)向服务器发起连接
     * 2 打开套接字输入流,接受服务器的数据
     * 3 打印数据
     */
    public class Client {
    	public static void main(String[] args) {
    		try {
    			Client client = new Client();
    			client.receiveServerMsg();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 接受服务器信息
    	 * @throws Exception
    	 */
    	public void receiveServerMsg()throws Exception{
    		try(
    				Socket socket = new Socket("192.168.11.168", 61671);
    				InputStream in = socket.getInputStream();
    				BufferedReader br = new BufferedReader(new InputStreamReader(in));
    				){
    			//responseMsg存储服务器发送的相应信息 
    			String responseMsg = br.readLine();
    			System.out.println("客户端收到"+responseMsg);
    		}
    	}
    }
    
    

    半双工

      单线程的基础上服务器和客户端双向通信(一个客户端 一个服务器)

    场景:一个客户端和一个服务器端,客户端向服务器端发送请求(字符串),服务器收到客户端请求之后,判断客户端请求,如果客户端发送的是“J0812”,服务器响应“正确指令”给客户端,如果客户端发送的不是“J0812",服务器响应”错误指令“给客户端。

    package com.whsxt.day15.socket2;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author caojie
     *1 创建服务器端套接字ServerSocket
     *2 接受客户端请求 accept
     *3 创建输入管道和输出管道
     *4 接受客户端请求的数据(输入管道)
     *5 处理客户端请求判断客户端请求:如果客户端发送的是“J0812”,服务器响应“正确指令”给客户端
     *								    如果客户端发送的不是“J0812",服务器响应”错误指令“给客户端
     */
    public class Server {
    	public static void main(String[] args) {
    		try(ServerSocket ss = new ServerSocket(45956);) {
    			Server server = new Server();
    			server.heandlerRequest(ss);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 接受客户端连接,打开客户端套接字管道管道,处理请求,将相应结果发送可客户端
    	 * @param ss
    	 * @throws Exception
    	 */
    	public void heandlerRequest(ServerSocket ss)throws Exception{
    		try(
    				//accept():没有收到客户端连接,服务器会一直阻塞
    				Socket socket = ss.accept();
    				BufferedReader br = new BufferedReader(
    						new InputStreamReader(
    								socket.getInputStream()));
    				BufferedWriter bw = new BufferedWriter(
    						new OutputStreamWriter(
    								socket.getOutputStream()));
    				){
    			// 请求消息:readLine()方法为阻塞式的方法,没有收到客户端的请求信息服务器会一直阻塞
    			String requestMsg =br.readLine();
    			//处理请求:responseMsg 服务器发送给客户端的响应信息
    			String responseMsg = null;
    			if("J0812".equals(requestMsg)) {
    				responseMsg="正确指令";
    			}else {
    				responseMsg="错误指令";
    			}
    			bw.write(responseMsg);
    			bw.newLine();
    			bw.flush();
    		}
    	}
    }
    
    
    package com.whsxt.day15.socket2;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    
    /**
     * @author caojie
     * 1 创建客户端套接字,指定ip地址和端口,向服务器发起连接
     * 2 打开输入和输出管道
     * 3 使用输出管道向服务器发送请求信息
     * 4 使用输入管道接受服务器的响应信息
     */
    public class Client {
    	public static void main(String[] args) {
    		try {
    			Client client = new Client();
    			client.sendRequest();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * @throws Exception
    	 *  * 1 创建客户端套接字,指定ip地址和端口,向服务器发起连接
     * 2 打开输入和输出管道
     * 3 使用输出管道向服务器发送请求信息
     * 4 使用输入管道接受服务器的响应信息
    	 */
    	public void sendRequest()throws Exception{
    		try(
    				Socket socket = new Socket("localhost",45956);
    				BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    				BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    				){
    			//向服务器发送请求数据
    			bw.write("j0812");
    			bw.newLine();
    			bw.flush();
    			//接受服务器响应的数据
    			String responseMsg = br.readLine();
    			System.out.println(responseMsg);
    		}
    	}
    }
    
    

    场景:客户端每隔1秒向服务器发送请求,请求信息和响应信息跟前一个例子一样

    服务器端:

      在循环中监听客户端请求

    package com.whsxt.day15.socket3;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @author caojie
     *场景:客户端每隔1秒向服务器发送请求,请求信息和响应信息跟前一个例子一样
     */
    public class Server {
    
    	public static void main(String[] args) {
    		try (ServerSocket ss = new ServerSocket(43131);){
    			Server server =new Server();
    			server.handlerRequest(ss);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public void handlerRequest(ServerSocket ss)throws Exception{
    		//服务器不断监听客户端请求
    		while(true) {
    			try(
    					//监听请求:没有请求一直阻塞
    					Socket socket =ss.accept();
    					//获取客户端套接字的输入和输出管道
    					BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    					BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    					){
    				//requestMsg接受客户端发送的请求信息
    				String requestMsg =br.readLine();
    				//存储发送给服务器端的响应信息
    				String responseMsg = null;
    				// 条件成立表示正确指令
    				if("J0812".equals(requestMsg)) {
    					responseMsg="正确指令";
    				}else {
    					responseMsg="错误正确指令";
    				}
    				bw.write(responseMsg);
    				bw.newLine();
    				bw.flush();
    			}
    		}
    	}	
    }
    
    

    客户端

    package com.whsxt.day15.socket3;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    public class Client {
    	public static void main(String[] args) {
    		try {
    			Client client = new Client();
    			ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
    			client.sendRequest(ses);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 发送信息到服务器
    	 * @param ses 线程池对象
    	 * @throws Exception
    	 */
    	public void sendRequest(ScheduledExecutorService ses)throws Exception{
    		ses.scheduleWithFixedDelay(new Runnable() {
    			
    			@Override
    			public void run() {
    				try(
    						Socket socket = new Socket("localhost", 43131);
    						//获取客户端套接字的输入和输出管道
    						BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    						BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    						) {
    					//使用客户端套接字的输出管道向服务器发送请求
    					bw.write("J0812");
    					bw.newLine();
    					bw.flush();
    					//使用客户端套接字的输入管道接受服务器响应的结果
    					String responseMsg =br.readLine();
    					System.out.println(responseMsg);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		}, 1000, 1000, TimeUnit.MICROSECONDS);
    	}
    }
    
    

    全双工

      多线程的情况下客户端和服务器双向通信(客户端多个,服务器一个,针对每个客户端的请求服务器都会启动一个线程处理客户端的请求)

    使用Properties读取配置文件数据

    package com.whsxt.day15.socket4;
    
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    /**
     * @author caojie
     * 步骤:1磁盘中的配置文件使用当前类加载器加载到IO管道中
     * 	   2将IO管道数据加载到Properties
     *     3Properties数据put到HashMap
     */
    public class J0812Properties {
    
    	/**
    	 * 存储配置文件的信息
    	 */
    	private static Properties props = new Properties();
    	
    	/**
    	 * 将配置文件信息放入缓存
    	 */
    	private static Map<String,String> socketMap = new HashMap<>();
    	
    	static {
    		try(
    				//将磁盘配置文件加载到IO流中
    				//Thread.currentThread().getContextClassLoader() 当前线程的类加载器,加载配置文件j0812.properties
    				InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("j0812.properties");
    				){
    			//将配置文件的信息加载到Properties
    			props.load(in);
    			//将props里面的数据加载到socketMap中
    			loadData();
    		}catch(Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * 将props里面的数据加载到socketMap中
    	 */
    	private static void loadData() {
    		String ipAdddress = props.getProperty("j0812.socket.ip");
    		String port = props.getProperty("j0812.socket.port");
    		socketMap.put("ipAddress", ipAdddress);
    		socketMap.put("port",port);
    	}
    	
    	/**
    	 * 根据Key获取Value
    	 * @param key
    	 * @return value
    	 */
    	public static String getValueByKey(String key) {
    		return socketMap.get(key);
    	}
    }
    
    

    Properties配置文件

    j0812.socket.ip=localhost
    j0812.socket.port=48092
    

    歌曲缓存

    package com.whsxt.day15.socket4;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author caojie
     * 歌曲缓存:歌曲名作为Key ,歌词作为Value 
     */
    public class SongCache {
    	
    	private static Map<String,String> songCache = new HashMap<>();
    	
    	static {
    		songCache.put("你","你从天而降的你落在我的马背上");
    		songCache.put("给所有知道我名字的人","请你为我再将双手舞动,我会知道你在哪个角落");
    		songCache.put("挪威的森林","心中的枷锁该如何才能解脱");
    	}
    	
    	/**
    	 * 根据歌曲名称返回对应的歌词
    	 * @param key 歌曲名称
    	 * @return 歌词
    	 */
    	public static String getSongWordByKey(String key) {
    		return songCache.get(key);
    	}
    }
    
    
    package com.whsxt.day15.socket4;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author caojie
     *线程池工具类
     */
    public class J0812Utils {
    
    	private static ExecutorService es ;
    	
    	/**
    	 * 创建固定数量的线程池,线程大小为100
    	 * @return 线程池
    	 * @throws Exception
    	 */
    	public static ExecutorService createThreadPool()throws Exception{
    		//条件成立:线程池为空,创建线程池,整个程序运行期间只会创建一次
    		if(null == es) {
    			es = new ThreadPoolExecutor(100, 100,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>());
    		}
    		return  es;
    	}
    	
    }
    
    
    package com.whsxt.day15.socket4;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    
    /**
     * @author caojie
     * 1 创建服务器端套接字
     * 2 接受客户端的连接请求
     * 3 创建客户端套接字的输入和输出管道
     * 4 处理客户端请求
     */
    public class Server {
    
    	public static void main(String[] args) {
    		final int port = Integer.parseInt(J0812Properties.getValueByKey("port"));
    		try(ServerSocket ss = new ServerSocket(port)){
    			ExecutorService es = J0812Utils.createThreadPool();
    			Server server = new Server();
    			server.handlerRequest(ss, es);
    		}catch(Exception e) {
    			System.err.println("服务器处理数据失败");
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	 * 处理客户端请求
    	 * 接受请求
    	 * 创建内部类处理请求
    	 * @param ss 服务器套接字
    	 * @param es 线程池
    	 * @throws Exception 通信异常
    	 */
    	public void handlerRequest(ServerSocket ss, ExecutorService es)throws Exception{
    		//处理客户端请求
    		while(true) {
    			Socket socket = ss.accept();
    			//使用线程池执行(处理客户端请求)
    			es.execute(new SongTask(socket));
    			
    		}
    	}
    	
    	/**
    	 * @author caojie
    	 *  成员内部类:接受客户端请求,处理客户端请求,将响应结果发送给客户端
    	 */
    	class SongTask implements Runnable{
    
    		private Socket socket ;
    		
    		public  SongTask(Socket socket ) {
    			this.socket = socket;
    		}
    		
    		@Override
    		public void run() {
    			try(
    					BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    					BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    					){
    				//接受请求
    				String requestMsg = br.readLine();
    				//处理请求 responseMsg 响应给客户端的信息 
    				String responseMsg = SongCache.getSongWordByKey(requestMsg);
    				//条件成立:没有对应的歌词
    				if(null == responseMsg || responseMsg.length()==0) {
    					responseMsg="对不起,没有对应的歌词";
    				}
    				//将歌词信息写给客户端
    				bw.write(responseMsg);
    				bw.newLine();
    				bw.flush();
    			}catch(Exception e) {
    				System.err.println("服务器处理请求失败......");
    				e.printStackTrace();
    			}
    		}
    	}
    }
    
    
    package com.whsxt.day15.socket4;
    
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.util.Random;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author caojie
     * 1 创建客户端套接字
     * 2 打开客户端套接字输入和输出管道
     * 3 向服务器发送请求
     * 4 接受服务器响应结果
     */
    public class Client {
    	public static void main(String[] args) {
    		ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
    		try {
    			Client client = new Client();
    			client.sendRequest(ses);
    		} catch (Exception e) {
    			System.err.println("客户端发送请求失败");
    			e.printStackTrace();
    		}
    	}
    	
    	/**
    	  * 创建一个Random产生0~2之间的随机数 0发送"你" 1发送“给所有知道我名字的人”2发送"挪威的森林"
    	 * @param ses
    	 * @throws Exception
    	 */
    	public void sendRequest(ScheduledExecutorService ses )throws Exception{
    		Random random = new Random();
    		final String ip = J0812Properties.getValueByKey("ipAddress");
    		final int port = Integer.parseInt(J0812Properties.getValueByKey("port"));
    		ses.scheduleWithFixedDelay(new Runnable() {
    			@Override
    			public void run() {
    				try(
    						Socket socket = new Socket(ip,port);
    						BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    						BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    						){
    					String requestMsg =null;
    					switch (random.nextInt(3)) {
    					case 0:
    						requestMsg="你";
    						break;
    					case 1:
    						requestMsg="给所有知道我名字的人";
    						break;
    					case 2:
    						requestMsg="挪威的森林";
    						break;
    					}
    					//向客户端发送请求
    					bw.write(requestMsg);
    					bw.newLine();
    					bw.flush();
    					//接受服务器的响应结果
    					String responseMsg =br.readLine();
    					System.out.println(responseMsg);
    					
    				}catch (Exception e) {
    					e.printStackTrace();
    				}
    			}
    		}, 1000, 1000, TimeUnit.MILLISECONDS);
    	}
    }
    
    

    Properties

      继承与HashTable,也是使用键值对存储数据

    问题:ServerSocket和Socket他们的ip和端口经常需要变化,不可能一直修改代码

      需要将IP地址和端口号放入到配置文件中,使用Properties来加载配置文件的数据,然后将加载的数据存储到Properties中

      但我需要用到IP和端口的时候就从配置文件中获取

    步骤:

      1、定义工具类使用缓存加载配置文件中的数据

      2、定义配置文件

    package com.whsxt.day15.socket4;
    
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    
    /**
     * @author caojie
     * 步骤:1磁盘中的配置文件使用当前类加载器加载到IO管道中
     * 	   2将IO管道数据加载到Properties
     *     3Properties数据put到HashMap
     */
    public class J0812Properties {
    
    	/**
    	 * 存储配置文件的信息
    	 */
    	private static Properties props = new Properties();
    	
    	/**
    	 * 将配置文件信息放入缓存
    	 */
    	private static Map<String,String> socketMap = new HashMap<>();
    	
    	static {
    		try(
    				//将磁盘配置文件加载到IO流中
    				//Thread.currentThread().getContextClassLoader() 当前线程的类加载器,加载配置文件j0812.properties
    				InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("j0812.properties");
    				){
    			//将配置文件的信息加载到Properties
    			props.load(in);
    			//将props里面的数据加载到socketMap中
    			loadData();
    		}catch(Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * 将props里面的数据加载到socketMap中
    	 */
    	private static void loadData() {
    		String ipAdddress = props.getProperty("j0812.socket.ip");
    		String port = props.getProperty("j0812.socket.port");
    		socketMap.put("ipAddress", ipAdddress);
    		socketMap.put("port",port);
    	}
    
    	/**
    	 * 根据Key获取Value
    	 * @param key
    	 * @return value
    	 */
    	public static String getValueByKey(String key) {
    		return socketMap.get(key);
    	}
    }
    
    j0812.socket.ip=localhost
    j0812.socket.port=48092
    
  • 相关阅读:
    如何学好编程
    进制转换
    第五周学习总结 20201204 于瀛鹏
    xor运算
    20201204 于瀛鹏 第四周学习总结
    20201204 于瀛鹏 第三周学习总结
    IEEE754浮点数
    base64编码
    罗马数字(1-3999)转阿拉伯数字
    俄罗斯方块
  • 原文地址:https://www.cnblogs.com/lyang-a/p/15078494.html
Copyright © 2020-2023  润新知