当前位置: 首页 > news >正文

【黑马java基础】网络通信

可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)。

java提供了java.net.*包下提供了网络编程的解决方案

通信的基本架构主要有两种形式:一种是CS架构(Client 客户端/Server服务端)、一种是BS架构(Brower 浏览器/Server服务端)。

  • CS架构的特点:CS架构需要用户在自己的电脑或者手机上安装客户端软件,然后由客户端软件通过网络连接服务器程序,由服务器把数据发给客户端,客户端就可以在页面上看到各种数据了。

在这里插入图片描述

  • **BS架构的特点:**BS架构不需要开发客户端软件,用户只需要通过浏览器输入网址就可以直接从服务器获取数据,并由服务器将数据返回给浏览器,用户在页面上就可以看到各种数据了。

    在这里插入图片描述

    这两种结构不管是CS、还是BS都是需要用到网络编程的相关技术。我们学习Java的程序员,以后从事的工作方向主要还是BS架构的。

一、网络通信三要素

三要素分别是IP地址、端口号、通信协议

在这里插入图片描述

  1. IP地址:表示设备在网络中的地址,是网络中设备的唯一标识

  2. 端口号:应用程序在设备中唯一的标识

  3. 协议:连接和数据在网络中传输的规则。

如下图所示:假设现在要从一台电脑中的微信上,发一句“你愁啥?”到其他电脑的微信上,流程如下

1.先通过ip地址找到对方的电脑
2.再通过端口号找到对方的电脑上的应用程序
3.按照双方约定好的规则发送、接收数据

在这里插入图片描述

1.1 IP地址

**IP(Ineternet Protocol)全称互联网协议地址,是分配给网络设备的唯一表示。**IP地址分为:IPV4地址、IPV6地址

IP地址分类

IPV4地址由32个比特位(4个字节)组成,如果下图所示,但是由于采用二进制太不容易阅读了,于是就将每8位看成一组,把每一组用十进制表示(叫做点分十进制表示法)。所以就有了我们经常看到的IP地址形式,如:192.168.1.66

在这里插入图片描述

如果想查看本机的IP地址,可以在CMD命令行窗口,输入ipconfig命令查看,如下图所示:

在这里插入图片描述

经过不断的发展,现在越来越多的设备需要联网,IPV4地址已经不够用了,所以扩展出来了IPV6地址。

IPV6采用128位二进制数据来表示(16个字节),号称可以为地球上的每一粒沙子编一个IP地址,

IPV6比较长,为了方便阅读,每16位编成一组,每组采用十六进制数据表示,然后用冒号隔开(称为冒分十六进制表示法),如下图所示

在这里插入图片描述

现在的网络设备,一般IPV4和IPV6地址都是支持的。


域名

聊完什么是IP地址和IP地址分类之后,接下来再给大家介绍一下和IP地址相关的一个东西,叫做域名。

我们在浏览器上访问某一个网站是,就需要在浏览器的地址栏输入网址,这个网址的专业说法叫做域名。比如:传智播客的域名是http://www.itcast.cn

域名和IP其实是一一对应的,由运营商来管理域名和IP的对应关系。我们在浏览器上敲一个域名时,首先会在我们电脑的DNS服务器上找有没有记录过的域名,有的话,返回给真实IP地址进行访问;如果域名没有记录过,由运营商的域名解析服务,把域名转换为ip地址,再通过IP地址去访问对应的服务器设备。

在这里插入图片描述

公网IP,内网IP

  • 公网IP:是可以连接互联网的IP地址;内网IP:也叫局域网IP,只能组织机构内部使用。

  • 192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用。

  • 特殊的IP地址:127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主机。

IP常用命令:

ipconfig:查看本机IP地址。
ping IP地址:检查网络是否连通。

InetAddress

代表IP地址,常用方法有:

名称说明
public static InetAddress getLocalHost()获取本机IP,会以一个inetAddress的对象返回
public static InetAddress getByName(String host)根据ip地址或者域名,返回一个inetAdress对象
public String getHostName()获取该ip地址对象对应的主机名。
public String getHostAddress()获取该ip地址对象中的ip地址信息。
public boolean isReachable(int timeout)在指定毫秒内,判断主机与该ip对应的主机是否能连通

代码如下:

