文章目录
文章目录
前言
Java中的网络编程已经是属于JavaSE部分的尾声内容了,这一周剩下的还有一个反射的内容。都属于理论性与操作性较强的部分,如果内容错误欢迎留言指点
以下是本篇文章正文内容,下面案例可供参考
一、什么是网络编程
(1)计算机网络
内容:所谓计算机网络,是指将空间上具有独立功能的多台计算机以及它的外部设备(例如路由器等),通过通信线路连接起来,在网络操作系统,网络管理软件以及网络通信协议的管理下,实现资源共享和信息传递的计算机系统。
(2)网络编程
实现多态计算机之间数据共享与传递,网络应用程序主要组成是:网络编程+IO流+多线程
(3) 网络三要素与三大协议
3.1网络三要素
网络通讯模型:请求(客户端)—》响应请求(服务端)
内容:网络三大要素为:IP地址 、 端口号(port)、 网络协议(数据传输的规则)
IP地址:网络中计算机的唯一标识(IP地址是一个32位的二进制数据,为了方便,将一个字节的二进制转换为一个十进制的数据),IP地址的组成=网络号段+主机段(IPV4是32位,IPV6是128位,我们的计算机通常是IPV4,本机的IP地址都是“127.0.0.1”);
端口号:每一个网络程序都至少一个端口号,这是用于标示进程的逻辑地址,不同的地址其端口号标示不同,有效端口号范围:0 ~ 65535,其中0 ~ 1024是系统使用或保留端口
网络协议:都是数据传输的规则,TCP 、UDP都是传输层的协议(后面会详细介绍)
3.2网络三大协议
TCP:一种面向连接的协议,其稳定性比较好,通过客户端与服务端之间的Socket进行交流访问。是一种基于字节流的传输层协议,其建立过程需要通过三次握手与四次挥手结束。
UDP:面向无连接的协议,有一定的缺陷,如果用来传送数据包可能会造成数据丢失,是一种不是很负责的协议,但是其速度较快 可用于视频通话上。传输的每个包的大小为64KB。
HTTP:超文本传输协议是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。
(3)套接字“Socket”的使用
套接字的内容:如果要具体讲的话他就有点烧脑了,我们就简单提一下它的功能,Socket是将网络三大要素中的 IP 和 端口号 打包成了一个内容使得计算机之间的交流更加简单便利,Scoket也叫套接字,其表示的是IP地址和端口号的组合。网络编程主要就是指Socket编程,网络间的通信其实就是Socket间的通信,数据就通过IO流在两个Scoket间进行传递。
二、三大协议的使用步骤与操作案例
(1)TCP协议(面向连接的协议,可靠性更高)
内容:三次握手,四次挥手类似于打电话
理解图如下:
(2)UDP协议(无连接的协议)
内容:单方面发送消息数据包,类似于发快递。
特点:容易造成数据丢失,故此常应用于视频聊天,相比较于TCP它更显得不负责任,此外UDP没有服务端,只有客户端即直接行驶的是用户之间的交流没有服务端的介入。
(3)TCP VS UDP
(4)HTTP协议
三、实战案例:实现不同用户之间的聊天
(1)TCP案例部分:
1.1一对一聊天
说明:实现一对一聊天需要用到线程的知识,为了能够使得客户端与服务端之间的交流能够顺利创建一个接收的线程
客户端(Client)代码如下:
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
/**
* 需求:优化一对一聊天
*/
public static void main(String[] args) throws Exception {
Socket socket=new Socket("127.0.0.1",8080);
Scanner input=new Scanner(System.in);
new ReceiveThread(socket).start();
PrintStream ps=new PrintStream(socket.getOutputStream());
while (true){
ps.println("雍仲:"+input.next());
}
}
}
服务端(Server):
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public static void main(String[] args) throws Exception {
//创建服务端
ServerSocket serverSocket = new ServerSocket(8080);
Socket socket=serverSocket.accept();//由服务端创建一个 服务于客户端的Socket
Scanner input=new Scanner(System.in);
//向服务端发送消息
new ReceiveThread(socket).start();
PrintStream ps=new PrintStream(socket.getOutputStream());
while (true){
ps.println("中国人寿保险:"+input.next());
}
}
}
线程接受器
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ReceiveThread extends Thread{
private Socket socket;
public ReceiveThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader bufferedReader=null;
try {
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
while (true){
String readLine=null;
try {
readLine=bufferedReader.readLine();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(readLine);
}
}
}
1.2实现文件的传输(不正经系列)
这个案例的要求的实现主要是依靠对输入流以及输出流的熟练使用,以及通过对网络编程Socket的熟练使用
Client客户端
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
/**
* 需求:传输文件
*/
public static void main(String[] args) throws Exception {
Socket socket=new Socket("127.0.0.1",8080);
//1、创建流对象
FileInputStream fis=new FileInputStream("明日花绮罗.jpg");
OutputStream out=socket.getOutputStream();//将该内容发送到服务端 127.0.0.1
//2、读写数据
int len;
byte [] bs=new byte[1024];
while ((len=fis.read(bs))!=-1){
out.write(bs,0,len);
}
//3、关闭流对象
fis.close();
out.close();
}
}
Server服务端类:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket=new ServerSocket(8080);
Socket socket=serverSocket.accept();//接收客户端的消息的Socket
//1、创建流对象
InputStream is=socket.getInputStream();//利用服务端的Socket来进行接受客户端的传输内容,相当于 这代码就是客户端中的FileInputStream fis=new FileInputStream("明日花绮罗.jpg")
OutputStream os=new FileOutputStream("download.jpg");
//2、读写数据
int len;
byte [] bs=new byte[1024];
while ((len=is.read(bs))!=-1){
os.write(bs,0,len);
}
//3、关闭流对象
os.close();
is.close();
}
}
结果展示:
1.3多人之间聊天
说明 :对于实现多人聊天,我们需要注意的一点就是在我们不知道到底有多少个用户在聊天的时候我们就无法确定有多少个客户端Socket,因此就不能在服务端中明确创建多少个服务端的Socket来进行对象之间的匹配,因此我们需要用到多线程,而谈及多线程的话明显是要实现多个用户的交流,而对于这一部分我们作为服务器的控制者,那么我们就需要收集到所有用户的IP地址和Socket来进行他们之间的消息收集和发送到其余的用户那儿去故此 我们需要用到一个Map,而这个Map就是多线程下的安全性最高的ConcurrenHashMap。
创建用户端类(Client)
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
/**
* 需求 :实现群聊
*/
public static void main(String[] args) throws Exception{
Socket socket=new Socket("127.0.0.1",8080);
Scanner input=new Scanner(System.in);
new ReceiveThread(socket).start();
PrintStream ps=new PrintStream(socket.getOutputStream());
while (true){
ps.println("雍仲杨:"+input.next());
}
}
}
创建服务端类(Server)
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;
public class Server {
//这里设置一个静态公共Map用来存放所有的客户端的Socket与其ip地址
public static ConcurrentHashMap<InetAddress,Socket> map=new ConcurrentHashMap<>();
public static void main(String[] args) throws Exception {
ServerSocket serverSocket=new ServerSocket(8080);
//由于是多个用户之间的聊天 因此在不确定有多少个用户的时候就无法知道有多少个用户的Socket,故此我们选择用一个死循环来创建服务端的SOcket来匹配与值数量的用户Socket
while (true){
Socket socket=serverSocket.accept();
//获取客户端的IP地址
InetAddress ip=socket.getInetAddress();
//存放IP与对应的Socket
map.put(ip,socket);
//创建线程并启动线程
new ServerThread(socket).start();
}
}
}
创建接受线程类(ReceiveThread)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class ReceiveThread extends Thread{
private Socket socket;
public ReceiveThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
while (true){
String readLine=null;
try {
readLine=br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(readLine);
}
}
}
创建服务线程类(ServerThread)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//接收当前Socket的消息
BufferedReader bufferedReader = null;
try {
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
while (true){
String readLine=null;
try {
readLine =bufferedReader.readLine();
System.out.println(readLine);
//发送给其他的Socket
ConcurrentHashMap<InetAddress,Socket> map=Server.map;
Set<Entry<InetAddress,Socket>> entrySet=map.entrySet();
for (Entry<InetAddress,Socket> entry:entrySet) {
InetAddress key=entry.getKey();
Socket value=entry.getValue();
if (socket.getInetAddress()!=key){//这句代码的意思是,如果当前的key不等于当前的socket那么说明就是等于其余的socket
PrintStream ps=new PrintStream(value.getOutputStream());
ps.println(readLine);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)UDP部分案例:
2.1通过用户之间的发送快递的形式来解释
创建第一个用户端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Client01 {
public static void main(String[] args) throws IOException {
/**
* 知识点:UDP协议
*/
//发送者所在城市的快递公司,发送者不需要写端口号
DatagramSocket socket=new DatagramSocket();
//发送礼物者发快递
//礼物
byte [] bs="鲜花一束~~~".getBytes();
//数据包--快递包裹
DatagramPacket packet=new DatagramPacket(bs,bs.length, InetAddress.getByName("127.0.0.1"),8080);
//发送快递
socket.send(packet);
//4、接受 用户端2 的礼物
byte [] buf=new byte[1024];
packet=new DatagramPacket(buf, buf.length);
socket.receive(packet);
System.out.println(new String(buf).trim());
}
}
创建用户端2
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Client02 {
public static void main(String[] args) throws IOException {
//接受礼物者
//接收者所在地的快递公司
DatagramSocket socket=new DatagramSocket();
//接受发送者的礼物
byte [] bs=new byte[1024];
DatagramPacket packet=new DatagramPacket(bs,bs.length);
socket.receive(packet);
System.out.println(new String(bs).trim());
//3、给 用户端01 回礼
byte [] buf="典藏版黑金卡".getBytes();
packet=new DatagramPacket(buf, buf.length,packet.getSocketAddress());
socket.send(packet);
}
}
(3)HTTP部分案例
Http部分案例我通过一个查询快递的接口来展示 具体的注释我写在了项目中
查询快递:需要获取网络接口URL查询快递的位置等信息,其中需要用到网络接口对象(URL)与连接对象HttpURLConnection以及后面的设置和获取响应码等
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class Test01 {
@SuppressWarnings("all")
public static void main(String[] args) throws IOException {
/**
* 知识点:HTTP
* 需求:查询快递
*/
//获取网络接口(这个可以在网上查到,但是不是所有的接口都能用)
String urlStr="http://www.kuaidi100.com/query?type=yunda&postid=4316645130015";
//创建网络接口的对象
URL url =new URL(urlStr);
//获取HttpURLConnection对象
HttpURLConnection conn= (HttpURLConnection) url.openConnection();
//设置
conn.setReadTimeout(5000);//设置阅读超时时间
conn.setRequestMethod("GET");//设置连接方式,我目前所知道的一共有两种“GET"与"POST"
conn.setConnectTimeout(5000);//设置连接超时时间
//获取响应的状态码
int responseCode=conn.getResponseCode();
if (responseCode==200){
//获取响应中的数据
BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8"));
char [] cs=new char[1024];
int len;
while ((len=br.read(cs))!=-1){
System.out.println(new String(cs,0,len));
}
}else if (responseCode==404){
System.out.println("页面丢失");
}
}
}
通过Http协议来传送网络上的资源到电脑上。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Test02 {
public static void main(String[] args)throws Exception {
/**
* 知识点:使用HTTP协议下载网络资源
*/
//1、获取资源地址接口
String str="https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180219%2F60a5778a47384ea9bc673be94a7f646a.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632019852&t=38c341554f5189d54f57c949f2de9fb9";
//2、获取网络连接对象
URL url=new URL(str);
//3、获取HttpConnection对象
HttpURLConnection connection= (HttpURLConnection) url.openConnection();
//4、设置各种状态与获取状态码
connection.setRequestMethod("GET");
connection.setReadTimeout(5000);//设置五秒的阅读有效时间
connection.setConnectTimeout(5000);//设置连接时间
//5、获取动态码
int responseCode=connection.getResponseCode();
//6、判断各种情况
if (responseCode == 200){
BufferedInputStream bis=new BufferedInputStream(connection.getInputStream());//通过输入流获取网络连接内容
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("AVgirl.jpg"));
int len;
byte [] bs=new byte[1024];
while ((len=bis.read(bs))!=-1){
bos.write(bs,0,len);
}
}else if (responseCode==404){
System.out.println("页面查找失败");
}
}
}
总结
本文主要是通过一些基本理论+实战场景来进行梳理的,内容还不完整,后期我会慢慢进行填补,若所写内容之中有错误请指正。