网络编程

网络编程

概念

  某个区域的多台计算机在某个介质(光纤、光缆、网线)的牵引下,按照指定的规则(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
原文地址:https://www.cnblogs.com/lyang-a/p/15078494.html