网络编程

本文介绍了网络通信的基础知识,包括TCP/IP协议、IP地址、子网掩码和端口的概念。详细阐述了Socket编程的基本原理及UDP、TCP两种主要协议的特点与应用。并通过示例代码展示了如何使用Python实现简单的并发服务器。

一. 网络通信概述

  1. 网络: 网络是一种辅助双方或者多方可以连接在一起的工具
  2. 使用网络的目的:
    1) 用网络能将多方连接在一起,然后进行数据传递
    2) 网络编程就是让在不同的电脑上的软件能够进行数据传递,即进程间的通信

二. TCP/IP 协议

(一)基本定义

  1. 网络协议: 网络协议为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
    每一个计算机都遵守的网络协议称作是TCP/IP 协议
  2. TCP/IP协议(族)
    互联网的众多协议中,最重要的就是TCP/IP协议。所以将互联网的协议简称为TCP/IP 协议。

在这里插入图片描述

(二)IP地址

  1. 定义: 因特网上的每台计算机和其它设备都规定了一个唯一的地址,叫做“IP地址。由于有这种唯一的地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。
  2. IP 地址的作用
    用来在网络中标记一台电脑的一串数字,比如说192.168.1.1 在本地局域网上是唯一的
  3. IP地址的分类
    每一个IP地址都包括两部分: 网络地址和主机地址
    ipv4 : 点分十进制
    ipv6: 冒分十六进制

在这里插入图片描述
4. 私有IP
在网络IP中,国际规定有一部分的IP是属于私有的,也就是不能在公共场合使用 范围是:
10.0.0.0 ~ 10.255.255.255
172.16.0.0 ~ 172.31.255.255
192.168.0.0 ~ 192.268.255.255

  1. 回环IP
    IP 地址是127.0.0.1 代表本机IP ,等于localhost 就是回环IP 可以用http://27.0.0.1 可以测试本机中的web 服务器

(三)子网掩码

子网掩码是不能单独使用的,它必须结合IP地址一起使用
子网掩码的作用:
将某个IP地址划分成网络地址和 主机地址这两部分。 子网掩码的设定必须遵照一定的规则,用来判断两个 IP 是否 在同一个网络
eg: A 172.25.254.18/24
B 172.25.0.10/24

(四) 端口

  1. 定义:
    "端口"是英文port的意译,可以认为是设备与外界通讯交流的出口。是唯一辨识进程的,便于不同程序之间的交流
    因为: PID 在每次重新 开启程序的时候就会发生变化
  2. 分类
    端口可分为虚拟端口和物理端口
    其中虚拟端口指计算机内部或交换机路由器内的端口,不可见。例如计算机中的80端口、21端口、23端口等。物理端口又称为接口,是可见端口,计算机背板的RJ45网口,交换机路由器集线器等RJ45端口。电话使用RJ11插口也属于物理端口的范畴。
  3. 端口只能是整数,范围是0~65535
    在这里插入图片描述

常见的端口:
ssh : 22
mysql : 3306
http : 80
https : 443

三. Socket 编程

(一) 基本概念

  1. 本地 进程间的通信: 队列,管道, 同步(互斥锁,条件变量等)

  2. 网络间的进程通信:
    网络层的 “ IP地址 ” 可以唯一 标识网络中的主机,而传输层的 “ 协议+ 端口” 可以唯一标识主机中的应用程序(进程)。因此利用IP地址,协议,端口就可以识别网络的进程

  3. socket 定义:
    socket (简称套接字) 是进程间的的一种通信方式,能够实现不同主机间的进程间的通信,我们在网络上的各种各样 服务大多都是基于socket 来完成通信的。

  4. 创建socket
    在 Python 中 使⽤socket 模块的函数 socket 就可以完成: socket.socket(AddressFamily, Type)
    1). Address Family:
    AF_INET: IPV4⽤于 Internet 进程间通信
    AF_INET6: IPV6⽤于 Internet 进程间通信
    2). Type:套接字类型
    SOCK_STREAM: 流式套接字,主要⽤于 TCP 协议
    SOCK_DGRAM: 数据报套接字,主要⽤于 UDP 协 议

