Python网络编程

查看Python中支持的编码方式:https://docs.python.org/3/library/codecs.html?highlight=utf

IP地址工具:http://ipblock.chacuo.net/

查看应用程序进程PID:

1.启动任务管理器,点击进程:默认是这样的:

Python网络编程

2.点击查看, 选择列,勾上PID即可。

Python网络编程

Python网络编程

进程PID与进程使用的端口号是不同的概念。

Windows下看看哪个端口被占用及占用的进程

命令:

netstat -aon | findstr "8080"
netstat -an#查看所有的端口使用情况

8080是要查看的端口号。

例如在pycharm中建立一个网络通信

TCP

Python网络编程

#time_server.py
import socket
import time
HOST = '127.0.0.1'
PORT = 12345
BUFSIZE = 1024
ADDR = (HOST,PORT)
tcpServerSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpServerSock.bind(ADDR)
tcpServerSock.listen(5)

while True:
    print("waiting for connection...")
    #这里tcpClietnSock为,包含了socket的信息
    tcpClientSock,addr = tcpServerSock.accept()
    print("...connected from:",addr)
    while True:
        data = tcpClientSock.recv(BUFSIZE)
        if not data:
            break
        # tcpClientSock.send('[%s] %s'%(time.ctime().encode('utf-8'),str(data).encode('utf-8')))
        # tcpClientSock.send('%s %s'%(time.ctime().encode('utf-8'),data))
        tcpClientSock.send(bytes("[%s] %s"%(time.ctime(),data.decode('utf-8')),encoding='utf-8'))
    tcpClientSock.close()

#time_client.py
import socket
HOST = '127.0.0.1'
PORT = 12345
BUFSIZE = 1024
ADDR = (HOST,PORT)

tcpClientSock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcpClientSock.connect(ADDR)

while True:
    data = input('>')
    if not data:
        break
    tcpClientSock.send(bytes(data,encoding='utf-8'))
    # tcpClientSock.send(data.encode('utf-8'))
    data = tcpClientSock.recv(BUFSIZE)
    if not data:
        break
    print(data.decode('utf-8'))
tcpClientSock.close()

服务器端使用的端口好为:12345,运行服务器和客户端后,发现客户端使用的端口号为12037,执行命令:

Python网络编程

查看到端口12345和12037被占用了,在任务管理其中查看到是Python3占用了这两个端口

Python网络编程

为什么使用端口,而不直接使用PID了?

PID也可以唯一表示一个进程呀,这是因为使用PID代价太大,双方通信之前,得需要提前知道对方的PID,但是PID是操作系统自动分配的,人工去查很麻烦。如果直接使用端口的话比较方便,一般一个服务的端口比较固定。

同一个系统中,端口不能重复。

通信调试助手:模拟发送和接收情况。链接:https://pan.baidu.com/s/1GJTYxvUKGIHB9KM-D0CVug 密码:31z1

Python网络编程

TCP连接中listen参数问题:

连接队列长度:客户端已经和服务器建立连接,或者客户端正在和服务器建立连接,但是还没有被服务器accept的最大个数。即同时等待accept的个数,和已经accept的个数没有关系。

Windows和Mac中严格安装这个参数来,Linux中内核会自己处理。

服务器

from socket import *
import time

s = socket(AF_INET,SOCK_STREAM)
s.bind(("127.0.0.1",12345))
s.listen(5)#队列缓存

while True:
    clientScoket,addr = s.accept()
    print(addr)
    time.sleep(1)

客户端

from socket import *

for i in range(10):
    c = socket(AF_INET, SOCK_STREAM)
    c.connect(("127.0.0.1",12345))#客户端只管啪啪啪connect,等连不上就报错
    print(i)

Python网络编程

Python网络编程

服务器端处理过慢,可能导致客户端连接超时报错。

本地的进程间通信:

网络间通信:socket

AF_UNIX

本地通信

AF_INET

IPv6

AF_NETLINK




SOCK_STREAM

TCP

SOCK_DGRAM

UDP

UDP在局域网中几乎不会丢包

Python3中发送数据需要bytes类型,不能是字符串类型,不然会报错:

Python网络编程

发送前将字符串编码,接收后将字符串解码。

中文一般是UTF-8,或者gb2312。

data='hello world'
data.encode('utf-8')
b'hello world'
bytes(data,encoding='utf-8')
b'hello world'
data='黄铁牛'
data.encode('utf-8')
b'黄铁牛'
bytes(data,encoding='utf-8')
b'黄铁牛'

UDP简单示例

Python网络编程

服务器:

