java网络编程的介绍
主要讲解网络编程的实现步骤。基于控制台的聊天室小项目是为了加深对网络编程的理解。学网络编程要对java线程和IO的基本操作有一定的了解。不了解的可以先看下面的两篇文章
用代码介绍多线程(java基础篇).
用代码介绍java的-io常见类.
本文目录
现在直接上代码
InetAddress
代码说明其作用
public class Main {
public static void main(String[] args) {
try {
//获取本机对象
InetAddress localHost = InetAddress.getLocalHost();
//获取本机的ip地址
String address = localHost.getHostAddress();
//获取本机名
String hostName = localHost.getHostName();
//根据域名获取信息
InetAddress baiduHost = InetAddress.getByName("www.baidu.com");
//百度的ip地址
String baiduAddress = baiduHost.getHostAddress();
//百度的域名
String baiduName = baiduHost.getHostName();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
InetSocketAddress
InetSocketAddress包含了InetAddress。InetAddress不能指定端口号。ip地址只是指定主机,并没有详细到某个应用(app)上。端口是同一台主机是不同应用的区分。端口范围0-65535。
import java.net.InetAddress;
import java.net.InetSocketAddress;
public class Main {
public static void main(String[] args) {
//获取InetSocketAddress对象。域名加端口号。指定的地址详细了
InetSocketAddress localhost = new InetSocketAddress("www.baidu.com",80);
//获取到了InetAddress对象。可对照上一标题InetAddress的介绍
InetAddress inetAddress = localhost.getAddress();
//inetAddress不能获取端口
int port = localhost.getPort();
System.out.println(port);
//结果
//80
//这里是构造方法指定的。
}
}
URL的介绍
每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL。看代码了解一下URL这个类
import java.net.MalformedURLException;
import java.net.URL;
public class Main {
public static void main(String[] args) {
try {
URL url = new URL("http://www.baidu.com:80/index.html?uname=shsxt&age=18#a");
//获取协议
String protocol = url.getProtocol();
System.out.println("协议:"+protocol);
//域名
String host = url.getHost();
System.out.println("域名:"+host);
//获取资源路径包括请求参数
System.out.println("请求资源1:"+url.getFile());
//仅仅获取路径不包括请求参数
System.out.println("请求资源2:"+url.getPath());
//参数
System.out.println("参数:"+url.getQuery());
//可以看出url.getpath()+url.getQuery()=url.getFile
//锚点
System.out.println("锚点:"+url.getRef());
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过URL下载对应的资源(爬虫的必备技能)
普通的下载,当网上设了反爬虫会失效
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
public class Main {
public static void main(String[] args) {
try {
//网上资源地址.这里是图片
URL url = new URL("http://img.coocaa.com/www/attachment/forum/201602/16/085938u86ewu4l8z6flr6w.jpg");
//下载
InputStream inputStream = url.openStream();
//输出到本地。这是IO的知识点。可以看开头推荐的文章
FileOutputStream out = new FileOutputStream("src/url.jpg");
byte[] bytes = new byte[1024];
int len=-1;
while ((len=inputStream.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过URL下载对应的资源(简单破解一下反爬虫)
这里只是简单的提一下。加深对URL的理解。重点不是讲解爬虫。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
public static void main(String[] args) {
try {
//https://www.dianping.com有反爬虫。通过上面的方法会报错
URL url = new URL("https://www.dianping.com");
//获取连接
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//请求方式。get或post。
con.setRequestMethod("GET");
//这里是去浏览器访问。按F12选netWork,点击www.dianping.com找到User-Agent(如下图)
con.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36");
//获取输入流
InputStream inputStream = con.getInputStream();
BufferedInputStream in = new BufferedInputStream(inputStream);
//输出。这里下载的是一个网页
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/dianping.html"));
byte[] bytes = new byte[1024];
int len;
while ((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
获取setRequestProperty参数值
UDP的介绍
Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。UDP协议要求包小于64K。属于不可靠传输。优点效率高
用UDP实现一对一聊天。(半成品,基础较好的可跳)
发起端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.Scanner;
public class UdpClient {
public static void main(String[] args) {
System.out.println("发起端");
try {
while(true){
//============发送端==============================
// 使用DatagramSocket 指定端口 创建当前应用
DatagramSocket client = new DatagramSocket(8081);
//输入要发送的内容
Scanner scanner = new Scanner(System.in);
String next = scanner.next();
//转成字节数组
//只能传字节数组。如果想传一个对象过去。DataOutputStream+ByteArrayOutputStream去实现。可观看本文开头推荐的用代码介绍java的-io常见类
byte[] bytes = next.getBytes();
// 封装成DatagramPacket 包裹,需要指定目的地.使用InetSocketAddress指定地址(发送到哪里去)
//注意文件不能超过64K
//8080是接收方的端口
//"localhost"是接端的域名或ip地址
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,new InetSocketAddress("192.168.142.1",8080));
//开始发送
client.send(packet);
//====================接收端=====================
//接收消息
byte[] bytes1 = new byte[1024];
//以包的形式接收
DatagramPacket packet1 = new DatagramPacket(bytes1, 0, bytes1.length);
//接收(当有人发消息来是执行)
client.receive(packet1);
//通过包获取字节数组
byte[] data = packet1.getData();
//输出收到的信息
System.out.println(new String(data));
//释放资源
client.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
接收端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.Scanner;
public class UdpServer {
public static void main(String[] args) {
System.out.println("接收端");
try {
while(true){
// 使用DatagramSocket 指定端口 创建当前应用
DatagramSocket datagramSocket = new DatagramSocket(8080);
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
//接收(等待有人发送消息是执行)
datagramSocket.receive(packet);
byte[] data = packet.getData();
System.out.println(new String(data,0,data.length));
//输入要回复的内容(这里需要接收到消息才能回消息)
Scanner scanner = new Scanner(System.in);
String next = scanner.next();
byte[] bytes1 = next.getBytes();
//发送到localhost的8081的应用上(这里发收到前面的->发起端)
DatagramPacket packet1 = new DatagramPacket(bytes1, 0, bytes1.length, new InetSocketAddress("localhost", 8081));
datagramSocket.send(packet1);
datagramSocket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行发起端再运行接收端。在发起端输入内容。接收端就会收到。然后接收端输入内容回复给发起端。这样一来一回。有说话才有回话。就发送过后要接收到回复才可以发送了。一次只能发送一句话,如果对方不回就不能发送了显然不符合逻辑
用UDP实现一对一聊天。(最终版)
加入多线程使得聊天一次可以接收多条信息。也可以发送多条信息。而且不规定谁是发起者。
线程发送类
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Scanner;
public class UdpSend implements Runnable {
private DatagramSocket client;
private String toIP;
private int toPort;
private boolean isRunning;
private String name;
UdpSend(int port,String toIP,int toPort,String name){
//初始化
this.name = name;
isRunning=true;
this.toIP = toIP;
this.toPort=toPort;
try {
client = new DatagramSocket(port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (isRunning){
byte[] bytes = new byte[1024];
Scanner scanner = new Scanner(System.in);
String next = scanner.next();
if ("q".equals(next)){
//如果输入了q就发送下线了
bytes = "我下线了,拜".getBytes();
}else {
//正常内容
next = this.name+":"+next;
bytes = next.getBytes();
}
//发送包。bytes是要发送是的内容,用InetSocketAddress指定端口地址
DatagramPacket packet = new DatagramPacket(bytes,0,bytes.length,new InetSocketAddress(toIP,toPort));
try {
//开始发送
client.send(packet);
} catch (IOException e) {
//退出循环
isRunning=false;
e.printStackTrace();
if(client!=null){
//释放资源
client.close();
}
}
//检测是否要退出。输入q退出
if ("q".equals(next)){
break;
}
}
//释放资源
if (client!=null){
client.close();
}
}
}
线程接收类
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpReceive implements Runnable {
private DatagramSocket server;
private boolean isRunning;
UdpReceive(int port){
isRunning=true;
try {
server = new DatagramSocket(port);
} catch (SocketException e) {
if (server!=null)
server.close();
isRunning=false;
e.printStackTrace();
}
}
@Override
public void run() {
while (isRunning){
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length);
try {
//等待接收
server.receive(packet);
byte[] data = packet.getData();
System.out.println(new String(data,0,data.length));
} catch (Exception e) {
isRunning=false;
if (server!=null)
server.close();
e.printStackTrace();
}
}
if (server!=null)
server.close();
}
}
教师端
import java.util.Scanner;
public class UdpTeacherThread {
public static void main(String[] args) {
System.out.println("输入q退出");
Scanner scanner = new Scanner(System.in);
System.out.println("输入姓名");
String name = scanner.next();
//8087是发送的端口。localhost是发送的目的地址。8081是发送的目的端口。name是你的名字
UdpSend send = new UdpSend(8087, "localhost", 8081, name);
//8080是接收的目的端口。地址是本机
UdpReceive receive = new UdpReceive(8080);
//开启发送线程
new Thread(send).start();
//开启接收线程
Thread thread = new Thread(receive);
thread.setDaemon(true);
thread.start();
//发送和接收不是同一个线程就不会阻塞了。
}
}
学生端
教师端和学生端基本一致就端口指定对就行了
import java.util.Scanner;
public class UdpStudentThread {
public static void main(String[] args) {
System.out.println("输入q退出");
Scanner scanner = new Scanner(System.in);
System.out.println("输入姓名");
String name = scanner.next();
new Thread(new UdpSend(8088,"localhost",8080,name)).start();
Thread thread = new Thread(new UdpReceive(8081));
thread.setDaemon(true);
thread.start();
}
}
好了到这里就结束了UDP的介绍。
TCP的介绍
TCP 是面向连接的传输控制协议,而UDP是无连接的数据报服务;TCP 具有高可靠性,确保传输数据的正确性,不出现丢失或乱序;TCP对包的大小无限制又称传输流。
TCP是通过服务器的形式转发的。就请求和响应式。客户端只和服务器对接。客户和客户无法对接,必须通过服务器这个中转站
TCP服务端编写(易理解)模拟图片上传
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class FileServer {
public static void main(String[] args) {
System.out.println("server");
try {
//指定端口。地址是本机
ServerSocket server = new ServerSocket(8080);
//阻塞式连接(有连接才会往下执行)
Socket client = server.accept();
System.out.println("连接成功");
//获取客户端发送的内容
BufferedInputStream in = new BufferedInputStream(client.getInputStream());
//将内容保存到src/IO3.png
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("src/IO3.png"));
byte[] bytes = new byte[1024];
int len=-1;
while ((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
TCP客户端编写(易理解)模拟图片上传
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.net.Socket;
public class FileClient {
public static void main(String[] args) {
System.out.println("-----Client-----");
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
//8080是服务去的端口,localhost是服务去的地址
try ( Socket client =new Socket("localhost",8080);
//client.getOutputStream()把内容输出到Socket里
BufferedOutputStream out = new BufferedOutputStream(client.getOutputStream());
//客户要上传的图片
BufferedInputStream in = new BufferedInputStream(new FileInputStream("src/IO.png"));){
int len;
byte[] bytes = new byte[1024];
while ((len=in.read(bytes))!=-1){
//开始写入
out.write(bytes,0,len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
先启动服务端再启动客户端
TCP模式的基于控制台的群聊
Socket +ServerSocket +多线程+IO实现
ServerSocket (服务端)
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
//实现群聊
class CharServerRun03 implements Runnable{
//
private Socket client;
private DataInputStream dis;
private DataOutputStream dos;
private boolean isRunning;
private String name;
//用来记录在线人
private CopyOnWriteArrayList<CharServerRun03> allClient;
CharServerRun03(Socket client,CopyOnWriteArrayList<CharServerRun03> allClient){
//赋值
this.allClient = allClient;
isRunning=true;
this.client=client;
try {
//接收客户端的信息
dis = new DataInputStream(client.getInputStream());
//给客户端发送信息
dos = new DataOutputStream(client.getOutputStream());
//获取客户端发来的名字
this.name=receive();
//发送给上线的用户
startSend("欢迎来到xxxx系统");
//告诉其他人name上线了
sendOthers("系统消息:"+name+"上线了");
} catch (Exception e) {
try {
release();
} catch (Exception ex) {
ex.printStackTrace();
}
e.printStackTrace();
}
}
//释放资源
private void release() throws Exception {
sendOthers("系统消息:"+name+"下线了");
//下线了
allClient.remove(this);
this.isRunning=false;
if(client!=null){
client.close();
}
if(dis!=null){
dis.close();
}
if (dos!=null){
dos.close();
}
}
//收集数据
private String receive(){
String datas=null;
try {
datas = dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
try {
release();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return datas;
}
public void startSend(String msg){
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendOthers(String msg){
System.out.println("发送"+msg);
//给所以人发信息(除了自己)
for (CharServerRun03 client : allClient) {
if(client!=this){
client.startSend(msg);
System.out.println("发送成功");
}
}
}
@Override
public void run() {
while(isRunning){
//获取客户发来的信息
String msg = receive();
if(msg!=null){
//开始发送
sendOthers(name+":"+msg);
}
}
}
}
public class CharServer03 {
//记录在线人的对象
//CopyOnWriteArrayList<CharServerRun03>和List<?>用法一一样
//CopyOnWriteArrayList<CharServerRun03>线程安全。List<?>多线程不安全
private static CopyOnWriteArrayList<CharServerRun03> allClient = new CopyOnWriteArrayList<>();
public static void main(String[] args) {
try (ServerSocket server = new ServerSocket(8080); ){
int sum=0;
//实现多个用户连接
while(true){
//阻塞式
Socket client = server.accept();
System.out.println(++sum+":个用户连接");
CharServerRun03 c = new CharServerRun03(client, allClient);
//添加到在线集合中
allClient.add(c);
//启动线程
new Thread(c).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Socket (客户端)
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
class ClientSendMsg implements Runnable{
private BufferedReader scanner;
//写出要发送的内容。
private DataOutputStream dataOutputStream;
private Socket client;
//客户的姓名
private String name;
private boolean isRunning;
ClientSendMsg(Socket client,String name){
this.name = name;
isRunning = true;
this.client=client;
try {
scanner = new BufferedReader(new InputStreamReader(System.in));
//client.getOutputStream();把内容输出到Socket待发送
dataOutputStream = new DataOutputStream(client.getOutputStream());
//告诉服务器客户的名字
dataOutputStream.writeUTF(name);
dataOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
//出异常释放资源
release();
}
}
//释放资源
private void release(){
//结束循环
isRunning=false;
CharUtil.close(client,scanner,dataOutputStream);
}
@Override
public void run() {
//多次发送
while (isRunning){
String msg = null;
try {
//System.out.println("输入内容:");
msg = scanner.readLine();
dataOutputStream.writeUTF(msg);
dataOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
release();
}
}
}
}
//接收信息
class ClientGetMsg implements Runnable{
private Socket client;
private DataInputStream dataInputStream;
private boolean isRunning;
ClientGetMsg(Socket client){
isRunning=true;
this.client=client;
try {
//client.getInputStream();服务器发来的
dataInputStream = new DataInputStream(client.getInputStream());
} catch (IOException e) {
e.printStackTrace();
release();
}
}
private void release(){
isRunning=false;
CharUtil.close(client,dataInputStream);
}
@Override
public void run() {
while (isRunning){
// System.out.print("输出");
try {
//获取
String msg = dataInputStream.readUTF();
System.out.println(msg);
} catch (IOException e) {
e.printStackTrace();
release();
}
}
}
}
public class CharClient03 {
public static void main(String[] args) {
try {
//"localhost", 8080是服务器的地址和端口
Socket client = new Socket("localhost", 8080);
System.out.print("输入用户名:");
Scanner scanner = new Scanner(System.in);
String name = scanner.next();
//开启发信息的线程
new Thread(new ClientSendMsg(client,name)).start();
//开启收信息的线程
new Thread(new ClientGetMsg(client)).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
启动服务器后。启动多个客户端就可以实现群聊。实在看不懂可以加我微信讨论讨论。微信号:hzclovehxr。