Java
网络编程
概念
- 网络编程可以让程序与网络上的其他设备中的程序进行数据交互
网络通信基本模式
- 常见的通信模式有如下两种形式,Client-Server(CS),Browser/Server(BS)
Client-Server
Client
- 需要程序员开发实现
- 用户需要安装客户端
Server
- 需要程序员开发实现
Browser-Server
Browser
- 不需要程序员开发
- 用户需要安装浏览器
Server
- 需要程序员开发实现
学习要求
网络通信三要素 | UDP通信 | TCP通信 | 即时通信 | 模拟BS系统 |
---|---|---|---|---|
一个消息发送给对方需要哪些关键因素 | 消息直接发送给对象,不确认对方是否在线,不做消息确认 | 基于可靠传输的方式进行的通信模式。解决不同场景的通信需求 | 如何实现即时通信,具体是如何实现的 | WEB系统是如何支持访问到网页的,具体是如何与服务器通信的 |
网络三要素
三要素概述
IP地址:设备在网络中的地址,是唯一的标识
端口:应用程序在设备中唯一的标识
协议:数据在网络传输的规则,常见的协议有UDP协议和TCP协议
要素一:IP地址
- IP(internet Protocol):全程“互联网协议地址”,是分配给上网设备的唯一标志
- 常见的IP分类为:IPv4和IPv6
- IPv4 (四个字节)32位
- IPv6:128位(16个字节)
- IPv6分成8个整数,每个整数 用四个十六进制位表示,数之间用冒号(:)分开
IP地址基本寻路
采用域名
IP地址形式
- 公网地址和私有地址(局域网使用)
- 192.168.开头的就是常见的局域网地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用
IP常用命令
- ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址
- 本机127.0.0.1或者localhost:称为回送地址也可称本地回环地址,只会寻找当前所在本机
IP地址操作类——inetAddress
InetAddress的使用
- 此类表示Internet协议(IP)地址
InetAddress API如下
名称 | 说明 |
---|---|
public static InetAddress getLocalHost() | 返回本主机的地址对象 |
public static InetAddress getByName(String host) | 得到指定主机的IP地址对象,参数是域名或者IP地址 |
public Sting getHostName() | 获取此IP地址的主机名 |
public Sting getHostAddress() | 返回IP地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连通IP地址对应的主机,返回true |
package com.yu.Day1012Demo;
import java.net.InetAddress;
public class InetAddressDemo01 {
public static void main(String[] args) throws Exception{
//1. 获取本机地址对象
InetAddress ia = InetAddress.getLocalHost();
System.out.println(ia);
System.out.println(ia.getHostName());
System.out.println(ia.getHostAddress());
//输出内容是电脑蓝牙名和IP地址
//2.获取域名对象的IP地址
InetAddress ia2 = InetAddress.getByName("www.baidu.com");
System.out.println(ia2);
//3. 获取公网IP对象
InetAddress ia3 = InetAddress.getByName("110.242.68.4");
System.out.println(ia3);
//4. 判断是否连通,ping 5s之前测试是否连通
System.out.println(ia3.isReachable(5000));
}
}
总结
-
IP地址的代表类是
InetAddress类
-
如何获取本机IP对象
- public static InetAddress getLocalHost()
-
如何判断与该IP地址对象是否互通
- public boolean isReachable(int timeout)
要素二:端口号
- 端口号:标识正在计算机设备上运行的进程(程序),被规定为一个16为的二进制,范围0-65535
端口类型
- 周知端口:0-1023,被预先定义的知名应用占用(如HTTP占用80,FTP占用21)
- 注册端口:1024-49151,分配给iyh进程或某些应用程序。(如Tomcat占用8080,MySQL占用3306)
- 动态端口:49152-65535,之所以称之为动态端口,是因为它一般不固定分配某种进程,而是动态分配
注意:我们自己开发的程序注册端口,且一个设备中不能出现两个程序端口号一样,否则出错
总结
- 端口号的作用是什么
- 唯一标识正在计算机设备上运行的进程(程序)
- 一个设备中,能否出现两个应用程序的端口号一样,为什么?
- 不可以,如果一样会出现端口号冲突错误
要素三:协议
通信协议
- 连接和通信数据的规则被称为网络通信协议
网络通信协议有两套参考模型
- OSI参考模型:世界上互联协议标准,全球通信规范,由于此模型过于理想化未能在因特网上进行广泛推广
- TCP/IP参考模型(或TCP/IP协议):事实上的国际标准
OSI参考模型 | TCP/IP参考模型 | 各层对应 | 面向操作 |
---|---|---|---|
应用层 表示层 会话层 |
应用层 | HTTP、FTP、DNS、SMTP | 应用程序需要关注的:浏览器、邮箱、程序员一般在这一层开发 |
传输层 | 传输层 | TCP、UDP | 选择使用的TCP,UDP协议 |
网络层 数据链路层 |
网络层 | IP、ICMP | 封装源和目标IP,进行路径选择 |
物理层 | 数据链路层——物理 | 物理寻址、比特流 | 物理设备中传输 |
传输层的两个常见协议
- TCP(Transmission Control Protocol):传输控制协议
- UDP(User Datagram Protocol):用户数据报协议
TCP协议特点
- 使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议
- 传输前,采用“三次握手”方式建立连接,所以是可靠的
- 在连接中可进行大量数据的传输
- 连接、发送数据都需要确认、且传输完毕后、还需释放已建立的连接,通信效率较低
TCP协议通信场景
- 对信息安全要求较高的场所,例如:文件下载、金融等数据通信
UDP协议
- UDP是一种无连接、不可靠传输的协议
- 将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64kb内
- 发送不管对方是否准备号,接收方收到也不确认,故而不可靠的
- 可以广播发送,发送数据结束时无需释放资源,开销小,速度快
UDP协议通信场景
- 语音通话,视频通话等
UDP通信
特点
- UDP是一种无连接、不可靠传输的协议
- 将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64kb内
- 发送不管对方是否准备号,接收方收到也不确认,故而不可靠的
DatagramPacket:数据包对象
构造器 | 说明 |
---|---|
public DatagramPacket(byte[] buf,int length,InetAddress address,int port) | 创建发送端数据包对象 buf:要发送的内容,字节数组 length:要发送内容的字节长度 address:接收端的IP地址对象 port:接收端的端口号 |
public DatagramPacket(byte[] buf,int length) | 创建接收端的数据包对象 buf:用来存储接收来的内容 length:能够接收内容长度 |
DatagramPacket常用方法
方法 | 说明 |
---|---|
public int getLength() | 获得实际接收到的字节个数 |
DatagramSocket:发送端和接收端对象
构造器 | 说明 |
---|---|
public DatagramSocket() | 创建发送端的Socket对象,系统会随机分配一个端口号 |
public DatagramSocket(int port) | 创建接收端的Socket对象并指定端口号 |
DatagramSocket类成员方法
方法 | 说明 |
---|---|
public void send(DatagramPacket dp) | 发送数据包 |
public void receive(DatagramPacket p) | 接收数据包 |
总结
- UDP发送端和接收端的对象是哪个?
- public DatagramSocket():创建发送端的Socket对象
- public DatagramSocket(int port):创建接收端的Socket对象
- 数据包对象是哪个?
- DatagramPacket
- 如何发送、接收数据包?
- 使用DatagramSocket的如下方法
- public void send(DatagramPacket dp):发送数据包
- public void receive(DatagramPacket dp):接收数据包
- 先执行接收端在执行发送端
package com.yu.Day1012Demo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ClientDemo01 {
public static void main(String[] args) throws Exception{
//1.创建发送端对象,发送端自带默认端口号
DatagramSocket socket = new DatagramSocket();
//2. 创建一个数据包对象封装数据
/**
* public DatagramPacket(byte[] buf,
* int length,
* InetAddress address,
* int port)
* 参数一 封装发送的数据
* 参数二:发送数据的大小
* 参数三:服务器的Ip地址
* 参数四:服务端端口号
*/
byte[] bytes = "我是一颗快乐的韭菜,你想吃吗?".getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length,
InetAddress.getLocalHost(),8888);
//3.发送数据出去
socket.send(packet);
socket.close();
}
}
package com.yu.Day1012Demo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerDemo01 {
public static void main(String[] args) throws Exception{
//1.创建接收端对象,连接端口
DatagramSocket socket = new DatagramSocket(8888);
//2.创建一个数据包对象接收数据
byte[] bytes = new byte[1034*64];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
//3. 等待接收数据
socket.receive(packet);
//4. 取出数据即可
int len = packet.getLength();
String sr = new String(bytes,0,len);
System.out.println(sr);
//获取发送端的ip和端口
String ip = packet.getSocketAddress().toString();
System.out.println(ip);
int port = packet.getPort();
System.out.println(port);
socket.close();
}
}
多发多收
案例:使用UDP通信实现:多发多收消息
package com.yu.Day1012Demo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class ClientDemo02 {
public static void main(String[] args) throws Exception{
//1.创建发送端对象,发送端自带默认端口号
DatagramSocket socket = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("请输入");
String msg = sc.nextLine();
if("exit".equals(msg)){
System.out.println("离线成功!!!");
socket.close();
break;
}
//2. 创建一个数据包对象封装数据
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length,
InetAddress.getLocalHost(),8888);
//3.发送数据出去
socket.send(packet);
}
}
}
package com.yu.Day1012Demo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerDemo02 {
public static void main(String[] args) throws Exception{
//1.创建接收端对象,连接端口
DatagramSocket socket = new DatagramSocket(8888);
//2.创建一个数据包对象接收数据
byte[] bytes = new byte[1024*64];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
while (true) {
//3. 等待接收数据
socket.receive(packet);
//4. 取出数据即可
int len = packet.getLength();
String sr = new String(bytes,0,len);
String ip = String.valueOf(packet.getAddress());
int port = packet.getPort();
System.out.println("发送方的ip地址为"+ip+"端口号为:"+port+"内容为"+sr);
}
}
}
总结
- UDP的接收端为什么可以接收很多发送端的消息
- 接收端只负责接收数据包,无所谓是哪个发送端的数据包
广播、组播
UDP的三种通信方式
- 单播:单台主机与单台主机之间的通信
- 广播:当前主机与所在网络中的所有主机通信
- 组播:当前主机与选定的一组主机的通信
UDP如何实现广播
- 使用广播地址:255.255.255.255
- 具体操作
- 发送端发送的数据包的目的地写的是广播地址,且指定端口。(255.255.255.255 9999)
- 本机所在网段的其他主机的程序只要能匹配端口成功即可收到消息了(9999)
UDP如何实现组播
- 使用组播地址:224.0.0.0~239.255.255.255
- 具体操作
- 发送端的数据包的目的地是组播IP(例如 224.0.1.1 9999)
- 接收端必须绑定该组播IP(224.0.1.1),端口还有对应发送端的目的地端口9999,这样即可接收该组播消息
- DatagramSocket的子类MulticastSocket可以在接收端绑定组播ip
package com.yu.Day1012Demo;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class ClientDemo04 {
public static void main(String[] args) throws Exception{
//1.创建发送端对象,发送端自带默认端口号
DatagramSocket socket = new DatagramSocket();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("请输入");
String msg = sc.nextLine();
if("exit".equals(msg)){
System.out.println("离线成功!!!");
socket.close();
break;
}
//2. 创建一个数据包对象封装数据
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length,
InetAddress.getByName("224.0.1.1"),9999);
//3.发送数据出去
socket.send(packet);
}
}
}
package com.yu.Day1012Demo;
import java.net.*;
public class ServerDemo04 {
public static void main(String[] args) throws Exception{
//1.创建接收端对象,连接端口
MulticastSocket socket = new MulticastSocket(9999);
//把当前接收端加入到一个组播中去,绑定对应的组播消息的组播ip
//过时
// socket.joinGroup(InetAddress.getByName("224.0.1.1"));
socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1"),9999),
NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));
//2.创建一个数据包对象接收数据
byte[] bytes = new byte[1024*64];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
while (true) {
//3. 等待接收数据
socket.receive(packet);
//4. 取出数据即可
int len = packet.getLength();
String sr = new String(bytes,0,len);
String ip = String.valueOf(packet.getAddress());
int port = packet.getPort();
System.out.println("发送方的ip地址为"+ip+"端口号为:"+port+"内容为"+sr);
}
}
}
TCP通信协议
*** 注意:在Java中只有使用java.net.Sokcket类实现通信,底层即是使用了TCP协议***
Socket
构造器 | 说明 |
---|---|
public Socket(String host,int port) | 创建发送端的Socket对象与服务端连接,参数为服务端程序的IP和端口号 |
Socket类成员方法
方法 | 说明 |
---|---|
OutputStream getOutputStream() | 获得字节输出流对象 |
InputStream getInputStream() | 获得字节输出流对象 |
总结
- TCP通信的客户端的代表类是谁?
- Socket类
- public Socket(String host,int port)
- TCP通信如何使用Socket管道发送,接收数据。
- OutputStream getOutputStream():获得字节输出流对象(发)
ServerSocket (服务端)
构造器 | 说明 |
---|---|
public ServerSocket(int port) | 注册服务端端口 |
ServerSocket类成员方法
方法 | 说明 |
---|---|
public Socket accept() | 等待接收客户端的Socket通信连接 连接成功返回Socket对象与客户端建立端到端通信 |
总结
- TCP通信服务端用的代表类
- ServerSocket类,注册端口
- 调用accept()方法阻塞等待接收客户端连接,得到Socket对象
- TCP通信的基本原理
- 客户端怎么发,服务端就应该怎么收
- 客户端如果没有消息,服务端会进入阻塞等待
- Socket一方关闭或者出现异常,对方Socket也会失效或者出错
package com.yu.Day1012Demo;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
public class ClientDemo05 {
public static void main(String[] args) throws Exception{
//1. 创建Socket通信管道请求服务其的连接
//public Socket(String host.int port)
//参数一:服务器的IP地址
//参数二:服务器的端口
Socket socket = new Socket("127.0.0.1",7777);
//2.从socket通信管道中得到一个字节输出流负责发送数据
OutputStream os = socket.getOutputStream();
//3. 把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
//4.发送消息
ps.println("我是TCP的客户端,我已经与你对接,并发出通话:约不?");
ps.flush();
//关闭通话
socket.close();
}
}
package com.yu.Day1012Demo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo05 {
public static void main(String[] args) throws Exception{
//1.注册窗口
ServerSocket serverSocket = new ServerSocket(7777);
//2.必须调用accept方法。等待接收客户端的Socket连接请求,建立Socket通信管道
Socket socket = serverSocket.accept();
//3.从Socket通信管道中得到一个字节输出流
InputStream is = socket.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msq;
while ((msq = br.readLine())!=null) {
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msq);
}
}
}
TCP通信实现多发多收
群发多收
package com.yu.Day1012Demo;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo07 {
public static void main(String[] args) throws Exception{
//1. 创建Socket通信管道请求服务其的连接
//public Socket(String host.int port)
//参数一:服务器的IP地址
//参数二:服务器的端口
Socket socket = new Socket("127.0.0.1",7777);
//2.从socket通信管道中得到一个字节输出流负责发送数据
OutputStream os = socket.getOutputStream();
//3. 把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("请输入要发送的消息:");
String msq = sc.nextLine();
//4.发送消息
ps.println(msq+"爱你");
ps.flush();
}
//关闭通话
// socket.close();
}
}
package com.yu.Day1012Demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderThread extends Thread {
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msq;
while ((msq = br.readLine())!=null) {
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msq);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"下线了!!!");
}
}
}
package com.yu.Day1012Demo;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo07 {
public static void main(String[] args) throws Exception{
//1.注册窗口
ServerSocket serverSocket = new ServerSocket(7777);
//2.必须调用accept方法。等待接收客户端的Socket连接请求,建立Socket通信管道
while (true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"上线了");
new ServerReaderThread(socket).start();
}
}
}
总结
- 本次是如何实现服务端接收多个客户端的消息的
- 主线程定义了循环负责接收客户端Socket管道连接
- 没接收到一个Socket通信管道后分配一个独立的线程负责它
TCP通信-使用线程池优化
客户端同上
package com.yu.Day1012Demo.Executor;
import com.yu.Day1012Demo.ServerReaderThread;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class ServerDemo {
private static ExecutorService pool = new ThreadPoolExecutor(
3,5,6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) throws Exception{
System.out.println("===服务器启动成===");
//1.注册窗口
ServerSocket serverSocket = new ServerSocket(7777);
//2.必须调用accept方法。等待接收客户端的Socket连接请求,建立Socket通信管道
while (true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"上线了");
//负责读取消息
Runnable target = new ServerReaderRunnable(socket);
pool.execute(target);
}
}
}
package com.yu.Day1012Demo.Executor;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msq;
while ((msq = br.readLine())!=null) {
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msq);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"下线了!!!");
}
}
}
控制了总体数量,不会让服务器负载过多而崩掉
即时通信
端口转发
- 即时通信是什么含义,要实现怎么样的设计?
- 即时通信,是指一个客户端的消息发出去,其他客户端可以接收到
- 即时通信需要进行端口转发的设计思想
- 服务端需要把在线的Socket管道存储起来
- 一旦收到一个消息要推送给其他管道
package com.yu.Day1012Demo.jishitongxin;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo00 {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("127.0.0.1",7777);
//创建一个独立的线程专门负责这个客户端的读消息(服务端随时可能转发消息过来)
new ClientReaderThread(socket).start();
OutputStream os = socket.getOutputStream();
//3. 把低级的字节流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("请输入要发送的消息:");
String msq = sc.nextLine();
//4.发送消息
ps.println(msq+"爱你");
ps.flush();
}
//关闭通话
// socket.close();
}
}
class ClientReaderThread extends Thread{
private Socket socket;
public ClientReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msq;
while ((msq = br.readLine())!=null) {
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msq);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"被踢出群聊!!!");
}
}
}
package com.yu.Day1012Demo.jishitongxin;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class ServerDemo00 {
//定义一个静态的List集合存储当前全部在线socket管道
public static List<Socket> all_onLine_socket = new ArrayList<>();
public static void main(String[] args) throws Exception{
//1.注册窗口
ServerSocket serverSocket = new ServerSocket(7777);
//2.必须调用accept方法。等待接收客户端的Socket连接请求,建立Socket通信管道
while (true) {
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress()+"上线了");
all_onLine_socket.add(socket);
new ServerReaderThread(socket).start();
}
}
}
class ServerReaderThread extends Thread {
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
//4.把字节输入流包装成缓冲字符输入流进行消息的接收
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//5.按照行读取消息
String msq;
while ((msq = br.readLine())!=null) {
System.out.println(socket.getRemoteSocketAddress()+"说了:"+msq);
//把这个消息进行端口转发给全部客户端socket管道
sendMsgToAll(msq);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"下线了!!!");
ServerDemo00.all_onLine_socket.remove(socket);
}
}
private void sendMsgToAll(String msq) throws Exception {
for (Socket socket : ServerDemo00.all_onLine_socket) {
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println(msq);
ps.flush();
}
}
}
模拟BS通信
实现BS开发
注意:服务器必须给浏览器响应HTTP协议格式的数据,否则浏览器不识别
package com.yu.Day1012Demo.BS;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class BSserverDemo {
private static ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory());
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(8080);
while(true){
Socket socket = ss.accept();
pool.execute(new ServerReaderRunnable(socket));
}
}
}
package com.yu.Day1012Demo.BS;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//浏览器 已经与本线程建立了Socket管道
//相应消息给浏览器显示
PrintStream ps = new PrintStream(socket.getOutputStream());
//必须相应HTTP协议格式数据,否则浏览器不认识消息
ps.println("HTTP/1.1 200 0k");
ps.println("Content-Type:text/html;charset=UTF-8");
ps.println();//必须发送一个空行
//才可以响应数据回去给浏览器
ps.println("<span style='color:red;font-size:90px'>最牛的1497</span>");
ps.close();
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress()+"下线了!!!");
}
}
}
标签:java,Socket,编程,网络,socket,new,import,public,heimaJava
From: https://www.cnblogs.com/yuxxiao/p/16786768.html