#服务器
from socket import *
import time
def main():
    udpServerSocket = socket(AF_INET,SOCK_DGRAM)
    #这里服务器可以绑定自己的ip地址和端口号,ip地址一般为空,表示本机的所有可用ip地址,
    #可以是网络地址,本地地址和127.0.0.1
    udpServerSocket.bind(("",12346))
    BUFFERSIZE = 1024#单位字节
    while True:
        print("..waiting for message:")
        data,addr = udpServerSocket.recvfrom(BUFFERSIZE)
        print("[%s] %s received from %s"%(time.ctime(),data.decode('utf-8'),addr))
        udpServerSocket.sendto(bytes("[%s] %s"%(time.ctime(),data.decode('utf-8')),encoding='utf-8'),addr)


if __name__ == "__main__":
    main()

客户端:

#客户端
from socket import *
udpClientSock = socket(AF_INET,SOCK_DGRAM)
#这里客户端可以绑定自己的ip地址和端口号,ip地址一般为空,表示本机的所有可用ip地址,
#可以是网络地址,本地地址和127.0.0.1。这样以后客户端的端口号就不变了。
#客户端一般不绑定端口
#udpClientSock.bind(('',9999))#不能这里绑网络地址,下面发送给本地地址。
BUFFERSIZE = 1024
while True:
    data = input('>')
    if not data:
        break
    udpClientSock.sendto(data.encode('utf-8'),("172.24.55.11",12346))
    data,ADDR = udpClientSock.recvfrom(BUFFERSIZE)
    if not data:
        break
    print(data.decode('utf-8'))
udpClientSock.close()
Python网络编程

Python网络编程


udp模拟QQ聊天,多线程,两边同时接收,发送

#服务器
from threading import Thread
from socket import *
import time
BUFFERSIZE = 1024