(二) UDP

  1. 定义
    UDP : 用户数据报文协议, 是一个无连接的简单的面向数据报文运输层协议。包括⽬的端⼝号和源端⼝号信息

  2. 特点
    1) 无连接
    每一个数据报文都是一个独立的消息。包括完整的源地址或者目标地址。
    由于通讯不需要连接,所以可以实现⼴播发送。
    2)不可靠
    UDP 不提供可靠性, 只是将应用程序给IP层的数据报发出去,但并不能保证能够到达目的地。并且发送⽅所发送的数据报并不⼀定以相同的次序到达接收⽅。
    3) 传输速度快
    UDP 因为在传输数据报前不用在客户端和服务器之间建立一个连接,并且没有超时重发等机制,因此传输的速度很快
    4) 大小限制
    UDP传输数据时有⼤⼩限 制,每个被传输的数据报必须限定在64KB之内。
    在这里插入图片描述

  3. 应用场景
    UDP 是面向消息的协议, 通信的时候 不需要建立联系,且不可靠
    因此主要应用与多点通信和实时的数据任务
    语音广播, 视频, QQ , TFTP(简单文件传输) , SMMP(简单网络管理协议)
    DNS(域名解析)

  4. 工作流程
    在这里插入图片描述

实现代码如下:

服务端:

import socket

udpServer = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
udpServer.bind(('0.0.0.0', 10001))  # 10001 : 绑定的端口,返回的是元组
while True:
    recvData, address = udpServer.recvfrom(1024)  # 接收1024字节 # , 第一个是客户端发送的信息,第二个是客户端和服务端交互的地址
    print("等待客户端UDP 连接")
    print("客户端:", recvData.decode('utf-8'))
    if recvData == b'quit':
        break

    b = input("请输入回复:").encode('utf-8')
    if not b:
        continue
    udpServer.sendto(b, address)  # 发送的必须是bytes类型

udpServer.close()

客户端:

import socket

udpClient = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
print("聊天开始")
while True:
    a = input("请输入要说的话:").encode('utf-8')
    udpClient.sendto((a),('172.25.254.46', 10001))
    if a == b'quit':
        break
    if not a :
        continue
    recvData, address = udpClient.recvfrom(1024)
    print("接收服务端的数据:", recvData.decode('utf-8'))




udpClient.close()

(三) TCP

  1. 定义
    TCP :传输控制协议(Transmission Control Protocol ) 是一个面向连接的, 可靠 的,基于字节流的传输层通信协议
  2. 执行过程

在这里插入图片描述

三次握手
在这里插入图片描述

SYN : 同步序列标号
ACK : 确认包
四个状态: SYN_SENT , LISTEN() , SYN_RCVD , ESTABLISHED
过程:
第一次握手: 客户端向服务器发送连接请求时,客户端回答送同步序列标号SYN 到服务器,假设SYN = x ,等待服务器确认,这时客户端地的状态是 SYN_ SENT

第二次握手: 当服务器收到客户端发送的SYN 之后,服务器 要确认客户端发过来的SYN , 在这里服务器发送确认包ACK , 这里ACK + 1 , 意思就是: “ 已经成功接收到发送的SYN 了” , 同时服务端也会向客户端发送一个SYN 打包,这时服务器的状态时SYN_RECV.

第三次握手: 客户端收到服务器发送的SYN 和ACK 包之后, 需要向服务器发送确认包ACK , “我也收到你发送的SYN 了,我也会给你发送个确认”,发送完毕后,客户端和服务端的状态就是ESTABLISH , 即TCP 连接成功

四次分手
当客户端和服务端断开连接的时候,需要进行四次分手
在这里插入图片描述

断开连接的请求是由客户端发起的
第一次分手: 客户端向服务端发送一个带有FIN 标记的报文段

第二次分手: 服务端收到客户端发送的FIN , 服务端可能还有数据没有传输结束,所以服务端不会立刻向客户端发送FIN ,而是先发送一个ACK ,意思是 “ 你的请断开链接求我收到了,但我数据没有发送完,稍等”

第三次分手: 当服务端的数据传输结束后,服务端便可以断开连接,服务端会向客户端发送FIN

