第一节 网络编程
1、什么是网络编程
网络编程可以让程序与网络上的其他设备中的程序进行数据交互
2、网络通信基本模式
常见的通信模式有如下2中形式:Client-Server(CS)、Browser/Server(BS)
第二节 网络通信三要素
1、说说网络通信至少需要几个要素
IP地址:设备在网络中的地址,是唯一的标识
端口:应用程序在设备中唯一的标识
协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议
2、IP地址是做什么的,具体有几种
定位网络上的设备,有IPv4,IPv6
================================================================================================
===================================================================================
3、如何查看本机IP地址,如何看是否与对方互通
ipconfig
ping 192.168.10.23
4、本机IP是谁
127.0.0.1或者是localhost
5、IP地址的代表类是谁
InetAddress类
6、如何获取本机IP对象
public static InetAddress getLocalhost()
7、如何判断与该IP地址对象是否互通
public boolean isReachable(int timeout)
1 package com.itheima.d1_inetAddress; 2 import java.net.InetAddress; 3 /** 4 目标:InetAddress类概述(了解) 5 一个该类的对象就代表一个IP地址对象。 6 7 InetAddress类成员方法: 8 static InetAddress getLocalHost() 9 * 获得本地主机IP地址对象。 10 static InetAddress getByName(String host) 11 * 根据IP地址字符串或主机名获得对应的IP地址对象。 12 String getHostName() 13 * 获得主机名。 14 String getHostAddress() 15 * 获得IP地址字符串。 16 */ 17 public class InetAddressDemo01 { 18 public static void main(String[] args) throws Exception { 19 // 1.获取本机地址对象。 20 InetAddress ip1 = InetAddress.getLocalHost(); 21 System.out.println(ip1.getHostName()); 22 System.out.println(ip1.getHostAddress()); 23 24 // 2.获取域名ip对象 25 InetAddress ip2 = InetAddress.getByName("www.baidu.com"); 26 System.out.println(ip2.getHostName()); 27 System.out.println(ip2.getHostAddress()); 28 29 // 3.获取公网IP对象。 30 InetAddress ip3 = InetAddress.getByName("112.80.248.76"); 31 System.out.println(ip3.getHostName()); 32 System.out.println(ip3.getHostAddress()); 33 34 // 4.判断是否能通: ping 5s之前测试是否可通 35 System.out.println(ip3.isReachable(5000)); 36 } 37 }InetAddressDemo01
8、端口号的作用是什么?
唯一标识正在计算机设备上运行的进程(程序)
9、一个设备中,能否出现2个应用程序的端口号一样,为什么
不可以,如果一样会出现端口冲突错误
注意:一个程序在不同电脑上的端口应一致,不然会出错,就比如QQ的端口号是8888,那QQ在别人电脑上也应该是8888
10、通信协议是什么
计算机网络中,连接和通信数据的规则被称为网络通信协议
===================================================================================================
11、TCP通信协议的特点是什么样的
它是一种面向连接的可靠通信协议
传输前,采用“三次握手”方式建议连接,点对点的通信,所以可靠
传输后,采用“四次挥手”方式解除连接
在连接中可进行大数据量的传输
通信效率较低
=============================================================================================
12、UDP协议的特点是什么
用户数据报协议(User Datagram Protocol)
UDP是面向无连接,不可靠传输的通信协议
速度快,有大小限制一次最多发送63K,数据不安全,易丢失数据
第三节 UDP通信
0、UDP协议的特点
UDP是一种无连接、不可靠传输的协议
将数据源IP、目的地IP和端口以及数据封装成数据包,大小限制在64KB内,直接发送出去即可
1、UDP发送端和接收端的对象是哪个
public DatagramSocket():创建发送端的Socket对象 默认会有端口号
public DatagramSocket(int port):创建接收端的Socket对象
2、数据包对象是哪个
DatagramPacket
3、如何发送、接收数据包
使用DatagramSocket的如下方法:
public void send(DatagramPacket dp):发送数据包
publis void receive(DatagramPacket dp):接收数据包
==============================================================================================
==================================================================================
1 package com.itheima.d2_udp1; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 import java.net.InetAddress; 6 import java.net.SocketException; 7 8 /** 9 发送端 一发 一收 10 */ 11 public class ClientDemo1 { 12 public static void main(String[] args) throws Exception { 13 System.out.println("=====客户端启动======"); 14 15 // 1、创建发送端对象:发送端自带默认的端口号(人) 16 DatagramSocket socket = new DatagramSocket(6666); 17 18 // 2、创建一个数据包对象封装数据(韭菜盘子) 19 /** 20 public DatagramPacket(byte buf[], int length, 21 InetAddress address, int port) 22 参数一:封装要发送的数据(韭菜) 23 参数二:发送数据的大小 24 参数三:服务端的主机IP地址 25 参数四:服务端的端口 26 */ 27 byte[] buffer = "我是一颗快乐的韭菜,你愿意吃吗?".getBytes(); 28 DatagramPacket packet = new DatagramPacket( buffer, buffer.length, 29 InetAddress.getLocalHost() , 8888); 30 31 // 3、发送数据出去 32 socket.send(packet); 33 34 socket.close(); 35 } 36 }ClientDemo
1 package com.itheima.d2_udp1; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 import java.net.SocketException; 6 7 /** 8 接收端 9 */ 10 public class ServerDemo2 { 11 public static void main(String[] args) throws Exception { 12 System.out.println("=====服务端启动======"); 13 // 1、创建接收端对象:注册端口(人) 14 DatagramSocket socket = new DatagramSocket(8888); 15 16 // 2、创建一个数据包对象接收数据(韭菜盘子) 17 byte[] buffer = new byte[1024 * 64]; 18 DatagramPacket packet = new DatagramPacket(buffer, buffer.length); 19 20 // 3、等待接收数据。 21 socket.receive(packet); 22 23 // 4、取出数据即可 24 // 读取多少倒出多少 25 int len = packet.getLength(); 26 String rs = new String(buffer,0, len); 27 System.out.println("收到了:" + rs); 28 29 // 获取发送端的ip和端口 30 String ip =packet.getSocketAddress().toString(); 31 System.out.println("对方地址:" + ip); 32 33 int port = packet.getPort(); 34 System.out.println("对方端口:" + port); 35 36 socket.close(); 37 } 38 }ServerDemo
4、UDP通信:多发多收:UDP的接收端为什么可以接收很多发送端的消息?
接收端只负责接收数据包,无所谓是哪个发送端的数据包
==========================================================================================
=========================================================================================
1 package com.itheima.d3_udp2; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 import java.net.InetAddress; 6 import java.net.MulticastSocket; 7 import java.util.Scanner; 8 9 /** 10 发送端 多发 多收 11 */ 12 public class ClientDemo1 { 13 public static void main(String[] args) throws Exception { 14 System.out.println("=====客户端启动======"); 15 16 // 1、创建发送端对象:发送端自带默认的端口号(人) 17 DatagramSocket socket = new DatagramSocket(7777); 18 19 20 Scanner sc = new Scanner(System.in); 21 while (true) { 22 System.out.println("请说:"); 23 String msg = sc.nextLine(); 24 25 if("exit".equals(msg)){ 26 System.out.println("离线成功!"); 27 socket.close(); 28 break; 29 } 30 31 // 2、创建一个数据包对象封装数据(韭菜盘子) 32 byte[] buffer = msg.getBytes(); 33 DatagramPacket packet = new DatagramPacket( buffer, buffer.length, 34 InetAddress.getLocalHost() , 8888); 35 36 // 3、发送数据出去 37 socket.send(packet); 38 } 39 40 } 41 }ClientDemo
1 package com.itheima.d3_udp2; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 6 /** 7 接收端 8 */ 9 public class ServerDemo2 { 10 public static void main(String[] args) throws Exception { 11 System.out.println("=====服务端启动======"); 12 // 1、创建接收端对象:注册端口(人) 13 DatagramSocket socket = new DatagramSocket(8888); 14 15 // 2、创建一个数据包对象接收数据(韭菜盘子) 16 byte[] buffer = new byte[1024 * 64]; 17 DatagramPacket packet = new DatagramPacket(buffer, buffer.length); 18 19 while (true) { 20 // 3、等待接收数据。 21 socket.receive(packet); 22 // 4、取出数据即可 23 // 读取多少倒出多少 24 int len = packet.getLength(); 25 String rs = new String(buffer,0, len); 26 System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs); 27 } 28 } 29 }ServerDemo
5、如何实现广播,具体怎么操作
发送端目的IP使用广播IP:255.255.255.255 9999
所在网段的其他主机对应了端口(9999)即可接收信息
6、如何实现组播,具体怎么操作
发送端目的IP使用组播IP,且指定端口
所在网段的其他主机注册了该组播IP和对应端口即可接收信息
1 package com.itheima.d4_upd3; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 import java.net.InetAddress; 6 import java.util.Scanner; 7 8 /** 9 发送端 多发 多收 10 */ 11 public class ClientDemo1 { 12 public static void main(String[] args) throws Exception { 13 System.out.println("=====客户端启动======"); 14 15 // 1、创建发送端对象:发送端自带默认的端口号(人) 16 DatagramSocket socket = new DatagramSocket(); 17 18 19 Scanner sc = new Scanner(System.in); 20 while (true) { 21 System.out.println("请说:"); 22 String msg = sc.nextLine(); 23 24 if("exit".equals(msg)){ 25 System.out.println("离线成功!"); 26 socket.close(); 27 break; 28 } 29 30 // 2、创建一个数据包对象封装数据(韭菜盘子) 31 byte[] buffer = msg.getBytes(); 32 // 注意:只要目的地IP是 255.255.255.255 这个消息将以广播的形式对外发送 33 // DatagramPacket packet = new DatagramPacket( buffer, buffer.length, 34 // InetAddress.getByName("255.255.255.255") , 8888); 35 36 DatagramPacket packet = new DatagramPacket( buffer, buffer.length, 37 InetAddress.getByName("224.0.1.1") , 9898); 38 39 // 3、发送数据出去 40 socket.send(packet); 41 } 42 43 } 44 }ClientDemo
1 package com.itheima.d4_upd3; 2 3 import java.net.DatagramPacket; 4 import java.net.DatagramSocket; 5 6 /** 7 接收端 8 */ 9 public class ServerDemo2 { 10 public static void main(String[] args) throws Exception { 11 System.out.println("=====服务端启动======"); 12 // 1、创建接收端对象:注册端口(人) 13 DatagramSocket socket = new DatagramSocket(8888); 14 15 // 2、创建一个数据包对象接收数据(韭菜盘子) 16 byte[] buffer = new byte[1024 * 64]; 17 DatagramPacket packet = new DatagramPacket(buffer, buffer.length); 18 19 while (true) { 20 // 3、等待接收数据。 21 socket.receive(packet); 22 // 4、取出数据即可 23 // 读取多少倒出多少 24 int len = packet.getLength(); 25 String rs = new String(buffer,0, len); 26 System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs); 27 } 28 } 29 }ServerDemo1
1 package com.itheima.d4_upd3; 2 3 import java.net.*; 4 5 /** 6 接收端 7 */ 8 public class ServerDemo3 { 9 public static void main(String[] args) throws Exception { 10 System.out.println("=====服务端启动======"); 11 // 1、创建接收端对象:注册端口(人) 12 MulticastSocket socket = new MulticastSocket(9898); 13 14 // 注意:绑定组播地址(加群) 15 socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1") , 9898), 16 NetworkInterface.getByInetAddress(InetAddress.getLocalHost())); 17 18 // 2、创建一个数据包对象接收数据(韭菜盘子) 19 byte[] buffer = new byte[1024 * 64]; 20 DatagramPacket packet = new DatagramPacket(buffer, buffer.length); 21 22 23 while (true) { 24 // 3、等待接收数据。 25 socket.receive(packet); 26 // 4、取出数据即可 27 // 读取多少倒出多少 28 int len = packet.getLength(); 29 String rs = new String(buffer,0, len); 30 System.out.println("收到了来自:" + packet.getAddress() +", 对方端口是" + packet.getPort() +"的消息:" + rs); 31 } 32 } 33 }ServerDemo2
第四节 TCP通信
1、TCP通信快速入门
=========================================================================
A、TCP通信的客户端的代表类是谁?
Socket类
public Socket(String host,int port)
B、TCP通信如何使用Socket管道发送、接收数据
OutputStream getOutputStream():获得字节输出流对象(发)
InputStream getInputStream():获得字节输入流对象(收)
1 package com.itheima.d5_socket1; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.io.PrintStream; 6 import java.net.Socket; 7 8 /** 9 目标:完成Socket网络编程入门案例的客户端开发,实现1发1收。 10 */ 11 public class ClientDemo1 { 12 public static void main(String[] args) { 13 try { 14 System.out.println("====客户端启动==="); 15 // 1、创建Socket通信管道请求有服务端的连接 16 // public Socket(String host, int port) 17 // 参数一:服务端的IP地址 18 // 参数二:服务端的端口 19 Socket socket = new Socket("127.0.0.1", 7777); 20 21 // 2、从socket通信管道中得到一个字节输出流 负责发送数据 22 OutputStream os = socket.getOutputStream(); 23 24 // 3、把低级的字节流包装成打印流 25 PrintStream ps = new PrintStream(os); 26 27 // 4、发送消息 28 ps.println("我是TCP的客户端,我已经与你对接,并发出邀请:约吗?"); 29 ps.flush(); 30 31 // 关闭资源。 32 // socket.close(); 33 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 } 38 }ClientDemo
C、TCP通信服务端用的代表类?
ServerSocket类,注册端口
调用accept()方法阻塞等待接收客户端连接。得到Socket对象
D、TCP通信的基本原理
客户端怎么发,服务端就应该怎么收
客户端如果没有消息,服务端会进入阻塞等待
Socket一方关闭或者出现异常、对方Socket也会失效或者出错
=================================================================================================
1 package com.itheima.d5_socket1; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.net.ServerSocket; 8 import java.net.Socket; 9 10 /** 11 目标:开发Socket网络编程入门代码的服务端,实现接收消息 12 */ 13 public class ServerDemo2 { 14 public static void main(String[] args) { 15 try { 16 System.out.println("===服务端启动成功==="); 17 // 1、注册端口 18 ServerSocket serverSocket = new ServerSocket(7777); 19 // 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道 20 Socket socket = serverSocket.accept(); 21 // 3、从socket通信管道中得到一个字节输入流 22 InputStream is = socket.getInputStream(); 23 // 4、把字节输入流包装成缓冲字符输入流进行消息的接收 24 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 25 // 5、按照行读取消息 26 String msg; 27 if ((msg = br.readLine()) != null){ 28 System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg); 29 } 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } 33 } 34 }ServerDemo
2、TCP通信:多发多收消息
A、本次多发多收是如何实现的
客户端使用循环反复地发送消息
服务端使用循环反复地接收信息
B、现在服务端为什么不可以同时接收多个客户端的消息
目前服务端是单线程的,每次只能处理一个客户端的消息
1 package com.itheima.d6_socket2; 2 3 import java.io.OutputStream; 4 import java.io.PrintStream; 5 import java.net.Socket; 6 import java.util.Scanner; 7 8 /** 9 目标:实现多发和多收 10 */ 11 public class ClientDemo1 { 12 public static void main(String[] args) { 13 try { 14 System.out.println("====客户端启动==="); 15 // 1、创建Socket通信管道请求有服务端的连接 16 // public Socket(String host, int port) 17 // 参数一:服务端的IP地址 18 // 参数二:服务端的端口 19 Socket socket = new Socket("127.0.0.1", 7777); 20 21 // 2、从socket通信管道中得到一个字节输出流 负责发送数据 22 OutputStream os = socket.getOutputStream(); 23 24 // 3、把低级的字节流包装成打印流 25 PrintStream ps = new PrintStream(os); 26 27 Scanner sc = new Scanner(System.in); 28 while (true) { 29 System.out.println("请说:"); 30 String msg = sc.nextLine(); 31 // 4、发送消息 32 ps.println(msg); 33 ps.flush(); 34 } 35 36 // 关闭资源。 37 // socket.close(); 38 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } 42 } 43 }ClientDemo
1 package com.itheima.d6_socket2; 2 3 import java.io.BufferedReader; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 /** 10 目标:开发Socket网络编程入门代码的服务端,实现接收消息 11 */ 12 public class ServerDemo2 { 13 public static void main(String[] args) { 14 try { 15 System.out.println("===服务端启动成功==="); 16 // 1、注册端口 17 ServerSocket serverSocket = new ServerSocket(7777); 18 while (true) { 19 // 2、必须调用accept方法:等待接收客户端的Socket连接请求,建立Socket通信管道 20 Socket socket = serverSocket.accept(); 21 // 3、从socket通信管道中得到一个字节输入流 22 InputStream is = socket.getInputStream(); 23 // 4、把字节输入流包装成缓冲字符输入流进行消息的接收 24 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 25 // 5、按照行读取消息 26 String msg; 27 while ((msg = br.readLine()) != null){ 28 System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg); 29 } 30 } 31 } catch (Exception e) { 32 e.printStackTrace(); 33 } 34 } 35 }ServerDemo
3、TCP通信:同时接收多个客户端消息
A、本次是如何实现服务端接收多个客户端的消息的
主线程定义了循环负责接收客户端Socket管道连接
每接收到一个Socket通信管道后分配一个独立的线程负责处理它
1 package com.itheima.d7_socket3; 2 3 import java.io.OutputStream; 4 import java.io.PrintStream; 5 import java.net.Socket; 6 import java.util.Scanner; 7 8 /** 9 目标:实现服务端可以同时处理多个客户端的消息。 10 */ 11 public class ClientDemo1 { 12 public static void main(String[] args) { 13 try { 14 System.out.println("====客户端启动==="); 15 // 1、创建Socket通信管道请求有服务端的连接 16 // public Socket(String host, int port) 17 // 参数一:服务端的IP地址 18 // 参数二:服务端的端口 19 Socket socket = new Socket("127.0.0.1", 7777); 20 21 // 2、从socket通信管道中得到一个字节输出流 负责发送数据 22 OutputStream os = socket.getOutputStream(); 23 24 // 3、把低级的字节流包装成打印流 25 PrintStream ps = new PrintStream(os); 26 27 Scanner sc = new Scanner(System.in); 28 while (true) { 29 System.out.println("请说:"); 30 String msg = sc.nextLine(); 31 // 4、发送消息 32 ps.println(msg); 33 ps.flush(); 34 } 35 36 // 关闭资源。 37 // socket.close(); 38 39 } catch (Exception e) { 40 e.printStackTrace(); 41 } 42 } 43 }Client
1 package com.itheima.d7_socket3; 2 3 import java.io.BufferedReader; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.net.ServerSocket; 7 import java.net.Socket; 8 9 /** 10 目标:实现服务端可以同时处理多个客户端的消息。 11 */ 12 public class ServerDemo2 { 13 public static void main(String[] args) { 14 try { 15 System.out.println("===服务端启动成功==="); 16 // 1、注册端口 17 ServerSocket serverSocket = new ServerSocket(7777); 18 // a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。 19 while (true) { 20 // 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息 21 Socket socket = serverSocket.accept(); 22 System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!"); 23 // 3、开始创建独立线程处理socket 24 new ServerReaderThread(socket).start(); 25 } 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 } 30 }ServerDemo
1 package com.itheima.d7_socket3; 2 3 import java.io.BufferedReader; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.net.Socket; 7 8 public class ServerReaderThread extends Thread{ 9 private Socket socket; 10 public ServerReaderThread(Socket socket){ 11 this.socket = socket; 12 } 13 @Override 14 public void run() { 15 try { 16 // 3、从socket通信管道中得到一个字节输入流 17 InputStream is = socket.getInputStream(); 18 // 4、把字节输入流包装成缓冲字符输入流进行消息的接收 19 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 20 // 5、按照行读取消息 21 String msg; 22 while ((msg = br.readLine()) != null){ 23 System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg); 24 } 25 } catch (Exception e) { 26 System.out.println(socket.getRemoteSocketAddress() + "下线了!!!"); 27 } 28 } 29 }ServerReaderThread
4、TCP通信:使用线程池优化
A、上面多收多发的通信架构存在什么问题?
客户端与服务端的线程模型是N-N的关系
客户端并发越多,系统瘫痪的越快
B、本次使用线程池的优势在哪里?
服务端可以复用线程处理多个客户端,可以避免系统瘫痪
合适客户端通信时长较短的场景
1 package com.itheima.d8_socket4; 2 3 import java.io.OutputStream; 4 import java.io.PrintStream; 5 import java.net.Socket; 6 import java.util.Scanner; 7 8 /** 9 拓展:使用线程池优化:实现通信。 10 */ 11 public class ClientDemo1 { 12 public static void main(String[] args) { 13 try { 14 System.out.println("====客户端启动==="); 15 // 1、创建Socket通信管道请求有服务端的连接 16 // public Socket(String host, int port) 17 // 参数一:服务端的IP地址 18 // 参数二:服务端的端口 19 Socket socket = new Socket("127.0.0.1", 6666); 20 21 // 2、从socket通信管道中得到一个字节输出流 负责发送数据 22 OutputStream os = socket.getOutputStream(); 23 24 // 3、把低级的字节流包装成打印流 25 PrintStream ps = new PrintStream(os); 26 27 Scanner sc = new Scanner(System.in); 28 while (true) { 29 System.out.println("请说:"); 30 String msg = sc.nextLine(); 31 // 4、发送消息 32 ps.println(msg); 33 ps.flush(); 34 } 35 // 关闭资源。 36 // socket.close(); 37 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 } 42 }Client
1 package com.itheima.d8_socket4; 2 import com.itheima.d7_socket3.ServerReaderThread; 3 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 import java.util.concurrent.*; 7 8 /** 9 目标:实现服务端可以同时处理多个客户端的消息。 10 */ 11 public class ServerDemo2 { 12 13 // 使用静态变量记住一个线程池对象 14 private static ExecutorService pool = new ThreadPoolExecutor(300, 15 1500, 6, TimeUnit.SECONDS, 16 new ArrayBlockingQueue<>(2) 17 , Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); 18 19 public static void main(String[] args) { 20 try { 21 System.out.println("===服务端启动成功==="); 22 // 1、注册端口 23 ServerSocket serverSocket = new ServerSocket(6666); 24 // a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。 25 while (true) { 26 // 2、每接收到一个客户端的Socket管道, 27 Socket socket = serverSocket.accept(); 28 System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!"); 29 30 // 任务对象负责读取消息。 31 Runnable target = new ServerReaderRunnable(socket); 32 pool.execute(target); 33 } 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 } 38 }Server
1 package com.itheima.d8_socket4; 2 3 import java.io.BufferedReader; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.net.Socket; 7 8 public class ServerReaderRunnable implements Runnable{ 9 private Socket socket; 10 public ServerReaderRunnable(Socket socket){ 11 this.socket = socket; 12 } 13 @Override 14 public void run() { 15 try { 16 // 3、从socket通信管道中得到一个字节输入流 17 InputStream is = socket.getInputStream(); 18 // 4、把字节输入流包装成缓冲字符输入流进行消息的接收 19 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 20 // 5、按照行读取消息 21 String msg; 22 while ((msg = br.readLine()) != null){ 23 System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg); 24 } 25 } catch (Exception e) { 26 System.out.println(socket.getRemoteSocketAddress() + "下线了!!!"); 27 } 28 } 29 }ServerReaderRunnable
5、TCP通信实战案例:即时通信
A、即时通信是什么含义,要实现怎么样的设计?
即时通信,是指一个客户端的消息发出去,其他客户端可以接收到
即使通信需要进行端口转发的设计思想
服务端需要把在线的Socket管道存储起来
一旦收到一个消息要推送给其他管道
1 package com.itheima.d9_chat; 2 3 import java.io.OutputStream; 4 import java.io.PrintStream; 5 import java.net.Socket; 6 import java.util.Scanner; 7 8 /** 9 拓展:即时通信 10 11 客户端:发消息的同时,随时有人发消息过来。 12 服务端:接收消息后,推送给其他所有的在线socket 13 */ 14 public class ClientDemo1 { 15 public static void main(String[] args) { 16 try { 17 System.out.println("====客户端启动==="); 18 // 1、创建Socket通信管道请求有服务端的连接 19 // public Socket(String host, int port) 20 // 参数一:服务端的IP地址 21 // 参数二:服务端的端口 22 Socket socket = new Socket("127.0.0.1", 6868); 23 24 // 马上为客户端分配一个独立的线程负责读取它收到的消息 25 new ClientReaderThread(socket).start(); 26 27 // 2、从socket通信管道中得到一个字节输出流 负责发送数据 28 OutputStream os = socket.getOutputStream(); 29 30 // 3、把低级的字节流包装成打印流 31 PrintStream ps = new PrintStream(os); 32 33 Scanner sc = new Scanner(System.in); 34 while (true) { 35 System.out.println("请说:"); 36 String msg = sc.nextLine(); 37 // 4、发送消息 38 ps.println(msg); 39 ps.flush(); 40 } 41 // 关闭资源。 42 // socket.close(); 43 44 } catch (Exception e) { 45 e.printStackTrace(); 46 } 47 } 48 }ClientDemo
1 package com.itheima.d9_chat; 2 3 import java.io.BufferedReader; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.io.PrintStream; 7 import java.net.Socket; 8 9 public class ClientReaderThread extends Thread{ 10 private Socket socket; 11 public ClientReaderThread(Socket socket){ 12 this.socket = socket; 13 } 14 @Override 15 public void run() { 16 try { 17 // 3、从socket通信管道中得到一个字节输入流 18 InputStream is = socket.getInputStream(); 19 // 4、把字节输入流包装成缓冲字符输入流进行消息的接收 20 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 21 // 5、按照行读取消息 22 String msg; 23 while ((msg = br.readLine()) != null){ 24 System.out.println(socket.getRemoteSocketAddress() + "收到了: " + msg); 25 } 26 } catch (Exception e) { 27 System.out.println("服务端把你踢出去了~~"); 28 } 29 } 30 31 }ClientReaderThread
1 package com.itheima.d9_chat; 2 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 import java.util.ArrayList; 6 import java.util.List; 7 8 /** 9 目标: 即时通信 10 */ 11 public class ServerDemo2 { 12 13 public static List<Socket> onLineSockets = new ArrayList<>(); 14 15 public static void main(String[] args) { 16 try { 17 System.out.println("===服务端启动成功==="); 18 // 1、注册端口 19 ServerSocket serverSocket = new ServerSocket(6868); 20 // a.定义一个死循环由主线程负责不断的接收客户端的Socket管道连接。 21 while (true) { 22 // 2、每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息 23 Socket socket = serverSocket.accept(); 24 System.out.println(socket.getRemoteSocketAddress()+ "它来了,上线了!"); 25 // 把当前客户端管道Socket加入到在线集合中去 26 onLineSockets.add(socket); 27 28 // 3、开始创建独立线程处理socket 29 new ServerReaderThread(socket).start(); 30 } 31 } catch (Exception e) { 32 e.printStackTrace(); 33 } 34 } 35 }ServerDemo
1 package com.itheima.d9_chat; 2 3 import java.io.*; 4 import java.net.Socket; 5 6 public class ServerReaderThread extends Thread{ 7 private Socket socket; 8 public ServerReaderThread(Socket socket){ 9 this.socket = socket; 10 } 11 @Override 12 public void run() { 13 try { 14 // 3、从socket通信管道中得到一个字节输入流 15 InputStream is = socket.getInputStream(); 16 // 4、把字节输入流包装成缓冲字符输入流进行消息的接收 17 BufferedReader br = new BufferedReader(new InputStreamReader(is)); 18 // 5、按照行读取消息 19 String msg; 20 while ((msg = br.readLine()) != null){ 21 System.out.println(socket.getRemoteSocketAddress() + "说了:: " + msg); 22 // 把这个消息发给当前所有在线socket 23 sendMsgToAll(msg); 24 } 25 } catch (Exception e) { 26 System.out.println(socket.getRemoteSocketAddress() + "下线了!!!"); 27 // 从在线集合中抹掉本客户端socket 28 ServerDemo2.onLineSockets.remove(socket); 29 } 30 } 31 32 private void sendMsgToAll(String msg) { 33 try { 34 // 遍历全部的在线 socket给他们发消息 35 for (Socket onLineSocket : ServerDemo2.onLineSockets) { 36 // 除了自己的socket,其他socket我都发!! 37 if(onLineSocket != socket){ 38 PrintStream ps = new PrintStream(socket.getOutputStream()); 39 ps.println(msg); 40 ps.flush(); 41 } 42 } 43 } catch (Exception e) { 44 e.printStackTrace(); 45 } 46 } 47 }ServerReaderDemo
6、TCP通信实战案例:模拟BS系统
A、TCP通信如何实现BS请求网页信息回来呢
客户端使用浏览器发起请求(不需要开发客户端)
服务端必须按照浏览器的协议规则响应数据
浏览器使用什么协议规则呢
HTTP协议
===========================================================================================================================
1 package com.itheima.d10_bs; 2 3 import java.io.PrintStream; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 import java.util.concurrent.*; 7 8 /** 9 了解:BS-浏览器-服务器基本了解。 10 11 引入: 12 之前客户端和服务端都需要自己开发。也就是CS架构。 13 接下来模拟一下BS架构。 14 15 客户端:浏览器。(无需开发) 16 服务端:自己开发。 17 需求:在浏览器中请求本程序,响应一个网页文字给浏览器显示 18 19 20 */ 21 public class BSserverDemo { 22 // 使用静态变量记住一个线程池对象 23 private static ExecutorService pool = new ThreadPoolExecutor(3, 24 5, 6, TimeUnit.SECONDS, 25 new ArrayBlockingQueue<>(2) 26 , Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); 27 28 public static void main(String[] args) { 29 try { 30 // 1.注册端口 31 ServerSocket ss = new ServerSocket(8080); 32 // 2.创建一个循环接收多个客户端的请求。 33 while(true){ 34 Socket socket = ss.accept(); 35 // 3.交给一个独立的线程来处理! 36 pool.execute(new ServerReaderRunnable(socket)); 37 } 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 } 42 }BServer
1 package com.itheima.d10_bs; 2 3 import java.io.BufferedReader; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.io.PrintStream; 7 import java.net.Socket; 8 9 public class ServerReaderRunnable implements Runnable{ 10 private Socket socket; 11 public ServerReaderRunnable(Socket socket){ 12 this.socket = socket; 13 } 14 @Override 15 public void run() { 16 try { 17 // 浏览器 已经与本线程建立了Socket管道 18 // 响应消息给浏览器显示 19 PrintStream ps = new PrintStream(socket.getOutputStream()); 20 // 必须响应HTTP协议格式数据,否则浏览器不认识消息 21 ps.println("HTTP/1.1 200 OK"); // 协议类型和版本 响应成功的消息! 22 ps.println("Content-Type:text/html;charset=UTF-8"); // 响应的数据类型:文本/网页 23 24 ps.println(); // 必须发送一个空行 25 26 // 才可以响应数据回去给浏览器 27 ps.println("<span style='color:red;font-size:90px'>《最牛的149期》 </span>"); 28 ps.close(); 29 } catch (Exception e) { 30 System.out.println(socket.getRemoteSocketAddress() + "下线了!!!"); 31 } 32 } 33 }ServerReaderRunnable 标签:JAVA,进阶,java,--,new,import,public,Socket,socket From: https://www.cnblogs.com/Flower--Dance/p/16696812.html