#接收数据,打印
def receive_data():
    global dest_addr
    while True:
        data,addr = udpSocket.recvfrom(BUFFERSIZE)
        dest_addr = addr
        print("\r>>[%s] receive %s from %s
<<"%(time.ctime(),data.decode('utf-8'),addr),end='')

def send_data():
    while True:
        data = input("<<")
        udpSocket.sendto(data.encode('utf-8'),dest_addr)


udpSocket = None#一般空对象为None,空字符串为''
dest_addr = None

def main():
    global udpSocket
    udpSocket = socket(AF_INET,SOCK_DGRAM)
    udpSocket.bind(('',45678))

    receive_thread = Thread(target=receive_data)
    send_thread = Thread(target=send_data)

    receive_thread.start()
    send_thread.start()

    receive_thread.join()
    send_thread.join()

if __name__ == '__main__':
    main()

#客户端
from threading import Thread
from socket import *
import time
BUFFERSIZE = 1024

#接收数据,打印
def receive_data():
    while True:
        data,addr = udpSocket.recvfrom(BUFFERSIZE)
        print("\r>>[%s] receive %s from %s
<<"%(time.ctime(),data.decode('utf-8'),addr),end='')

#发送数据
def send_data():
    while True:
        data = input("<<")
        udpSocket.sendto(data.encode('utf-8'),('127.0.0.1',45678))


udpSocket = None#一般空对象为None,空字符串为''

def main():
    global udpSocket
    udpSocket = socket(AF_INET,SOCK_DGRAM)
    udpSocket.bind(('', 45679))

    receive_thread = Thread(target=receive_data)
    send_thread = Thread(target=send_data)

    receive_thread.start()
    send_thread.start()

    receive_thread.join()
    send_thread.join()

if __name__ == '__main__':
    main()

Python网络编程

Python网络编程

udpSocketClient.recvfrom(1024)报错问题:

参考:https://stackoverflow.com/questions/35805664/socket-recvfrom1024-throws-socket-error-invalid-argument-supplied#

data,addr = udpSocketClient.recvfrom(BUFFERSIZE)
OSError: [WinError 10022] 提供了一个无效的参数。

在客户端接收数据之前,操作系统需要知道该进程的端口号,才能将数据发送给客户端。所以不能一上来就直接接收,需要先给客户端帮一个端口udpSocketClient.bind('ip',port)或者先让客户端发数据udpSocketClient.send(data,('destip',port)),这样客户端先发数据后,系统就会自动给客户端分配一个端口,这样客户端就能够接收数据了。

UDP广播

TCP没有广播,

import socket
import time
#这里的broadcast表示受限的广播地址,也可以直接写广播地址
# dest = ('',8080)
dest = ('172.24.54.255',8080)


s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#如果想要发送广播数据,需要对套接字进行设置,才能发送广播数据,否则不能发送广播数据
s.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)

s.sendto("Hi".encode('utf-8'),dest)
print("等待对方回复")

while True:
    data,address = s.recvfrom(2048)
    print("[%s] received from %s:%s"%(time.ctime(),address,data.decode('utf-8')))
Python网络编程

Python网络编程


Cisco packet tracer

下载:链接:https://pan.baidu.com/s/1OaJtyCIv1nJvMVcUhS-Cfw 密码:h6os

arp -a查看IP地址和Mac地址对应情况

Python网络编程

arp -d清空本地缓存的IP地址和Mac地址对应信息

路由器配置:

Python网络编程

配置主机IP地址,子网掩码,默认网关,配置路由器IP地址,子网掩码,静态路由,开启路由器端口。

Python网络编程

Python网络编程

A网段a和B网段b通信:

看a有没有默认网关,没有直接挂,

a通过arp解析网关的Mac地址

A将信息发给A的网关

A的网关将信息根据路由表发给下一跳路由器(不知道Mac地址的话,arp解析)

...

Python网络编程

访问网页过程:

1.DNS域名解析

看主机PC7有没有设置默认网关,没有直接挂

如果不知道默认网关的Mac地址,就arp解析默认网关的Mac地址

主机PC7发送DNS请求解析数据给默认网关,网关转发给其他路由器,直到目的DNS服务器的默认网关,

DNS服务器的网关如果不知道DNS服务器的Mac地址,同样arp解析DNS服务器Mac地址

DNS网关将DNS请求解析数据发给DNS服务器

DNS服务器解析后,将数据返回给原主机PC7

2.主机PC和HTTP服务器TCP三次握手

3.http报文传输

4.tcp4次挥手。

TCP短连接:

每发送一次数据就要进行三次握手,四次挥手:TCP握手,发送数据,TCP四次挥手。。。TCP握手,发送数据,TCP四次挥手。应用:web应用,在HTTP/1.0中,默认使用的是短连接

TCP长连接

建立一个连接后,只要没有显式关闭连接,就一直发送数据:TCP三次握手,发送数据,发送数据,TCP四次挥手。旨在建立 1 次 TCP 连接后进行多次请求和响应的交互。应用:看视频; HTTP/1.1起,默认使用长连接

Python网络编程

Python网络编程

备注:HTTP的长连接与短连接,本质上就是TCP的长连接与短连接。

TCP十种状态

Python网络编程

常见网络攻击:

DoS(Denial of Service,拒绝服务)攻击

利用TCP三次握手漏洞,客户端在发送SYN,并且收到SYN+ACK后,迟迟不发送ACK,让服务器一直等待。例如,我一台电脑开十个进程,一个进程开十个线程攻击某个网站。占用它的listen队列,使其他人不能正常访问。

DNS攻击

DNS服务器被劫持:

攻击DNS服务器,修改域名对应的ip,当用户访问某个网站时,跳转到其他的页面,例如广告,钓鱼网站。钓鱼网站先把别的网站例如淘宝的网页类容down下来,做一个和淘宝一模一样的界面,用户以为是真的淘宝,输入用户名,密码,信息就被获取了。

DNS服务器常常是重兵把守,

Python网络编程

DNS欺骗:

用户在向DNS服务器请求域名解析的时候,攻击者⽤⼀个假的 DNS 应答来欺骗⽤户计算机,给它一个加的域名,IP对应。

Python网络编程

Client的DNS查询请求和DNS Server的应答数据包是依靠DNS报文的ID标识来相互对应的。在进行域名解析时,Client首先用特定的ID号向DNS Server发送域名解析数据包,这个ID是随机产生的。DNS Server找到结果后使用此ID给Client发送应答数据包。Client接收到应答包后,将接收到的ID与请求包的ID对比,如果相同则说明接收到的数据包是自己所需要的,如果不同就丢弃此应答包。根据攻击者的查询和应答原理,可使用不同方法实现攻击,如:

(1)因为DNS Message仅使用一个简单的认证码来实施真实性验证,认证码是由Client程序产生并由DNS Server返回结果的,客户机只是使用这个认证码来辨别应答与申请查询是否匹配,这就使得针对ID认证码的攻击威胁成为可能。

arp攻击

Python网络编程

攻击值攻击通信的双方,让他们的目的ip地址对应的Mac地址都是自己的,这样攻击者就可以接收到信息,接收到信息之后,为了不被发现,存储下来之后,再发送出去。

NAT

家里上网,就分配了一个公网ip地址,猫下面连路由器,路由器下面接电脑,手机。电脑,手机访问外网,通过了网络地址转换。

Python网络编程

单进程服务器:(没啥实际应用)

服务器:

from socket import *
serverScoket = socket(AF_INET,SOCK_STREAM)
serverScoket.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#让服务器意外结束时不能等待2MSL时间,就可以立即使用该端口
serverScoket.bind(("127.0.0.1",12345))
serverScoket.listen(5)

while True:
    clientScoket,addr = serverScoket.accept()
    print("serve for:",addr)

    try:
        while True:
            data = clientScoket.recv(1024)
            if len(data)>0:
                print("receve %s from %s"%(data.decode('utf-8'),addr))
            else:
                print("%s客户端已关闭"%(addr))
                break
    except:
        pass
    finally:
        clientScoket.close()

serverScoket.close()

客户端:

from socket import *
clientSocket = socket(AF_INET,SOCK_STREAM)
clientSocket.connect(("127.0.0.1",12345))

while True:
    data = input("please input the data:")
    if len(data)>0:
        clientSocket.send(data.encode('utf-8'))
    else:
        break
clientSocket.close()

同一时刻只能为一个用户服务,当服务器为一个客户服务时,而另外的新客户进行连接,只要服务器listen队列有空闲的位置,就会为这个新客户端进行连接,并且客户端可以发送数据,但当服务器为这个新客户端服务时,可能一次性把所有数据接收完毕。

多进程服务器:

服务器端:

from socket import *
from multiprocessing import *
#处理客户端的请求并为其服务
def dealWithClient(clientScoket,addr):
    while True:
        try:
            data = clientScoket.recv(1024)
        except:
            print("%s客户端已关闭" % (str(addr)))#客户端直接X掉关闭
            break
        if len(data) > 0:
            print("receve %s from %s" % (data.decode('utf-8'), addr))
        else:
            print("%s客户端已关闭" % (str(addr)))
            break
    clientScoket.close()

def main():
    serverScoket = socket(AF_INET, SOCK_STREAM)
    serverScoket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 让服务器意外结束时不能等待2MSL时间,就可以立即使用该端口
    serverScoket.bind(("127.0.0.1", 12345))
    serverScoket.listen(5)

    try:
        while True:
            clientSocket,addr = serverScoket.accept()
            # print(addr,type(addr))#add是一个元组
            print("serve for %s"%(str(addr)))
            client = Process(target=dealWithClient,args=(clientSocket,addr))
            client.start()

            #子进程已经copy了一份,这里可以关闭
            clientSocket.close()
    except:
        pass
    finally:
        serverScoket.close()




if __name__ == '__main__':
    main()

多线程服务器:

服务器端

from socket import *
from threading import  Thread
import time
#处理客户端的请求并为其服务
def dealWithClient(clientScoket,addr):
    while True:
        try:
            data = clientScoket.recv(1024)
        except:
            print("%s客户端已关闭" % (str(addr)))
            break
        if len(data) > 0:
            print("receve %s from %s" % (data.decode('utf-8'), addr))
        else:
            print("%s客户端已关闭" % (str(addr)))
            break
    clientScoket.close()

def main():
    serverScoket = socket(AF_INET, SOCK_STREAM)
    serverScoket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 让服务器意外结束时不能等待2MSL时间,就可以立即使用该端口
    serverScoket.bind(("127.0.0.1", 12345))
    serverScoket.listen(5)

    try:
        while True:
            time.sleep(5)
            clientSocket,addr = serverScoket.accept()
            # print(addr,type(addr))#add是一个元组
            print("serve for %s"%(str(addr)))
            client = Thread(target=dealWithClient,args=(clientSocket,addr))
            client.start()

            #线程中共享这个套接字,这里不能关闭
            # clientSocket.close()
    except:
        pass
    finally:
        serverScoket.close()




if __name__ == '__main__':
    main()

单进程服务器_非阻塞

from socket import  *


clientSocketList = []

def main():
    serverSocket = socket(AF_INET,SOCK_STREAM)
    serverSocket.bind(('127.0.0.1',12345))
    serverSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    serverSocket.listen(5)
    # 将套接字设置为非阻塞,这样就不会卡在accept处了
    serverSocket.setblocking(False)

    while True:
        try:
            clientSocket,addr = serverSocket.accept()
        except :
            pass
        else:
            print("一个新客户端到来:%s"%str(addr))
            #将clientSocket设置为非阻塞,在recv处就不阻塞了
            clientSocket.setblocking(False)
            clientSocketList.append((clientSocket,addr))

        for clientSocket,addr in clientSocketList:
            try:
                data = clientSocket.recv(1024)
            except BlockingIOError:
                pass
            except ConnectionResetError:
                clientSocket.close()
                clientSocketList.remove((clientSocket, addr))
                print("%s已经下线" % str(addr))
                break
            else:
                if len(data)>0:
                    print("%s:%s"%(str(addr),data.decode('utf-8')))
                else:
                    clientSocket.close()
                    clientSocketList.remove((clientSocket,addr))
                    print("%s已经下线"%str(addr))


if __name__ == '__main__':
    main()
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章