第四次分手: 客户端收到服务端发送的FIN之后,会向服务端发送确认ACK ,然后经过两个MSL 时长之后断开连接

实验代码如下:
服务端:

import socket
import os

# 1. 创建socket对象
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2. 绑定地址和端口
server.bind(('172.25.46.250', 1880))

# 3. 监听
server.listen(5)
print("server服务启动")

# 4. 接收客户端的连接
clientObj, clientAddr = server.accept()

while True:

    # 5. 接收客户端发送的消息
    recn_data = clientObj.recv(1024).decode('utf-8')
    print("接收到客户端发送的消息:", recn_data)
    # 6. 给客户端发送消息
    if recn_data == 'quit':
        break

    obj = os.popen(recn_data)
    result = obj.read()
    clientObj.send(result.encode('utf-8'))
# 7.关闭socket对象
clientObj.close()
server.close()

客户端:

import socket
import os

# 1. 创建socket对象
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2. 连接服务端
client.connect(('172.25.46.250', 1880))
while True:

# 3. 给服务端发送消息
    sendMessage = input("client >> ").encode('utf-8')
    client.send(sendMessage)
    if not sendMessage:
        continue

# 4. 接收服务端发送的消息
    recv_data = client.recv(1024).decode('utf-8')
    print("接收服务端发送的消息:", recv_data)
    if sendMessage == b'quit':
        break

# 5. 关闭socket对象
client.close()

(四)并发服务器
1.分类
并发服务器是socket 应用编程中最常见的应用模型,
1)根据连接方式可以跟为长连接和短连接
长连接: 通过SOCCKET 连接后不管是否使用都保持连接
短连接: 双方有数据交互的时候,建立TCP 连接,数据发送完成后断开连接。
2) 根据处理方式可以分为同步方式和异步方式
同步是 客户端发送请求给服务端等待服务器返回处理结果
异步是客户端发送请求给服务端,不等待服务器返回处理结果,而直接去完成其他流程,对于处理结果客户端可以事后查询和让服务器进行主动通知
在这里插入图片描述

  1. 多进程服务器:
    单进程服务器: 同一时刻只能为一个客户进行服务,不能同时为多个进行服务
    多进程服务器: 同时为多个客户进行服务
    多进程优点: 通过为每个客户端创建一个进程的方式,能够同时为多个客户端进行服务
    多进程确点: 当客户端不是特别多的时候,这种方式可行,但是要有上千个,那么每次创建进程的过程就需要消耗较大的资源,不可取
    在这里插入图片描述

代码实现:
服务端:

import socket
from multiprocessing import Process


def dealWithClient(clientObj, clientAddress):
    while True:

        # 5. 接收客户端发送的消息
        recn_data = clientObj.recv(1024).decode('utf-8')
        print(clientAddress[0] + str(clientAddress[1]) + ':>' + recn_data)
        # 6. 给客户端发送消息
        if recn_data == 'quit':
            break
    clientObj.close()


# 1. 创建socket对象
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2. 绑定地址和端口
server.bind(('172.25.46.250', 1380))

# 3. 监听
server.listen(5)
print("server服务启动")
while True:
    clientObj, clientAddr = server.accept()
    p = Process(target=dealWithClient, args=(clientObj, clientAddr))
    p.start()

客户端1:

import socket

# 1. 创建socket对象
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2. 连接服务端
client.connect(('172.25.46.250', 1380))
while True:

# 3. 给服务端发送消息
    sendMessage = input("client 1>> ").encode('utf-8')

    if not sendMessage:
        continue
    client.send(sendMessage)
# 4. 接收服务端发送的消息
    if sendMessage == b'quit':
        break

# 5. 关闭socket对象
client.close()

客户端2:

import socket

# 1. 创建socket对象
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2. 连接服务端
client.connect(('172.25.46.250', 1380))
while True:

# 3. 给服务端发送消息
    sendMessage = input("client 2>> ").encode('utf-8')

    if not sendMessage:
        continue
    client.send(sendMessage)
# 4. 接收服务端发送的消息
    if sendMessage == b'quit':
        break

# 5. 关闭socket对象
client.close()

总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值