什么网络编程?
计算机跟计算机之间通过网络进行数据传输
常见软件架构有哪些?
CS/BS
通信的软件架构CS\BS的各有什么区别和优缺点
CS:客户端服务端模式需要开发客户端
BS:浏览器服务端模式不需要开发客户端
CS:适合定制专业化的办公类软件如: IDEA、网游
BS:适合移动互联网应用,可以在任何地方随时访问的系统
网络编程三要素
IP:设备在网络中的地址,是唯一的标识
端口号:应用程序在设备中唯一的标识
协议:数据在网络中传输的规则,常见的协议有UDP、TCP、http、https、ftp
127.0.0.1,也可以是localhost: 是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。
如果是自己主机给自己主机发送信息,直接用127.0.0.1,永远表示本机。
查看本机IP:ipconfig
查看是否连通:ping
ping www.baidu.com
import java.net.InetAddress; //实现网络应用程序
import java.net.UnknownHostException;
public class net {
public static void main(String[] args) throws UnknownHostException {
InetAddress adderss1=InetAddress.getByName("XXXX"); //在给定主机名或IP情况下生成主机对象
System.out.println(adderss1);
InetAddress address2=InetAddress.getLocalHost(); //返回本地主机
System.out.println(address2);
String name=adderss1.getHostName(); //主机名
System.out.println(name);
String ip=adderss1.getHostAddress(); //主机ip
System.out.println(ip);
}
}
应用程序在设备中唯一的标识。
由两个字节表示的整数,取值范围: 0~65535
其中0~1023之间的端口号用于一些知名的网络服务或者应用,我们自己使用1024以上的端口号就可以了。
注意:一个端口号只能被一个应用程序使用。
import java.io.IOException;
import java.net.*;
public class send {
public static void main(String[] args) throws IOException {
//第一步:创建DatagramSocket对象(找快递公司)
DatagramSocket ds=new DatagramSocket();
//第二步:准备发送内容,地址,端口号
String str="cjm is pig!!";
byte[] strBytes=str.getBytes();
InetAddress address=InetAddress.getByName("127.0.0.1");
int port=10086; //指定发送端口
//第三步:DatagramPacket打包
DatagramPacket dp=new DatagramPacket(strBytes, strBytes.length,address,port);
//第四步:发送
ds.send(dp);
//第五步:释放
ds.close();
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class receive {
public static void main(String[] args) throws IOException {
//第一步:创建DatagramSocket对象,绑定发送端口
DatagramSocket ds = new DatagramSocket(10086);
//第二步:准备接收数据的DatagramPacket
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//第三步:接收数据
ds.receive(dp); //程序运行到这里会阻塞,等待发送方发送数据才能继续运行
//第四步:解析数据包
byte[] data = dp.getData();
int len = dp.getLength();
System.out.println("发送地址:" +dp.getAddress() + " 发送端口:" + dp.getPort() + " 发送内容:" + new String(data, 0, len));
//第五步:释放
ds.close();
}
}
注意:要先运行receive类,再运行send类,receive类才能接收到数据。
按照下面的要求实现程序:
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class testSend {
public static void main(String[] args) throws IOException {
DatagramSocket ds=new DatagramSocket();
Scanner scanner=new Scanner(System.in);
System.out.println("请输入您要发送的信息:");
while(true){
String str=scanner.nextLine(); //一次发送一行
if(str.equals("886")){
break;
}
byte[] bytes=str.getBytes();
InetAddress address=InetAddress.getByName("127.0.0.1");
int port=10086;
DatagramPacket dp=new DatagramPacket(bytes, bytes.length,address,port);
ds.send(dp);
}
ds.close();
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class testReceive {
public static void main(String[] args) throws IOException {
DatagramSocket ds=new DatagramSocket(10086);
byte[] bytes=new byte[1024];
DatagramPacket dp=new DatagramPacket(bytes, bytes.length);
while(true){
ds.receive(dp);
byte[] data=dp.getData();
int len=dp.getLength();
System.out.println("地址:端口号 "+dp.getAddress()+":"+dp.getPort()+" 发送信息:"+new String(data,0,len));
}
}
}
一对一
一对一组电脑
组播地址:224.0.0.0-239.255.255.255,其中224.0.0.0~224.0.0.255为预留的组播地址。
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class send {
public static void main(String[] args) throws IOException {
//创建MulticastSocket对象
MulticastSocket multicastSocket=new MulticastSocket();
String str="cjm is pig";
byte[] bytes=str.getBytes();
InetAddress address=InetAddress.getByName("224.0.0.1"); //组播地址
int port=10086;
DatagramPacket dp=new DatagramPacket(bytes, bytes.length,address,port);
multicastSocket.send(dp);
multicastSocket.close();
}
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class receive1 {
public static void main(String[] args) throws IOException {
//创建MulticastSocket对象
MulticastSocket multicastSocket = new MulticastSocket(10086);
byte[] bytes = new byte[1024];
//将当前主机加入224.0.0.1这一组
InetAddress address = InetAddress.getByName("224.0.0.1");
multicastSocket.joinGroup(address);
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
multicastSocket.receive(dp);
byte[] data = dp.getData();
int len = dp.getLength();
System.out.println("地址:端口" + dp.getAddress() + ":" + dp.getPort() + " 发送信息:" + new String(data, 0, len));
multicastSocket.close();
}
}
一对局域网中所有的电脑
广播地址:255.255.255.255。
import java.io.IOException;
import java.net.*;
public class send {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String str = "cjm is pig";
byte[] strBytes = str.getBytes();
InetAddress address = InetAddress.getByName("255.255.255.255"); //给局域网里面所有的地址发送数据
int port = 10086;
DatagramPacket dp = new DatagramPacket(strBytes, strBytes.length, address, port);
ds.send(dp);
ds.close();
}
}
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//1.创建socket,并且设置服务器的地址和端口号
Socket socket=new Socket("127.0.0.1",10086); //在创建对象的同时会连接服务器,如果连不上,代码会报错
//2.获取输出流
OutputStream os=socket.getOutputStream();
os.write("cjm is pig.".getBytes()); //中文会乱码
//3.释放资源
os.close();
socket.close();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//1.创建ServerSocket
ServerSocket ss=new ServerSocket(10086); //绑定端口
//2.监听客户端的连接,一但连接返回连接对象
Socket socket=ss.accept(); //阻塞,直到客户端请求连接,有连接返回连接对象
//3.从连接通道中获取输入流
InputStream is=socket.getInputStream();
int b;
while((b=is.read())!=-1){
System.out.print((char)b);
}
//4.释放资源
socket.close(); //关闭连接通道
ss.close(); //断开连接
}
}
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
//1.创建socket,并且设置服务器的地址和端口号
Socket socket=new Socket("127.0.0.1",10086); //在创建对象的同时会连接服务器,如果连不上,代码会报错
//2.获取输出流
OutputStream os=socket.getOutputStream();
os.write("cjm是猪!!!".getBytes());
//3.释放资源
os.close();
socket.close(); //利用四次挥手协议断开连接,确保连接通道里面的数据全部处理完毕
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//1.创建ServerSocket
ServerSocket ss=new ServerSocket(10086); //Server绑定端口号
//2.监听客户端的连接,一但连接返回连接对象
Socket socket=ss.accept();
//3.从连接通道中获取输入流
InputStream is=socket.getInputStream();
//解决中文乱码,把字节流转换成字符流
InputStreamReader isr=new InputStreamReader(is);
//提高读取效率
BufferedReader br=new BufferedReader(isr); //缓冲流
int b;
while((b=br.read())!=-1){
System.out.print((char)b);
}
//4.释放资源
socket.close(); //关闭连接通道
ss.close(); //断开连接
}
}
客户端:多次发送数据
服务器:接收多次数据并打印
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
Scanner sc=new Scanner(System.in);
Socket socket=new Socket("127.0.0.1",10086);
OutputStream os= socket.getOutputStream();
while(true){
System.out.println("请输入发送的信息:");
String str=sc.nextLine(); //按行发送
if(str.equals("886")){
System.out.println("发送结束!");
break;
}
os.write(str.getBytes());
System.out.println("发送完成!");
}
os.close();
socket.close();
}
}
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10086);
Socket socket=ss.accept();
InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
BufferedReader br=new BufferedReader(isr);
System.out.println("----------开始接收信息----------");
System.out.println("地址:端口号"+socket.getInetAddress()+":"+socket.getPort()+"发送的信息为:");
int b;
while((b=br.read())!=-1){
System.out.print((char)b);
}
}
}
程序会一直执行br.read(),多次读取到的内容并没有换行。
问题所在:
Scanner sc=new Scanner(System.in); String str=sc.nextLine();
sc.nextLine() 返回的是结束符之前的内容,并不会返回结束符,想要服务端输出有换行还需要在输入的字符后加一个换行符。
while(true){
System.out.println("请输入发送的信息:");
String str=sc.nextLine(); //按行发送,返回的是结束符之前的内容,并不会返回结束符
if(str.equals("886")){
System.out.println("发送结束!");
break;
}
str=str+'\n'; //加一个换行符
os.write(str.getBytes());
System.out.println("发送完成!");
}
客户端:发送一条数据,接收服务端反馈的信息并打印
服务器:接收数据并打印,再给客户端反馈消息
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",10086);
OutputStream os=socket.getOutputStream(); //打开输出流
Scanner sc=new Scanner(System.in);
System.out.println("请输入要发送的信息:");
String str=sc.nextLine();
str=str+"\n";
os.write(str.getBytes());
System.out.println("发送成功!");
socket.shutdownOutput(); //关闭输出流
System.out.println("开始接收反馈信息----------------");
InputStream is= socket.getInputStream(); //打开输入流
InputStreamReader isr=new InputStreamReader(is);
BufferedReader br=new BufferedReader(isr);
int b;
while((b=br.read())!=-1){
System.out.print((char)b);
}
System.out.println("接收反馈信息成功!");
socket.close();
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10086);
Socket socket=ss.accept();
InputStream is=socket.getInputStream(); //打开输入流
InputStreamReader isr=new InputStreamReader(is);
BufferedReader br=new BufferedReader(isr);
System.out.println("接收到的信息为:");
int b;
while((b=br.read())!=-1){
System.out.print((char)b);
}
System.out.println("开始发送反馈信息----------------");
OutputStream os= socket.getOutputStream(); //打开输出流
String str="服务端反馈信息:cjm是猪!\n";
os.write(str.getBytes());
System.out.println("发送反馈信息成功!");
socket.close();
ss.close();
}
}
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",10086);
//InputStream读取文件
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("net\\file\\22.txt"));
//创建输出流OutputStream
BufferedOutputStream bos=new BufferedOutputStream(socket.getOutputStream());
//将文件读到字节数组后输出
byte[] bytes=new byte[1024];
int len;
//read(byte[] data);从输入流里面读取若干字节装填到给定的数组里面
//如果输入流中字节足够则填满,否则只装填前面的部分.返回实际装填长度
while((len=bis.read(bytes))!=-1){ //输入流读入
bos.write(bytes,0,len); //输出流输出
}
bos.flush(); //刷新此缓冲的输出流
//使用outputstream写文件没有bos.flush();会导致文本文件内容缺失
socket.shutdownOutput(); //关闭输出流
//打开输入流接收反馈信息
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str=br.readLine();
System.out.println(str);
//关闭通道
socket.close();
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10086);
Socket socket=ss.accept();
//输入流接收文件
BufferedInputStream bis=new BufferedInputStream(socket.getInputStream());
//输出流保存文件
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("net\\fileout\\22.txt"));
//在输入流读取文件到字节数组再用输出流保存文件
byte[] bytes=new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.flush(); //刷新此缓冲的输出流
//使用outputstream写文件没有bos.flush();会导致文本文件内容缺失
//打开输出流发送反馈信息
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功!");
bw.newLine(); //写入一个行分隔符
bw.flush();
socket.close();
ss.close();
}
}
注意:记得关闭输入输出流。
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("127.0.0.1",10086);
//InputStream读取文件
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("net\\file\\11.png"));
//创建输出流OutputStream
BufferedOutputStream bos=new BufferedOutputStream(socket.getOutputStream());
//将文件读到字节数组后输出
byte[] bytes=new byte[1024];
int len;
//read(byte[] data);从输入流里面读取若干字节装填到给定的数组里面
//如果输入流中字节足够则填满,否则只装填前面的部分.返回实际装填长度
while((len=bis.read(bytes))!=-1){ //输入流读入
bos.write(bytes,0,len); //输出流输出
}
bos.flush(); //刷新此缓冲的输出流
//使用outputstream写文件没有bos.flush();会导致文本文件内容缺失
socket.shutdownOutput(); //关闭输出流
//打开输入流接收反馈信息
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str=br.readLine();
System.out.println(str);
//关闭通道
socket.close();
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10086);
Socket socket=ss.accept();
//输入流接收文件
BufferedInputStream bis=new BufferedInputStream(socket.getInputStream());
//输出流保存文件
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("net\\fileout\\11.png"));
//在输入流读取文件到字节数组再用输出流保存文件
byte[] bytes=new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.flush(); //刷新此缓冲的输出流
//使用outputstream写文件没有bos.flush();会导致文本文件内容缺失
//打开输出流发送反馈信息
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功!");
bw.newLine(); //写入一个行分隔符
bw.flush();
socket.close();
ss.close();
}
}
注意:记得关闭输入输出流。
解决上一题文件名重复问题
//解决文件名重复问题
//返回随机生成的UUID,再把其中的"-"去掉
String name= UUID.randomUUID().toString().replace("-","");
//输出流保存文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("net\\fileout\\"+name+".png"));
想要服务器不停止,能接收很多用户上传的图片。
提示:可以使用循环或者多线程
最优解是循环+多线程
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10086);
//InputStream读取文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\29163\\Desktop\\java net\\net\\file\\11.png"));
//创建输出流OutputStream
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
//将文件读到字节数组后输出
byte[] bytes = new byte[1024];
int len;
//read(byte[] data);从输入流里面读取若干字节装填到给定的数组里面
//如果输入流中字节足够则填满,否则只装填前面的部分.返回实际装填长度
while ((len = bis.read(bytes)) != -1) { //输入流读入
bos.write(bytes, 0, len); //输出流输出
}
bos.flush(); //刷新此缓冲的输出流
//使用outputstream写文件没有bos.flush();会导致文本文件内容缺失
socket.shutdownOutput(); //关闭输出流
//打开输入流接收反馈信息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str = br.readLine();
System.out.println(str);
//关闭通道
socket.close();
}
}
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class MyRunnable implements Runnable {
Socket socket;
public MyRunnable(Socket socket) { //传参
this.socket = socket;
}
@Override
public void run() {
try {
//输入流接收文件
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//解决文件名重复问题
//返回随机生成的UUID,再把其中的"-"去掉
String name = UUID.randomUUID().toString().replace("-", "");
//输出流保存文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\29163\\Desktop\\java net\\net\\fileout\\" + name + ".png"));
//在输入流读取文件到字节数组再用输出流保存文件
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.flush(); //刷新此缓冲的输出流
//使用outputstream写文件没有bos.flush();会导致文本文件内容缺失
//如果不关io,服务端运行的时候文件夹无法正常查看图片的缩略图,Windows自带的照片查看器可以打开图片,wps图片查看器不可以
bos.close(); //要把输出流关了,在服务端还在运行的时候文件夹才能正常查看图片的缩略图
//打开输出流发送反馈信息
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功!");
bw.newLine(); //写入一个行分隔符
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
while(true){
Socket socket = ss.accept();
new Thread(new MyRunnable(socket)).start(); //一旦接收一个请求就开启一个线程
}
}
}
频繁创建线程并销毁非常浪费系统资源,需要用线程池优化。
import test5.MyRunnable;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
//核心线程数量,线程池最大的线程数量,存活时间,时间单位,阻塞队列,线程工厂,任务拒绝策略
ThreadPoolExecutor pool=new ThreadPoolExecutor(
3,
16,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
while(true){
Socket socket = ss.accept();
//new Thread(new MyRunnable(socket)).start(); //一旦接收一个请求就开启一个线程
pool.submit(new MyRunnable(socket));
}
}
}
接收浏览器消息并打印。
客户端:不需要写。
服务器:接收数据并打印。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//1.创建ServerSocket
ServerSocket ss=new ServerSocket(10086);
//2.监听客户端的连接,一但连接返回连接对象
Socket socket=ss.accept();
//3.从连接通道中获取输入流
InputStream is=socket.getInputStream();
//解决中文乱码,把字节流转换成字符流
InputStreamReader isr=new InputStreamReader(is);
//提高读取效率
BufferedReader br=new BufferedReader(isr);
int b;
while((b=br.read())!=-1){
System.out.print((char)b);
}
//4.释放资源
socket.close(); //关闭连接通道
ss.close(); //断开连接
}
}
直接运行客户端程序,用浏览器访问127.0.0.1 :10086,控制台会打印浏览器发送的信息。
浏览器回写数据涉及前端知识。