🐱👓Java网络编程🐱🚀
1 网络编程基本概念
1、什么是计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大,功能强的网络系统,从而使众多的计算机可以方便地互相传通信息,共享硬件、软件、数据信息等资源。
2、计算机网络的主要功能
- 资源共享
- 信息传输与集中处理
- 均衡负荷与分布处理
- 综合信息服务(www/综合业务数字网络ISDN)等
3、网络通信协议
要使计算机连成的网络能够互通信息,需要对数据传输速率、传输代码、代码结构、传输控制步骤、出错控制等制定一组标准,这一组共同遵守的通信标准就是网络通信协议,不同的计算机之间必须使用相同的通讯协议才能进行通信。
4、网络通信接口
为了使两个结点之间能进行对话,必须在它们之间建立通信工具(即接口),使彼此之间能进行信息交换。接口包括两部分:
- 硬件装置:实现结点之间的信息传送(网线、路由器、Hub)
- 软件装置:规定双方进行通信的约定协议
5、TCP/IP
- TCP/IP:传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是Internet最基本的协议、Internet国际互联网络的基础,简单地说,就是由网络层的IP协议和传输层的TCP协议组成的。
- IP地址:网络中每台计算机的一个标识号
- 本地IP:127.0.0.1 localhost
- 端口号(PORT):端口号的范围:065535之间,01023之间的端口数是用于一些知名的网络服务和应用。
网络七层协议(OSI模型):
1物理层—>2数据链路层—>3网络层—>4传输层—>5会话层—
—>6表示层—>7应用层
网络四层协议(TCP/IP模型):
链路层(1-2)—>网络层(3)—>传输层(4)—>应用层(5-7)
6、程序开发结构
-
网络编程主要是指完成C/S程序的开发,程序的开发结构有两种:
- C/S(客户端/服务器)
开发两套程序,两套程序需要同时维护,例如:QQ。CS程序一般比较稳定。 - B/S(浏览器/服务器)
开发一套程序,客户端使用浏览器进行访问,例如,各个论坛。BS程序一般稳定性较差,而且安全性较差。但是,C/S的程序开发在实际的Java应用中毕竟很少了,而且整个java基本上都是以B/S为主。
- C/S(客户端/服务器)
-
C/S程序主要可以完成以下两种程序的开发:
- TCP:(Transmission Control Protocol)传输控制协议,采用三方握手的方式,保证准确的连接操作。
- UDP:(User Datagram Protocol)数据报协议,发送数据报,例如,手机短信或者是QQ消息。
- TCP、UDP的数据顿格式简单图例:(其中协议类型用于区分TCP、UDP)
协议类型 源IP 目标IP 源端口 目标端口 帧序号 帧数据
2 网络编程TCP协议
1、TCP程序概述
通过Socket来实现网络编程,Socket是网络驱动层提供给应用程序编程的接口和一种访问机制
TCP是一个可靠的协议,面向连接的协议。
实现TCP程序,需要编写服务器端和客户端,Java API为我们提供了java.net
包,为实现网络应用程序提供类。
ServerSocket
:此类实现服务器套接字。
Socket
:此类实现客户端套接字(也可以就叫“套接字”)。
Socket
是网络驱动层提供给应用程序编程的接口和一种机制。(快递员)
(编程—>Socket—>驱动—>网卡—>网线传输)
2、数据发送过程
3、数据接收过程
4、实现服务器端与客户端程序
-
服务器端:
public class ServerSocket extends Object
:此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。ServerSocket(int port)
:创建绑定到特定端口的服务器套接字。void setSoTimebut(int timeout)
:通过指定超时值启用/禁用SO_TIMEOUT,以毫秒为单位。InetAddress getlnetAddress()
:返回此服务器套接字的本地地址。Socket accept()
:侦听并接受到此套接字的连接。
-
客户端:
public class Socket extends Object
:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。Socket(String host, int port)
:创建一个流套接字并将其连接到指定主机上的指定端口号。InputStream getlnputStream()
:返回此套接字的输入流。OutputStream getOutputStream()
:返回此套接字的输出流。void setSoTimeout(int timeout)
:启用/禁用带有指定超时值的
SO_TIMEOUT,以毫秒为单位。
3 TCP实现ECHO程序
Echo,意为应答,程序的功能是客户端向服务器发送一个字符串,服务器不做任何处理,直接把字符串返回给客户端,Echo程序是最为基本的客户/服务器程序。
代码实现:
package com.huang.TestSocket.EchoDemo;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* description:服务器端
*/
public class EchoSeverSocket {
public static void main(String[] args) {
//创建一个服务器端的Socket(1024-65535)
try {
ServerSocket server = new ServerSocket(6666);
System.out.println("服务器已启动,正在等待客户端连接···");
//等待客户端的连接,造成阻塞,如果有客户端连接成功,立即返回一个Socket对象
Socket socket = server.accept();//字符流
System.out.println("客户端连接成功~"+server.getInetAddress().getHostAddress());
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
//通过输入流读取网络数据
String info = br.readLine();
System.out.println(info);
//获取输出流,向客户端返回消息
PrintStream ps = new PrintStream(
new BufferedOutputStream(socket.getOutputStream())
);
ps.println("echo: "+info);
ps.flush();//有BufferedStream类,需要刷新
//关闭
ps.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.huang.TestSocket.EchoDemo;
import java.io.*;
import java.net.Socket;
/**
* description:客户端
*/
public class EchoSocket {
public static void main(String[] args) {
//创建一个Socket对象,指定要连接的服务器
try {//"127.0.0.1"可替换成"localhost"
Socket socket = new Socket("127.0.0.1", 6666);
//获取socket的输入输出流
PrintStream ps = new PrintStream(
new BufferedOutputStream(socket.getOutputStream()));
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
ps.println("hello, welcome to here!");
ps.flush();
//读取服务器端返回的数据
String info = br.readLine();
System.out.println(info);
//关闭
ps.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4 服务器与多客户端通信
服务器端通过加入线程池来处理多个客户端请求,简单的设置线程数可以与CPU核数匹配,过多的线程数空闲会消耗服务器资源
要想服务器同时支持多个客户端的连接,就必须加入多线程的处理机制,将每一个连接的客户端都创建一个新的线程对象。
代码实现:
package com.huang.TestSocket.EchoDemo;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* description:服务器与多客户端通信
* 服务前端
*/
public class MutilServerDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(3);
try {
ServerSocket server = new ServerSocket(6666);
System.out.println("服务器已启动,正在等待连接···");
while(true){
Socket s = server.accept();
System.out.println(server.getInetAddress().getHostAddress());
es.execute(new UserThread(s));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class UserThread implements Runnable{
private Socket s;
public UserThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintStream ps = new PrintStream(new BufferedOutputStream(s.getOutputStream()));
String info = br.readLine();
System.out.println(info);
ps.println("echo:"+info);
ps.flush();
ps.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.huang.TestSocket.EchoDemo;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* description:服务器与多客户端通信
* 多客户端
*/
public class MutilClientDemo {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//创建一个Socket对象,指定要连接的服务器
try {//"127.0.0.1"可替换成"localhost"
Socket socket = new Socket("127.0.0.1", 6666);
//获取socket的输入输出流
PrintStream ps = new PrintStream(
new BufferedOutputStream(socket.getOutputStream()));
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
System.out.println("请输入:");
String info = input.nextLine();
ps.println(info);
ps.flush();
//读取服务器端返回的数据
info = br.readLine();
System.out.println(info);
//关闭
ps.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5 多客户之间的通信
多客户端之间的通信,使用服务器作为中转消息站,这是把消息集中处理的解决方案
- 服务器可以与多个客户端实现通信了,那我们真正的目的是要实现多介客户端之间的通信,
- 使用TCP协议实现的方案是:客户端的数据包通过服务器中转,发送到另一个客户端,如下图所示。
客户:数据包包括:我是谁、他是谁、消息是什么、消息类型
服务器通过消息类型来判断发送消息的客户端是什么意图,消息类型是我们自定义的标记
代码实现:
//------------------------消息类型定义-------------------------------------
package com.huang.TestSocket.communication;
/**
* description:消息类型
*/
public class MessageType {
public static final int TYPE_LOGIN = 0x1;//登录消息类型
public static final int TYPE_SEND = 0x2;//发送消息类型
}
//------------------------消息包-------------------------------------
package com.huang.TestSocket.communication;
import java.io.Serializable;
/**
* description:消息包
*/
public class Message implements Serializable {//继承可序列化的接口
private String from;//发送者
private String to;//接收者
private int type;//消息类型
private String info;//消息
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "Message{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", type=" + type +
", info='" + info + '\'' +
'}';
}
public Message() {
}
public Message(String from, String to, int type, String info) {
this.from = from;
this.to = to;
this.type = type;
this.info = info;
}
}
//------------------------服务器端程序-------------------------------------
package com.huang.TestSocket.communication;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* description:服务器端
*/
public class Server {
public static void main(String[] args) {
Vector<UserThread> vector = new Vector<>();
ExecutorService es = Executors.newFixedThreadPool(5);
//创建服务器端的Socket
try {
ServerSocket server = new ServerSocket(8888);
System.out.println("服务器已启动,正在等待连接···");
while (true) {
Socket socket = server.accept();
UserThread user = new UserThread(socket, vector);
es.execute(user);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 客户端处理的线程
*/
class UserThread implements Runnable {
private String name;//客户端的用户名称(唯一)
private Socket socket;
private Vector<UserThread> vector;//客户端处理线程的集合
private ObjectInputStream ois;
private ObjectOutputStream oos;
private boolean flag = true;
public UserThread(Socket socket, Vector<UserThread> vector) {
this.socket = socket;
this.vector = vector;
vector.add(this);
}
@Override
public void run() {
try {
System.out.println("客户端:" + socket.getInetAddress().getHostAddress() + "已连接");
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
while (flag) {
//读取消息对象
Message msg = (Message) ois.readObject();
int type = msg.getType();
switch (type) {
case MessageType.TYPE_LOGIN:
name = msg.getFrom();
msg.setInfo("欢迎你:");
oos.writeObject(msg);
break;
case MessageType.TYPE_SEND:
String to = msg.getTo();
UserThread ut;
int size = vector.size();
for (int i = 0; i < size; i++) {
ut = vector.get(i);
if(to.equals(ut.name) && ut != this){
ut.oos.writeObject(msg);
break;
}
}
break;
}
}
ois.close();
oos.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
//-----------------------客户端程序--------------------------------------
package com.huang.TestSocket.communication;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* description:客户端
*/
public class Client {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
ExecutorService es = Executors.newSingleThreadExecutor();
try {
Socket socket = new Socket("localhost", 8888);
System.out.println("服务器连接成功!");
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//向服务器发送登录信息
System.out.println("请输入名称:");
String name = input.nextLine();
Message msg = new Message(name, null, MessageType.TYPE_LOGIN, null);
oos.writeObject(msg);
msg = (Message) ois.readObject();
System.out.println(msg.getInfo() + msg.getFrom());
//启动读取消息的线程
es.execute(new ReadInfoThread(ois));
//使用主线程来实现发送消息
boolean flag = true;
while (flag){
msg = new Message();
System.out.println("To:");
msg.setTo(input.nextLine());
msg.setFrom(name);
msg.setType(MessageType.TYPE_SEND);
System.out.println("Info:");
msg.setInfo(input.nextLine());
oos.writeObject(msg);
}
} catch (IOException |ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class ReadInfoThread implements Runnable {
private ObjectInputStream in;
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
public ReadInfoThread(ObjectInputStream in) {
this.in = in;
}
@Override
public void run() {
try {
while (flag) {
Message message = (Message) in.readObject();
System.out.println("[" + message.getFrom() + "]对我说:" + message.getInfo());
}
if(in != null){
in.close();
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
6 网络编程UDP协议
UDP是无连接的,减少了开销和发送数据之前的延时,不保证可靠,UDP的报头长度要小于TCP的报头长度,例如QQ文件传输、pplive等都是使用UDP协议
1、UDP协议概述
UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的,每个被传输的数据报必须限定在64KB之内。
主要使用以下的两个类:
DatagramPacket
:此类表示数据报包。DatagramSocket
:此类表示用来发送和接收数据报包的套接字
2、UDP服务器与客户端程序
-
服务器端:
String info ="..."; //将信息封装成数据报 byte[]bytes = info.getBytes(); DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length,InetAddress.getByName("localhost"),5000);//客户端在5000端口监听 DatagramSocket server =newDatagramSocket(3000);//服务器的端口 server.send(dp);//发送数据报 server.close();
-
客户端:
byte b[]= new byte[1024];//接收内容 DatagramPacket dp = new DatagramPacket(b,b.length);//接收内容 //客户端在5000端口等待 DatagramSocket client = new DatagramSocket(5000); client.receive(dp);//接收内容 System.out.println(new String(dp.getData(),0,dp.getLength()); client.close();
代码实例:
//----------------------UDP服务器端-----------------------------------------
package com.huang.TestSocket.UDPDemo;
import java.io.IOException;
import java.net.*;
/**
* description:UDP 服务器端
* buf - 分组数据。
* offset - 分组数据偏移量。
* length - 分组数据长度。
* address - 目的地址。
* port - 目标端口号。
*/
public class UDPServer {
public static void main(String[] args) {
String info = "good good 学习,天天 up";
byte[] bytes = info.getBytes();
try {
//本程序端口,建立一个socket
DatagramSocket socket = new DatagramSocket(9000);
//建立一个包,数据|数据的长度起始|要发送给谁
DatagramPacket dp = new DatagramPacket(bytes, 0,
bytes.length, InetAddress.getByName("127.0.0.1"),8000);
//发送包
socket.send(dp);
//关闭流
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//----------------------UDP客户端-----------------------------------------
package com.huang.TestSocket.UDPDemo;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* description:
*/
public class UDPClient {
public static void main(String[] args) {
try {
//开放端口
DatagramSocket socket = new DatagramSocket(8000);
System.out.println("正在接收数据中···");
//接收数据包
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
socket.receive(dp);//阻塞接收
String s = new String(dp.getData(),0,dp.getLength());
System.out.println(s);
//关闭连接
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7 URL
URL是统一资源定位符,是互联网上标准资源的地址。而互联网上的每个文件都有唯一的一个的URL。
URL(uniform resource location)类URL代表一个统一资源定位符,它是指向互联网“资源”的指针。
抽象类URLConnection
是所有类的超类,它代表应用程序和URL之间的通信链接。
代码实例:
package com.huang.TestSocket.URLDemo;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* description:
*/
public class URLTest {
public static void main(String[] args) {
try {
URL url = new URL("https://cn.bing.com/images/search?view=detailV2&ccid=Zxtf2X2E&id=5EBF03931E73C97D339F2A6B573F0A599AEA63C1&thid=OIP.Zxtf2X2EddV-g7hKyBhilAHaQB&mediaurl=https%3a%2f%2fimg.96an.com%2fcaiji%2f2020%2f08%2ffrc-838d60f8e2020f11751960d11e7714c7.jpeg&exph=2337&expw=1080&q=%e5%9b%be%e7%89%87&simid=608019115722554400&FORM=IRPRST&ck=A1E27930D3373E10FB67AD262AEC4C1F&selectedIndex=23&ajaxhist=0&ajaxserp=0");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedInputStream in = new BufferedInputStream(conn.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("H:\\JAVA\\3.Java提升篇\\网络编程图片\\bizhi.jpg"));
byte[] bytes = new byte[1024];
int len = -1;
while((len = in.read(bytes)) != -1){
out.write(bytes,0,len);
out.flush();
}
in.close();
out.close();
System.out.println("下载成功!");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8 MINA框架
Mina框架可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina提供了事件驱动、异步操作的编程模型,默认使用NIO作为底层支持。
1、什么是MINA?一句话就是:一个简洁易用的基于TCP/IP通信的JAVA框架。
2、[mina包下载地址:](MINA Downloads — Apache MINA)
3、一个简单的网络程序需要的最少jar包:mina-core-2.0.16.jar,slf4j-api-1.7.21.jar
4、开发一个Mina应用,简单的说,就是创建连结,设定过滤规则,编写自己的消息处理器
5、示例:
//创建一个非阻塞的Server端Socket,用NIO
SocketAcceptor acceptor = new NioSocketAcceptor();//创建接收数据的过滤器
DefaultloFilterChainBuilder chain = acceptor.getFilterChain();
//设定这个过滤器将一行一行(Vr/n)的读取数据
chain.addLast("myChin",new ProtocolCodecFilter(new TextLineCodecFactory()));
//设定服务器端的消息处理器:一个SampleMinaServerHandler对象
acceptor.setHandler(new SampleMinaServerHandler());
int bindPort = 9999;
//绑定端口,启动服务器
try{acceptor.bind(new InetSocketAddress(bindPort));}catch(IOException e){(e.printStackTrace();}
System.out.println("Mina Server is Listing on:="+ bindPort);