package com.itheima.d1_ip;import java.net.InetAddress;/*** 目标:掌握InetAddress类的使用。*/
public class InetAddressTest {public static void main(String[] args) throws Exception {// 1、获取本机IP地址对象的InetAddress ip1 = InetAddress.getLocalHost();System.out.println(ip1.getHostName());System.out.println(ip1.getHostAddress());// 2、获取指定IP或者域名的IP地址对象。InetAddress ip2 = InetAddress.getByName("www.baidu.com");System.out.println(ip2.getHostName());System.out.println(ip2.getHostAddress());// ping www.baidu.comSystem.out.println(ip2.isReachable(6000));}
}//
ZHAO
192.168.2.49
www.baidu.com
183.2.172.42
true

百度的ip地址(183.2.172.42)访问不到,可能是因为CDN技术

1.2 端口号

端口号:指的是计算机设备上运行的应用程序的标识,被规定为一个16位的二进制数据,范围(0~65535)

端口号分为一下几类(了解一下)

  • 周知端口:0~1023,被预先定义的知名应用程序占用(如:HTTP占用80,FTP占用21)
  • 注册端口:1024~49151,分配给用户经常或者某些应用程序
  • 动态端口:49152~65536,之所以称为动态端口,是因为它一般不固定分配给某进程,而是动态分配的。

注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。

1.3 协议

网络上通信的设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议。

为了让世界上各种上网设备能够互联互通,肯定需要有一个组织出来,制定一个规则,大家都遵守这个规则,才能进行数据通信。

开放式网络互联标准:OSI网络参考模型

  • OSI网络参考模型:全球网络互联标准。
  • TCP/IP网络模型:事实上的国际标准。

在这里插入图片描述

传输层的2个通信协议

UDP(User Datagram Protocol):用户数据报协议;

TCP(Transmission Control Protocol) :传输控制协议。

1、UDP协议
  • 特点:无连接、不可靠通信。

  • 不事先建立连接,数据按照包发,一包数据包含:自己的IP、程序端口,目的地IP、程序端口和数据(限制在64KB内)等。

  • 发送方不管对方是否在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,故是不可靠的 。

通信效率高!广泛运用在语音通话(如果丢帧,无非就是声音小点之类),视频直播(无非画面模糊点)

2、TCP协议
  • 特点:面向连接、可靠通信。
  • TCP的最终目的:要保证在不可靠的信道上实现可靠的传输。
  • TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接。

TCP协议:三次握手建立可靠连接

可靠连接:确定通信双方,收发消息都是正常无问题的!(全双工)

在这里插入图片描述

因为通过三次握手可以确定,双方的收发都没问题。确保可以传输数据和以及数据的可靠性。

TCP协议:四次握手断开连接

目的:确保双方数据的收发都已经完成!

在这里插入图片描述

服务端在响应的时候,极有可能也同时发了一些数据给客户端,作为最后一次数据,客户端收到数据后,发出正式确认数据都收完,然后断开连接。

通信效率相对不高,广泛应用在网页、文件下载、支付等。

二、UDP通信

特点:无连接、不可靠通信。

不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。

Java提供了一个java.net.DatagramSocket类来实现UDP通信。

在这里插入图片描述

