计算机网络 是指地理位置不同的具有独立功能的多台计算机及其外部设备。
通过通信线路连接起来,在网络操作系统、网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程 在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换。
网络编程三要素 ①IP地址 ②端口 ③ 协议
IP地址 是网络中设备的唯一标识
IP地址分为两大类:
IPv4:
IPv6:
常用命令:
方法名 | 说明 |
---|---|
static InetAddress getByName(String host) | 确定主机名称的IP地址 主机名称可以是机器名称,也可以是IP地址 |
方法名 | 说明 |
---|---|
String getHostAddress() | 返回文本显示中的IP地址字符串 |
String getHostName() | 获取此IP地址的主机名 |
代码:
// package itiheima311
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
// getByName
InetAddress address = InetAddress.getByName("本地电脑主机名");
String name = address.getHostName();
String ip = address.getHostAddress();
System.out.println("主机名:" + name);
System.out.println("ip地址:" + ip);
// 输出内容:
// 主机名:xxxxxx
// ip地址:xxxxxx
}
}
注意
端口: 设备上应用程序的唯一标识
端口号:
协议: 计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议: 用户数据报协议(User Datagram Protocal)
TCP协议: 传输控制协议(Transmission Control Protocol)
UDP通信原理
注意:
常用方法及方法
方法名 | 说明 |
---|---|
发送数据 | |
DatagramSocket() | 创建发送端的套接字 |
DatagramPacket(Bytes[] bytes,int length,InetAddress ip,int port) | 创建数据包,把长度为length的字节数组发送到ip地址为InetAddress的主机的port端口 |
void send(DatagramPacket p) 所属类DatagramSocket |
从此套接字发送数据 |
接收数据 | |
DatagramSocket(int port) | 创建接收端套接字,监控指定端口port |
DatagramPacket(bytes[] buf , int length) | 构造数据包,用于接收长度为length的字节数组 |
void receive(DatagramPacket p) 所属类DatagramSocket |
从此套接字接收数据 |
byte[] getData() 所属类DatagramPacket |
返回数据包中的数据缓冲区数据 |
int getLength() 所属类DatagramPacket |
返回发送或者接收数据的长度 |
1 创建发送端的Socket对象(DatagramSocket)
DatagramSocket() 构造方法:构造数据报套接字并将其绑定到本地主机上的任何可用端口。
//DatagramSocket ds = new DatagramSocket();
2 创建数据,并把数据打包
DatagramPacket(byte[] buf,int length,InetAddress address,int port)
构造一个数据包,发送长度为length的数据包到指定主机上的指定端口号(目的主机)
//byte[] bys = "要传输数据".getBytes();
//int len = bys.length();
//InetAddress ip = new InetAddress("ip地址或主机名");
//DatagramPacket dp = new DatagramPackt(bys,len,ip,port);
3 调用DatagramSocket对象的方法send发送数据
void send (DatagramPacket p) 从此套接字发送数据报包
//ds.send(dp);
4 关闭发送端
void close() 关闭此数据报套接字
//ds.close();
代码:
// 311
public class SendDemo {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
byte[] bys = "hello,udp,我来了".getBytes();
// int length = bys.length;
// InetAddress address = InetAddress.getByName("ip地址");
// int port = 10086;
DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("ip地址"),10086);
ds.send(dp);
ds.close();
}
}
1 创建接收端的Socket对象(DatagramSocket)
DatagramSocket(int port):构造数据报套接字并将其绑定到本地主机上的指定端口
//DatagramSocket ds = new DatagramSocket(端口号);
2 创建一个数据包,用于接收数据
DatagramPacket(byte[] bys,int length):构造一个DatagramPacket用于接收长度为Length数据包
//byte[] bys = new byte[1024];
//DatagramPacket dp = new DatagramPacket(bys,bys.length);
3 调用DatagramSocket对象的方法接收数据
Socket对象.receive(数据包)
//ds.receive(dp);
4 解析数据包,并把数据在控制台显示
byte[] getData() 返回数据缓冲区
数据包.getData()——这个方法拿到的是数据缓冲区
int getLength() 返回要发送的数据的长度或接收到的数据长度
//byte[] data = dp.getData();
//int datalen = dp.getLength();
//String dataS = new String(data,0,datalen);
5 关闭接收端
Socket.close();
//ds.close();
代码:
// 311
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(10085);
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
ds.receive(dp);
byte[] data = dp.getData();
int len = dp.getLength();
String dataString = new String(data,0,len);
System.out.println("数据是:" + dataString);
ds.close();
}
}
要求:
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
代码:
// 311-test3
public class SendDemo {
public static void main(String[] args) throws SocketException, IOException {
/*要求:
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
*/
// 创建发送端的Socket
DatagramSocket sd = new DatagramSocket();
//键盘录入要发送的数据 使用的是字符缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
//
while((line = br.readLine()) != null){
if("886".equals(line)){
break;
}
byte[] bys = line.getBytes();
int len = line.length();
InetAddress address = InetAddress.getByName("ip地址");
int port = 10086;
// 发送数据的数据包
DatagramPacket dp = new DatagramPacket(bys,len,address,port);
// 数据发送
sd.send(dp);
// 数据是一行一行发送。
}
// 关闭发送流
sd.close();
br.close();
}
}
// 接收端
public class recevieDemo {
public static void main(String[] args) throws IOException {
// 创建一个socket对象
DatagramSocket ds = new DatagramSocket(10086);
byte[] bys = new byte[1024];
// 创建接收数据包
DatagramPacket dp = new DatagramPacket(bys,bys.length);
while(true) {
// 接收数据
ds.receive(dp);
// 解析数据
byte[] dataBys = dp.getData();
int dataLen = dp.getLength();
// 将字符数组数据转为字符串
String sOut = new String(dataBys, 0, dataLen);
// 控制台输出数据
System.out.println("发送数据是:" + sOut);
}
// 关闭接收端
// ds.close();
}
}
发送数据和接收数据涉及到的类
方法名 | 说明 |
---|---|
发送数据 | |
Socket(InetAddress address,int port) | 创建流套接字并连接到指定IP地址的指定端口号 |
Socket(String host,int port) | 创建套接字并将其连接到指定主机上的指定端口号 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
InputStream getInputStream() | 获取输入流 |
void write() | 将数据写入字节输出流中 |
public void shutdoenOutput() | Socket类提供了方法设置结束标识 |
接收数据 | |
ServerSocket(int port) | 创建绑定到指定端口的服务器套接字 |
Socket accept() 所属于ServerSocket类 |
侦听要连接到此套接字并接受它 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
InputStream getInputStream() | 获取输入流 |
int read() | 读取字节输入流数据,读一个字节、读一个字节数组 |
注意
2022/4/20 |
---|
创建Socket对象 和 ServerSocket对象 的时候,需要抛出一个异常IOException |
步骤涉及类信息说明:
常用方法
方法名 | 说明 |
---|---|
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
操作步骤:
1 创建客户端的Socket对象
Socket(InetAddress address,int port):创建流套接字并连接到指定IP地址的指定端口号
//Socket s = new Socket(InetAddress.getByName("IP地址"),端口号)
Socket(String host,int port):创建套接字并将其连接到指定主机上的指定端口号
//Socket s = new Socket("IP地址",端口号)
2 获取输出流 写数据
OutputStream getOutputStream() :返回此套接字的输出流
void write():写一个字节数组;写一个字节数组一部分;写一个int数据。
//OutputStream os = s.getOutputStream();
//os.write(要写的内容);
3 释放资源
close();
// s.close();
代码:
// itiheima311.test4
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 客户端的Socket对象
Socket s = new Socket("ip地址",端口号);
// 输出流对象,写数据
OutputStream os = s.getOutputStream();
os.write("you are my pretty sunshine".getBytes());
// 释放资源
s.close();
}
}
注意
直接运行client程序会报错的:因为TCP通信是需要三次握手,只有客户端没有服务端连接请求就会没有回复的响应,故此报错java.net.ConnectException: Connection refused: connect
步骤涉及的类:
步骤:
1 创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port) 创建绑定到指定端口的服务器套接字
//ServerSocket ss = new ServerSocket(端口号);
2 监听客户端连接,返回一个Socket对象
Socket accept() : 侦听要连接到此套接字并接受它
//Socket s = ss.accept();
3 获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream(); 获取输入流。
// InputStream is = s.getInputStream();//获取输入流
// byte[] bys = new byte[1024];//读数据 一次读一个字节数组
// int len;
// while((len = is.read(bys)) != -1){
// System.out.println(new String(bys,0,len));//显示在控制台
// }
4 释放资源
close()
// ss.close();
代码:
// itiheima311.test4
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(端口号);
// 监听客户端连接
Socket s = ss.accept();
// 获取输入流 读数据 并把数据显示在控制台
InputStream is = s.getInputStream();
// 一次读一个字节数组
byte[] bys = new byte[1024];
int len;
while((len = is.read(bys)) != -1){
System.out.println(new String(bys,0,len));
}
// 释放资源
is.close();
ss.close();
}
}
客户端:发送数据,接受服务器反馈
服务器:接收数据,给出反馈
2022/7/27
客户端:
Socket(String host,int port) s
// 发送
s.getOutputStream gos
gos.write(字符串.getBytes()) // 输出字节流不能写字符串,必须是字节或字节数组
// 接收
getInputStream gis
gis.read(bytes[] buf) buf
new String(buf,0,buf.length)
服务端:
ServerSocket(int port) ss
ss.accept() Scoket s
s.getInputStream() gis
gis.read(bytes[] buf) buf
s.getOutputStream() gos
gos.write() 给客户端写回信
// itiheima311.test5
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket s = new Socket("ip地址",端口号);
// 客户端发送数据
OutputStream os = s.getOutputStream();
os.write("每个人的生命中,都有无比艰难的那一年,将人生变得更美好而辽阔".getBytes());
// 接收服务端反馈
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
/*int len;
while((len = is.read(bys)) != -1){
System.out.println("客户端:" + new String(bys,0,len));
}*/
int len = is.read(bys);
String data = new String(bys,0,len);
System.out.println("服务端回信:" + data);
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10023);
Socket s = ss.accept();
// 服务端接收客户端发送的数据
// 获取输入流,读数据,并把结果显示在控制台
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
/* int len;
while((len = is.read(bys)) != -1){
System.out.println("服务器:" + new String(bys,0,len));
}*/
int len = is.read(bys);
String data = new String(bys,0,len);
System.out.println("服务器接收到客户端数据:" + data);
// 给客户端发送反馈信息
OutputStream os = s.getOutputStream();
os.write("数据已接收".getBytes());
ss.close();
}
}
客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务端:接收到的数据在控制台输出
2022/7/28
发送端:
Socket(String host,int port) s
BufferedReader(new InputStreamReader(System.in)) br
BufferedWriter(new OutputStreamWriter(s.getOutputStream())) bw
String line;
while((line = br.readline()) != null)
if("886".equals(line))
break;
bw.write(line);
bw.newLine();
bw.flush();
bw.close();
s.close();
接收端:
ServerSocket(int port) ss
ss.accept() Socket s
BufferedReader(new InputStreamReader(s.getInputStream())) br
String line;
whlie((line = br.readLine()) != null){
System.out.println(line);
}
ss.close();
代码:
// itiheima311.test6
public class ClientDemo {
public static void main(String[] args) throws IOException {
/*
客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务端:接收到的数据在控制台输出*/
Socket s = new Socket("192.168.1.100",10023);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line = br.readLine()) != null){
if("886".equals(line)){
break;
}
/*// 获取输出流对象
OutputStream os = s.getOutputStream();
os.write(line.getBytes());*/
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建ServerSocket对象
ServerSocket ss = new ServerSocket(10023);
//
Socket s = ss.accept();
// InputStream is = s.getInputStream();
/*byte[] bys = new byte[1024];
int len;
while((len = is.read(bys)) != -1){
String data = new String(bys,0,len);
System.out.println("服务器接收到数据:"+ data);
}*/
// 使用字符缓冲输入流 读取数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
ss.close();
}
}
需求
客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务器:接收到的数据写入文本文件
2022/7/28
发送端:
Socket(String host,int port) s
//接收再发送
BufferedReader(new InputStreamReader(System.in)) br
BufferedWriter(new OutputStreamWriter(s,getOutputStream())) bw
String line;
while((line = br.readLine()) != null){
if("886".equals())
break;
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
s.close();
服务端:
ServerSocket(int port) ss
ss.accept() Socket s
// 读数据写入文件中
BufferReader(new InputStreamReader(s.getInputStream())) br
Bufferwriter(new FileWriter(String filename)) bw
String line;
while((line = br.readLine()) != null)
bw.write(line);
bw.newLine();
bw.flush();
bw.close();
br.close();
ss.close();
public class CilentDemo {
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.1.100",10023);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line = br.readLine()) != null){
if("886".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(10023);
// 监听客户端连接
Socket s = ss.accept();
// 接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 把数据写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\bw.txt"));
String line;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
ss.close();
bw.close();
}
}
需求
客户端:数据来自于文本文件
服务器:接收到的数据写入文本文件
2022/7/28
客户端:
Socket(String host,int port) s
// 文件读数据 写入socket的输出流
BufferReader(new FileReader(String name)) br
BufferWriter(new OutputStreamWriter(s.getOutputStream())) bw
String line;
while((line = br.readLine()) != null)
bw.write(line);
bw.newLine();
bw.flush();
br.close();
bw.close();
s.close();
服务端:
ServerSocket(int port) ss
ss.accept() Socket s
// Socket读数据到输入流 数据写入文件中
BufferedReader(new InputStreamReader(s.getInputStream())) br
BufferedWriter(new FileWriter(String name)) bw
String line;
while((line = br.readLine()) != null)
bw.write(line);
bw.newLine();
bw.flush();
br.close();
bw.close();
ss.close();
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10023);
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\bw.txt"));
String line ;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
ss.close();
bw.close();
}
}
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket s = new Socket("192.168.1.100",10023);
// 给文件写数据
/* BufferedWriter bw = new BufferedWriter(new FileWriter(".\\br.txt"));
bw.write("没有一劳永逸,永远要重新开始,重新进入动荡,重新寻找,重新赢得欢喜");
bw.close();*/
// 封装文本文件的数据
BufferedReader br = new BufferedReader(new FileReader(".\\br.txt"));
// 封装输出流写数据
BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line = br.readLine()) != null){
bw1.write(line);
bw1.newLine();
bw1.flush();
}
s.close();
br.close();
}
}
需求
客户端:数据来自于文本文件,接收服务器反馈
服务器:接收到的数据写入文本文件,给出反馈
分析:
会出现程序卡住的问题,因为客户端上传程序没有明确的上传结束标识,服务器会持续等待一直在读数据。
原因:读数据的方式是阻塞式的
解决方法:自定义结束标记;使用shutdownOutput()方法
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.1.100",10023);
// 封装文本文件的数据
BufferedReader br = new BufferedReader(new FileReader(".\\br.txt"));
// 封装输出流写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
// 自定义数据上传结束标识
/* bw.write("886");
bw.newLine();
bw.flush();*/
// 会产生一个问题,当要发送的文件中存在886字符串的时候,后续内容将不会继续发送。
// Socket类提供了方法设置结束标识
// public void shutdoenOutput()
s.shutdownOutput();
// 接收服务器反馈
BufferedReader br1 = new BufferedReader(new InputStreamReader(s.getInputStream()));
String data;
while((data = br1.readLine()) != null){
System.out.println("客户端收到是:" + data);
}
s.close();
br.close();
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10023);
Socket s = ss.accept();
// 封装输入流数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bw = new BufferedWriter(new FileWriter(".\\bw.txt"));
String line;
while((line = br.readLine()) != null){
/*if("886".equals(line)){
break;
}*/
bw.write(line);
bw.newLine();
bw.flush();
}
// 服务器给出反馈
BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bw1.write("服务器已接收到数据");
bw1.newLine();
bw1.flush();
// 释放资源
bw.close();
ss.close();
}
}
需求
客户端:数据来自于文本文件,接收服务器反馈
服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程
2022/7/28分析
服务端需要继承Runnable接口,实现多线程;
服务端:
public class ServerThread implements Runnable{
private Socket s;
public ServerThread(Socket s){
this.s = s;
}
@Override
public void run(){
try{
}catch(IOException e){
}
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 创建服务器Socket对象
ServerSocket ss = new ServerSocket(10023);
// 通过while模拟服务器不关闭
while(true) {
// 监听客户端的连接,返回一个对应的Socket对象
Socket s = ss.accept();
// 为每一个客户端开启一个线程
new Thread(new ServerThread(s)).start();
}
}
}
public class ServerThread implements Runnable {
private Socket s;
public ServerThread(Socket s) {
// 最开始这里没有写 相当于没给方法传参数
this.s = s;
}
@Override
public void run() {
try{
// 接收数据写到文本文件
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// BufferedWriter bw = new BufferedWriter(new FileWriter(".\\312bw.txt"));
// 多个客户端上传多个程序,使用不同文件夹存储
int count = 0;
String path = ".bw[" + count + "].txt";
// 判断文件名是否有重复
File f = new File(path);
while(f.exists()){
count++;
path = ".bw[" + count + "].txt";
f = new File(path);
}
// 字符缓冲流对象
BufferedWriter bw = new BufferedWriter(new FileWriter(f));
String line;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
// 给客户端反馈
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件上传成功!");
// 服务器反馈一直是null的原因是:这里没有执行换行和刷新缓存
bwServer.newLine();
bwServer.flush();
// 释放资源
s.close();
bw.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket s = new Socket("192.168.1.100",10023);
// 封装文本文件
BufferedReader br = new BufferedReader(new FileReader(".\\br.txt"));
// 封装输出流数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
// 向字节流中写数据
String line;
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
bw.flush();
}
// 字符流输出过程结束标识符
s.shutdownOutput();
// 接收服务器反馈
BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
/* String information;
while((information = brClient.readLine()) != null){
System.out.println("客户端接收到的数据:" + information);
}*/
String data = brClient.readLine();
System.out.println("服务器的反馈:" + data);
// 释放资源
br.close();
s.close();
}
}