网络编程
网络编程
概念
某个区域的多台计算机在某个介质(光纤、光缆、网线)的牵引下,按照指定的规则(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