  • DatagramSocket: 用于创建客户端、服务端
构造器说明
public DatagramSocket()创建客户端的Socket对象, 系统会随机分配一个端口号。
public DatagramSocket(int port)创建服务端的Socket对象, 并指定端口号
方法说明
public void send(DatagramPacket dp)发送数据包
public void receive(DatagramPacket p)使用数据包接收数据
  • DatagramPacket:创建数据包
构造器说明
public DatagramPacket(byte[] buf, int length, InetAddress address, int port)创建发出去的数据包对象
public DatagramPacket(byte[] buf, int length)创建用来接收数据的数据包
方法说明
public int getLength()获取数据包,实际接收到的字节个数

客户端实现步骤:

① 创建DatagramSocket对象(客户端对象)           扔韭菜的人
② 创建DatagramPacket对象封装需要发送的数据(数据包对象)          韭菜盘子
③ 使用DatagramSocket对象的send方法,传入DatagramPacket对象           开始抛出韭菜
④ 释放资源

客户端代码如下:

package com.itheima.d2_upd1;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;/*** 目标:完成UDP通信快速入门:实现1发1收。*/
public class Client {public static void main(String[] args) throws Exception {// 1、创建客户端对象(发韭菜出去的人)DatagramSocket socket = new DatagramSocket(7777);// 2、创建数据包对象封装要发出去的数据(创建一个韭菜盘子)/* public DatagramPacket(byte buf[], int length,InetAddress address, int port)参数一:封装要发出去的数据。参数二:发送出去的数据大小(字节个数)参数三:服务端的IP地址(找到服务端主机)参数四:服务端程序的端口。*/byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),  6666);// 3、开始正式发送这个数据包的数据出去了socket.send(packet);System.out.println("客户端数据发送完毕~~~");socket.close(); // 释放资源!}
}

服务端实现步骤

①创建DatagramSocket对象并指定端口(服务端对象)           接韭菜的人
②创建DatagramPacket对象接收数据(数据包对象)         韭菜盘子
③使用DatagramSocket对象的receive方法,传入DatagramPacket对象          开始接收韭菜
④释放资源

服务端代码如下:

package com.itheima.d2_upd1;import java.net.DatagramPacket;
import java.net.DatagramSocket;/*** 目标:完成UDP通信快速入门-服务端开发*/
public class Server {public static void main(String[] args) throws Exception {System.out.println("----服务端启动----");// 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口DatagramSocket socket = new DatagramSocket(6666);// 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)byte[] buffer = new byte[1024 * 64]; // 64KB.DatagramPacket packet = new DatagramPacket(buffer, buffer.length);// 3、开始正式使用数据包来接收客户端发来的数据socket.receive(packet);// 4、从字节数组中,把接收到的数据直接打印出来// 接收多少就倒出多少// 获取本次数据包接收了多少数据。int len = packet.getLength();String rs = new String(buffer, 0 , len);System.out.println(rs);System.out.println(packet.getAddress().getHostAddress());System.out.println(packet.getPort());socket.close(); // 释放资源}
}

启动时,先启动服务端,然后启动客户端,才能保证数据不丢失

三、UDP通信-多发多收

通过循环,实现多发多收

客户端实现步骤

① 创建DatagramSocket对象(发送端对象)           扔韭菜的人
② 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
③ 如果用户输入的不是exit, 把数据封装成DatagramPacket           韭菜盘子
④ 使用DatagramSocket对象的send方法将数据包对象进行发送          开始抛出韭菜
⑤ 释放资源
package com.itheima.d3_udp2;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;/*** 目标:完成UDP通信快速入门:实现客户端反复的发。*/
public class Client {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地址(找到服务端主机)参数四:服务端程序的端口。*/Scanner sc = new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();// 一旦发现用户输入的exit命令,就退出客户端if("exit".equals(msg)){System.out.println("欢迎下次光临!退出成功!");socket.close(); // 释放资源break; // 跳出死循环}byte[] bytes = msg.getBytes();DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),  6666);// 3、开始正式发送这个数据包的数据出去了socket.send(packet);}}
}

接收端实现步骤

①创建DatagramSocket对象并指定端口(接收端对象)           接韭菜的人②创建DatagramPacket对象接收数据(数据包对象)          韭菜盘子③使用DatagramSocket对象的receive方法传入DatagramPacket对象④使用while死循环不断的进行第3
package com.itheima.d3_udp2;import java.net.DatagramPacket;
import java.net.DatagramSocket;/*** 目标:完成UDP通信快速入门-服务端反复的收*/
public class Server {public static void main(String[] args) throws Exception {System.out.println("----服务端启动----");// 1、创建一个服务端对象(创建一个接韭菜的人) 注册端口DatagramSocket socket = new DatagramSocket(6666);// 2、创建一个数据包对象,用于接收数据的(创建一个韭菜盘子)byte[] buffer = new byte[1024 * 64]; // 64KB.DatagramPacket packet = new DatagramPacket(buffer, buffer.length);while (true) {// 3、开始正式使用数据包来接收客户端(们)发来的数据socket.receive(packet);// 4、从字节数组中,把接收到的数据直接打印出来// 接收多少就倒出多少// 获取本次数据包接收了多少数据。int len = packet.getLength();String rs = new String(buffer, 0 , len);System.out.println(rs);System.out.println(packet.getAddress().getHostAddress());System.out.println(packet.getPort());System.out.println("--------------------------------------");}}
}

服务器端不用释放资源,因为它是常开的

允许多开配置:

在这里插入图片描述

四、TCP通信

特点:面向连接、可靠通信。

通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。

Java提供了一个java.net.Socket类来实现TCP通信。

在这里插入图片描述

TCP通信之-客户端开发

客户端程序就是通过java.net包下的Socket类来实现的。

构造器说明
public Socket(String host, int port)根据指定的服务器ip、端口号请求与服务器端建立连接,链接通过,就获得了客户端socket
方法说明
public OutputStream getOutputStream()获得字节输出流对象
public InputStream getInputStream()获得字节输入流对象

客户端实现步骤

①创建客户端的Socket对象,请求与服务端的连接。

②使用socket对象调用getOutputStream()方法得到字节输出流。

③使用字节输出流完成数据的发送。

④释放资源:关闭socket管道。

package com.itheima.d4_tcp1;import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;/***  目标:完成TCP通信快速入门-客户端开发:实现1发1收。*/
public class Client {public static void main(String[] args) throws Exception {// 1、创建Socket对象,并同时请求与服务端程序的连接。Socket socket = new Socket("127.0.0.1", 8888);// 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。OutputStream os = socket.getOutputStream();// 3、把低级的字节输出流包装成数据输出流DataOutputStream dos = new DataOutputStream(os);// 4、开始写数据出去了dos.writeUTF("在一起,好吗?");dos.close();socket.close(); // 释放连接资源}
}

TCP通信-服务端程序的开发

服务端是通过java.net包下的ServerSocket类来实现的。

构造器说明
public ServerSocket(int port)为服务端程序注册端口
方法说明
public Socket accept()阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。

服务端实现步骤

①创建ServerSocket对象,注册服务端端口。

②调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。

③通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。

④释放资源:关闭socket管道

package com.itheima.d4_tcp1;import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;/***  目标:完成TCP通信快速入门-服务端开发:实现1发1收。*/
public class Server {public static void main(String[] args) throws Exception {System.out.println("-----服务端启动成功-------");// 1、创建ServerSocket的对象,同时为服务端注册端口。ServerSocket serverSocket = new ServerSocket(8888);// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept();// 3、从socket通信管道中得到一个字节输入流。InputStream is = socket.getInputStream();// 4、把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);// 5、使用数据输入流读取客户端发送过来的消息String rs = dis.readUTF();System.out.println(rs);// 其实我们也可以获取客户端的IP地址System.out.println(socket.getRemoteSocketAddress());dis.close();socket.close();}
}

五、TCP通信-多发多收

使用TCP通信实现:多发多收消息。

①客户端使用死循环,让用户不断输入消息。

②服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息。

客户端代码如下:

package com.itheima.d5_tcp2;import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;/***  目标:完成TCP通信快速入门-客户端开发:实现客户端可以反复的发消息出去*/
public class Client {public static void main(String[] args) throws Exception {// 1、创建Socket对象,并同时请求与服务端程序的连接。Socket socket = new Socket("127.0.0.1", 8888);// 2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序。OutputStream os = socket.getOutputStream();// 3、把低级的字节输出流包装成数据输出流DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true) {System.out.println("请说:");String msg = sc.nextLine();// 一旦用户输入了exit,就退出客户端程序if("exit".equals(msg)){System.out.println("欢迎您下次光临!退出成功!");dos.close();socket.close();break;}// 4、开始写数据出去了dos.writeUTF(msg);dos.flush();}}
}

服务端代码如下:

package com.itheima.d5_tcp2;import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;/***  目标:完成TCP通信快速入门-服务端开发:实现服务端反复发消息*/
public class Server {public static void main(String[] args) throws Exception {System.out.println("-----服务端启动成功-------");// 1、创建ServerSocket的对象,同时为服务端注册端口。ServerSocket serverSocket = new ServerSocket(8888);// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept();// 3、从socket通信管道中得到一个字节输入流。InputStream is = socket.getInputStream();// 4、把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);while (true) {try {// 5、使用数据输入流读取客户端发送过来的消息String rs = dis.readUTF();System.out.println(rs);} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "离线了!");dis.close();socket.close();break;}}}
}

六、TCP通信-同时接收多个客户端

目前我们开发的服务端程序,是否可以支持与多个客户端同时通信 ?
不可以的。因为服务端现在只有一个主线程,只能处理一个客户端的消息。

为了让服务端能够支持多个客户端通信,就需要用到多线程技术。具体的实现思路如下图所示:每当有一个客户端连接服务端,在服务端这边就为Socket开启一条线程取执行读取数据的操作,来多少个客户端,就有多少条线程。用主线程接收客户端连接。按照这样的设计,服务端就可以支持多个客户端连接了。

在这里插入图片描述

按照这个思路,改写服务端的代码:

public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream();DataInputStream dis = new DataInputStream(is);while (true){try {String msg = dis.readUTF();System.out.println(msg);} catch (Exception e) {System.out.println("有人下线了:" + socket.getRemoteSocketAddress());dis.close();socket.close();break;}}} catch (Exception e) {e.printStackTrace();}}
}

接下来,再改写服务端的主程序代码,如下:

/***  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。*/
public class Server {public static void main(String[] args) throws Exception {System.out.println("-----服务端启动成功-------");// 1、创建ServerSocket的对象,同时为服务端注册端口。ServerSocket serverSocket = new ServerSocket(8888);while (true) {// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept();System.out.println("有人上线了:" + socket.getRemoteSocketAddress());// 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。new ServerReaderThread(socket).start();}}
}

七、TCP通信-综合案例(群聊)

把刚才的案例,改进成全能够实现群聊的效果,就是一个客户端发的消息,其他的每一个客户端都可以收到。

刚才我们写的多个客户端可以往服务端发现消息,但是客户端和客户端是不能直接通信的。想要试下全群聊的效果,我们还是必须要有服务端在中间做中转。 具体实现方案如下图所示:

我们可以在服务端创建一个存储Socket的集合,每当一个客户端连接服务端,就可以把客户端Socket存储起来;当一个客户端给服务端发消息时,再遍历集合通过每个Socket将消息再转发给其他客户端。

在这里插入图片描述

下面我们改造服务端代码,由于服务端读取数据是在线程类中完成的,所以我们改SerReaderThread类就可以了。服务端的主程序不用改。

public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream();DataInputStream dis = new DataInputStream(is);while (true){try {String msg = dis.readUTF();System.out.println(msg);// 把这个消息分发给全部客户端进行接收。sendMsgToAll(msg);} catch (Exception e) {System.out.println("有人下线了:" + socket.getRemoteSocketAddress());Server.onLineSockets.remove(socket);dis.close();socket.close();break;}}} catch (Exception e) {e.printStackTrace();}}private void sendMsgToAll(String msg) throws IOException {// 发送给全部在线的socket管道接收。for (Socket onLineSocket : Server.onLineSockets) {OutputStream os = onLineSocket.getOutputStream();DataOutputStream dos = new DataOutputStream(os);dos.writeUTF(msg);dos.flush();}}
}

八、BS架构程序(简易版)

前面我们所写的代码都是基于CS架构的。我们说网络编程还可以编写BS架构的程序,为了让同学们体验一下BS架构通信,这里我们写一个简易版的程序。仅仅只是体验下一,后期我们会详细学习BS架构的程序如何编写。

BS架构程序的实现原理,如下图所示:不需要开发客户端程序,此时浏览器就相当于是客户端,此时我们只需要写服务端程序就可以了。

在这里插入图片描述

在BS结构的程序中,浏览器和服务器通信是基于HTTP协议来完成的,浏览器给客户端发送数据需要按照HTTP协议规定好的数据格式发给服务端,服务端返回数据时也需要按照HTTP协议规定好的数据给是发给浏览器,只有这两双方才能完成一次数据交互。

客户端程序不需要我们编写(浏览器就是),所以我们只需要写服务端就可以了。

服务端给客户端响应数据的数据格式(HTTP协议规定数据格式)如下图所示:左图是数据格式,右图是示例。

在这里插入图片描述

接下来,我们写一个服务端程序按照右图示例的样子,给浏览器返回数据。注意:数据是由多行组成的,必须按照规定的格式来写。

8.1 服务端程序

先写一个线程类,用于按照HTTP协议的格式返回数据

public class ServerReaderThread extends Thread{private Socket socket;public ServerReaderThread(Socket socket){this.socket = socket;}@Overridepublic void run() {//  立即响应一个网页内容:“黑马程序员”给浏览器展示。try {OutputStream os = socket.getOutputStream();PrintStream ps = new PrintStream(os);ps.println("HTTP/1.1 200 OK");ps.println("Content-Type:text/html;charset=UTF-8");ps.println(); // 必须换行ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666<div>");ps.close();socket.close();} catch (Exception e) {e.printStackTrace();}}
}

再写服务端的主程序

/***  目标:完成TCP通信快速入门-服务端开发:要求实现与多个客户端同时通信。*/
public class Server {public static void main(String[] args) throws Exception {System.out.println("-----服务端启动成功-------");// 1、创建ServerSocket的对象,同时为服务端注册端口。ServerSocket serverSocket = new ServerSocket(8080);while (true) {// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept();System.out.println("有人上线了:" + socket.getRemoteSocketAddress());// 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。new ServerReaderThread(socket).start();}}
}

8.2 服务端主程序用线程池改进

为了避免服务端创建太多的线程,可以把服务端用线程池改进,提高服务端的性能。

先写一个给浏览器响应数据的线程任务

public class ServerReaderRunnable implements Runnable{private Socket socket;public ServerReaderRunnable(Socket socket){this.socket = socket;}@Overridepublic void run() {//  立即响应一个网页内容:“黑马程序员”给浏览器展示。try {OutputStream os = socket.getOutputStream();PrintStream ps = new PrintStream(os);ps.println("HTTP/1.1 200 OK");ps.println("Content-Type:text/html;charset=UTF-8");ps.println(); // 必须换行ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666<div>");ps.close();socket.close();} catch (Exception e) {e.printStackTrace();}}
}

再改写服务端的主程序,使用ThreadPoolExecutor创建一个线程池,每次接收到一个Socket就往线程池中提交任务就行。

public class Server {public static void main(String[] args) throws Exception {System.out.println("-----服务端启动成功-------");// 1、创建ServerSocket的对象,同时为服务端注册端口。ServerSocket serverSocket = new ServerSocket(8080);// 创建出一个线程池,负责处理通信管道的任务。ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS,new ArrayBlockingQueue<>(8) , Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());while (true) {// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求Socket socket = serverSocket.accept();// 3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。pool.execute(new ServerReaderRunnable(socket));}}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 远程连接工具连接不上虚拟机的解决方式(Xshell/Windterm...)
  • ECRS工时分析软件:工业工程精益生产的智慧引擎
  • 日本央行首次加息,同时缩减国债购买规模
  • 当服务器出现蓝屏时有哪些应对措施呢?
  • 智慧宿舍中的楼层管理
  • 图论之最短路径问题(朴素Dijksra算法\堆优化版Dijksra算法\Bellman-Ford\SPFA)
  • 超详细的MySQL CRUD 并配备了大量的测试用例, 包教包会
  • magic-api-server通过classpath增加数据库驱动
  • 【C++/STL】:哈希的应用 -- 位图布隆过滤器
  • 链表的实现(C++版)
  • uniapp小程序登录问题
  • QT--线程
  • 【Vue3】标签的 ref 属性
  • linux每个目录都是干啥的???linux目录说明
  • 11. 计算机网络TCP三次握手
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 【EOS】Cleos基础
  • 【跃迁之路】【669天】程序员高效学习方法论探索系列(实验阶段426-2018.12.13)...
  • crontab执行失败的多种原因
  • JAVA并发编程--1.基础概念
  • Java的Interrupt与线程中断
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Python语法速览与机器学习开发环境搭建
  • Redis的resp协议
  • SpiderData 2019年2月23日 DApp数据排行榜
  • SpiderData 2019年2月25日 DApp数据排行榜
  • spring security oauth2 password授权模式
  • SpringBoot 实战 (三) | 配置文件详解
  • Vue小说阅读器(仿追书神器)
  • vue总结
  • Yii源码解读-服务定位器(Service Locator)
  • 开发了一款写作软件(OSX,Windows),附带Electron开发指南
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 聊聊sentinel的DegradeSlot
  • 前端学习笔记之观察者模式
  • 浅谈web中前端模板引擎的使用
  • 如何编写一个可升级的智能合约
  • 如何学习JavaEE,项目又该如何做?
  • ​ssh免密码登录设置及问题总结
  • #ifdef 的技巧用法
  • (8)STL算法之替换
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (三)docker:Dockerfile构建容器运行jar包
  • (十六)视图变换 正交投影 透视投影
  • (十三)Flask之特殊装饰器详解
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (一)SpringBoot3---尚硅谷总结
  • (转)树状数组
  • ***linux下安装xampp,XAMPP目录结构(阿里云安装xampp)
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .net 生成二级域名
  • .net知识和学习方法系列(二十一)CLR-枚举
  • /dev/sda2 is mounted; will not make a filesystem here!
  • @EnableWebMvc介绍和使用详细demo