查看Python中支持的编码方式:https://docs.python.org/3/library/codecs.html?highlight=utf
IP地址工具:http://ipblock.chacuo.net/
查看应用程序进程PID:
1.启动任务管理器,点击进程:默认是这样的:
2.点击查看, 选择列,勾上PID即可。
进程PID与进程使用的端口号是不同的概念。
Windows下看看哪个端口被占用及占用的进程
命令:
netstat -aon | findstr "8080"
netstat -an#查看所有的端口使用情况
8080是要查看的端口号。
例如在pycharm中建立一个网络通信
TCP
#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.socket fd=932, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 12345), raddr=('127.0.0.1', 64764)>,包含了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:breaktcpClientSock.send(bytes(data,encoding='utf-8'))# tcpClientSock.send(data.encode('utf-8'))data = tcpClientSock.recv(BUFSIZE)if not data:breakprint(data.decode('utf-8'))
tcpClientSock.close()
服务器端使用的端口好为:12345,运行服务器和客户端后,发现客户端使用的端口号为12037,执行命令:
查看到端口12345和12037被占用了,在任务管理其中查看到是Python3占用了这两个端口
为什么使用端口,而不直接使用PID了?
PID也可以唯一表示一个进程呀,这是因为使用PID代价太大,双方通信之前,得需要提前知道对方的PID,但是PID是操作系统自动分配的,人工去查很麻烦。如果直接使用端口的话比较方便,一般一个服务的端口比较固定。
同一个系统中,端口不能重复。
通信调试助手:模拟发送和接收情况。链接:https://pan.baidu.com/s/1GJTYxvUKGIHB9KM-D0CVug 密码:31z1
TCP连接中listen参数问题:
连接队列长度:客户端已经和服务器建立连接,或者客户端正在和服务器建立连接,但是还没有被服务器accept的最大个数。即同时等待accept的个数,和已经accept的个数没有关系。
Windows和Mac中严格安装这个参数来,Linux中内核会自己处理。
服务器
from socket import *
import times = 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)
服务器端处理过慢,可能导致客户端连接超时报错。
本地的进程间通信:
网络间通信:socket
AF_UNIX | 本地通信 |
AF_INET | IPv6 |
AF_NETLINK | |
SOCK_STREAM | TCP |
SOCK_DGRAM | UDP |
UDP在局域网中几乎不会丢包
Python3中发送数据需要bytes类型,不能是字符串类型,不然会报错:
发送前将字符串编码,接收后将字符串解码。
中文一般是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'\xe9\xbb\x84\xe9\x93\x81\xe7\x89\x9b'
bytes(data,encoding='utf-8')
b'\xe9\xbb\x84\xe9\x93\x81\xe7\x89\x9b'
UDP简单示例
服务器:
#服务器
from socket import *
import time
def main():udpServerSocket = socket(AF_INET,SOCK_DGRAM)#这里服务器可以绑定自己的ip地址和端口号,ip地址一般为空,表示本机的所有可用ip地址,#可以是网络地址,本地地址和127.0.0.1udpServerSocket.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:breakudpClientSock.sendto(data.encode('utf-8'),("172.24.55.11",12346))data,ADDR = udpClientSock.recvfrom(BUFFERSIZE)if not data:breakprint(data.decode('utf-8'))
udpClientSock.close()
udp模拟QQ聊天,多线程,两边同时接收,发送
#服务器
from threading import Thread
from socket import *
import time
BUFFERSIZE = 1024#接收数据,打印
def receive_data():global dest_addrwhile True:data,addr = udpSocket.recvfrom(BUFFERSIZE)dest_addr = addrprint("\r>>[%s] receive %s from %s\n<<"%(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 = Nonedef main():global udpSocketudpSocket = 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\n<<"%(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 udpSocketudpSocket = 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()
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 = ('<broadcast>',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')))
Cisco packet tracer
下载:链接:https://pan.baidu.com/s/1OaJtyCIv1nJvMVcUhS-Cfw 密码:h6os
arp -a查看IP地址和Mac地址对应情况
arp -d清空本地缓存的IP地址和Mac地址对应信息
路由器配置:
配置主机IP地址,子网掩码,默认网关,配置路由器IP地址,子网掩码,静态路由,开启路由器端口。
A网段a和B网段b通信:
看a有没有默认网关,没有直接挂,
a通过arp解析网关的Mac地址
A将信息发给A的网关
A的网关将信息根据路由表发给下一跳路由器(不知道Mac地址的话,arp解析)
...
访问网页过程:
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起,默认使用长连接
备注:HTTP的长连接与短连接,本质上就是TCP的长连接与短连接。
TCP十种状态
常见网络攻击:
DoS(Denial of Service,拒绝服务)攻击
利用TCP三次握手漏洞,客户端在发送SYN,并且收到SYN+ACK后,迟迟不发送ACK,让服务器一直等待。例如,我一台电脑开十个进程,一个进程开十个线程攻击某个网站。占用它的listen队列,使其他人不能正常访问。
DNS攻击
DNS服务器被劫持:
攻击DNS服务器,修改域名对应的ip,当用户访问某个网站时,跳转到其他的页面,例如广告,钓鱼网站。钓鱼网站先把别的网站例如淘宝的网页类容down下来,做一个和淘宝一模一样的界面,用户以为是真的淘宝,输入用户名,密码,信息就被获取了。
DNS服务器常常是重兵把守,
DNS欺骗:
用户在向DNS服务器请求域名解析的时候,攻击者⽤⼀个假的 DNS 应答来欺骗⽤户计算机,给它一个加的域名,IP对应。
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攻击
攻击值攻击通信的双方,让他们的目的ip地址对应的Mac地址都是自己的,这样攻击者就可以接收到信息,接收到信息之后,为了不被发现,存储下来之后,再发送出去。
NAT
家里上网,就分配了一个公网ip地址,猫下面连路由器,路由器下面接电脑,手机。电脑,手机访问外网,通过了网络地址转换。
单进程服务器:(没啥实际应用)
服务器:
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))breakexcept:passfinally: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掉关闭breakif len(data) > 0:print("receve %s from %s" % (data.decode('utf-8'), addr))else:print("%s客户端已关闭" % (str(addr)))breakclientScoket.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:passfinally: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)))breakif len(data) > 0:print("receve %s from %s" % (data.decode('utf-8'), addr))else:print("%s客户端已关闭" % (str(addr)))breakclientScoket.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:passfinally: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 :passelse: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:passexcept ConnectionResetError:clientSocket.close()clientSocketList.remove((clientSocket, addr))print("%s已经下线" % str(addr))breakelse: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()