socket

socket编程:

c/s架构--打印机,web服务器
osi:一个完整的计算机系统由硬件,操作系统,应用软件组成
学习socket首先需要学互联网协议
cs架构的软件是基于网络来进行通信的
两个服务器机子的通信连接采用光缆,双绞线或者无线电波。
发送的01010101之类的数据需要进行分组才有意义,分组是数据链路层干的。常用以太网
网络层是用ip,如果要传去的地方和原地方不在同一个以太网,就要传去ip然后给网关传去其他的以太网。
192.158.356.1:27015,前面的ip是以太网的标识,网卡号是以太网中某个机器的表示,后面的端口号是
机器上开启的某个应用程序的标识,pid是某个软件的某个进程的标识。

socket封装了tcp/udp协议,是他们的简单的接口,使用socket就遵守了tcp/udp协议
开发效率就高了很多很多。

套接字:

基于文件的套接字:可以实现一台机器上的两个程序进行通信
AF_UNIX
基于网络的套接字:可以实现一台机器上的两个程序进行通信
AF_INET
主要学习AF_INET

套接字工作场景(比喻):

1.买手机
2.绑个手机卡
3.开机
4.等电话
5.拿到一个电话链接
6.收发消息
7.断开电话链接
8.关机

 

简单示例
1.服务端.py的程序
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#第一个参数代表基于网络通信,第二个参数代表基于tcp协议
#以上为买电话
phone.bind(('127.0.0.1',8000))
#以上ip地址和端口,类似插手机卡
phone.listen(5)
#5代表最多可以有几个电话呼入,以上类似于电话开机
print('____>')
conn,addr=phone.accept()
#以上是等电话然后拿到对方的电话链接,前面conn是电话链接,后面的addr是手机号
print('---->')
msg=conn.recv(1024)
print('客户端发来的消息是',msg)
#以上代表最多能收到1024的数据
conn.send(msg.upper())   #注意发送和接收的数据必须是二进制的数据
#以上为发送消息
conn.close()
#关闭本次通话
phone.close()
#关闭手机

 

2.客户端.py的程序
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#买电话
phone.connect(('127.0.0.1',8000)) #拨通对方的电话号码
phone.send(bytes('hello',encoding='utf-8'))
data=phone.recv(1024)
print('收到服务端发来的消息:',data)

 

tcp协议:

TCP 三次握手就好比两个人在街上隔着50米看见了对方,但是因为雾霾
等原因不能100%确认,所以要通过招手的方式相互确定对方是否认识自己。
步骤:
张三首先向李四招手(syn),李四看到张三向自己招手后,向对方点了点头
挤出了一个微笑(ack)。张三看到李四微笑后确认了李四成功辨认出了自己(进入estalished状态)。
但是李四还有点狐疑,向四周看了一看,有没有可能张三是在看别人呢,
他也需要确认一下。所以李四也向张三招了招手(syn),张三看到李四向自己招手
后知道对方是在寻求自己的确认,于是也点了点头挤出了微笑(ack),李四看到
对方的微笑后确认了张三就是在向自己打招呼(进入established状态)。
于是两人加快步伐,走到了一起,相互拥抱。

 

 

我们看到这个过程中一共是四个动作,张三招手--李四点头微笑--李四招手--张三点头
微笑。其中李四连续进行了2个动作,先是点头微笑(回复对方),然后再次招手(寻求确认)
,实际上可以将这两个动作合一,招手的同时点头和微笑(syn+ack)。于是四个动作就简化
成了三个动作,张三招手--李四点头微笑并招手--张三点头微笑。这就是三次握手的本质,
中间的一次动作是两个动作的合并。

tcp协议是好人协议,所有发过来的都会进行回应。很容易被黑客利用
TCP断开链接的过程和建立链接的过程比较类似,只不过中间的两部并不总是会合成一
步走,所以它分成了4个动作,张三挥手(fin)——李四伤感地微笑(ack)——李四
挥手(fin)——张三伤感地微笑(ack)。

 

内外网IP定义

    内网IP地址就是私有IP地址,不允许在公网上面传递,只能供内部使用。内网使用了
私有地址无法访问internet 会用到NAT-地址转换技术,将内部的私有地址转换为可以
访问internet的外网地址让内部可以上网。外网IP地址就是除了私有地址和被保留的
地址外的所有地址,需要申请才能使用。
ABC三类地址中划分出了三类私有地址:
    A类10.0.0.0~10.255.255.255
    B类172.16.0.0~172.31.255.255
    C类192.168.0.0~192.168.255.255

内网概念

    即所说的局域网,比如学校的局域网,局域网内每台计算机的IP地址在本局域网
    内具有互异性,是不可重复的。但两个局域网内的内网IP可以有相同的。

外网概念

    即互联网,局域网通过一台服务器或是一个路由器对外连接的网络,这个IP地址
是惟一的。也就是说内网里所有的计算机都是连接到这一个外网IP上,通过这一个
外网IP对外进行交换数据的。也就是说,一个局域网里所有电脑的内网IP是互不相
同的,但共用一个外网IP。(用ipconfig/all查到的IP是你本机的内网IP;在网页上
看到的是你连接互联网所使用的IP,即外网)
    127开头的IP主要用于测试 如:127.0.0.1 本机地址,主要用于测试。用汉语表示
,就是“我自己”。在baiWindows系统中,这个地址有一个别名“Localhost”。
寻址这样一个地址,是不能把它发到网络接口的。除非出错,否则在传输介质上永
远不应该出现目的地址为“127.0.0.1”的数据包。

改良款客户端与服务端程序

1.服务端.py的程序
from socket import *
ip_port=('10.38.237.150',5038)
devicenum=5
buffer_size=1024
tcp_serve=socket(AF_INET,SOCK_STREAM)#第一个参数代表基于网络通信,第二个参数代表基于tcp协议
tcp_serve.bind(ip_port)
tcp_serve.listen(devicenum)
while 1:
    conn,addr=tcp_serve.accept()
    print('双向链接时:',conn)
    print('客户端地址:',addr)
    while 1:
        try:
            msg=conn.recv(buffer_size)
            print('客户端发来的消息是',msg)
            if not msg: break #这里用于如果客户端强行断开不断发空就跳出
            conn.send(msg.upper())   #注意发送和接收的数据必须是二进制的数据
        except Exception:
            break    #用于如果客户端强行断掉不会报错
    #以上为发送消息
    conn.close()
tcp_serve.close()

 

2.客户端.py的程序
from socket import *
ip_port=('10.38.237.150',5038)
devicenum=5
buffer_size=1024
tcp_client=socket(AF_INET,SOCK_STREAM)#第一个参数代表基于网络通信,第二个参数代表基于tcp协议
tcp_client.connect(ip_port)
target=1
if target==1:
    while 1:
        neirong=input('请输入发送内容')
        if neirong =='':   #防止输入为空,导致程序卡死
            continue
        if neirong =='jieshu':
            target=0
        tcp_client.send(bytes(neirong,encoding='utf-8'))
        print('客户端已经发送消息')
        data=tcp_client.recv(buffer_size)
        print('服务端发来的消息是',data.decode('utf-8'))
tcp_client.close()
 

 

3.使用tcp实现远程控制

服务端程序:
from socket import *
import subprocess
ip_port=('10.38.237.150',5038)
devicenum=5
buffer_size=1024
tcp_serve=socket(AF_INET,SOCK_STREAM)#第一个参数代表基于网络通信,第二个参数代表基于tcp协议
tcp_serve.bind(ip_port)
tcp_serve.listen(devicenum)
while 1:
    conn,addr=tcp_serve.accept()
    print('双向链接时:',conn)
    print('客户端地址:',addr)
    while 1:
        try:
            msg=conn.recv(buffer_size)
            print('客户端发来的消息是',msg)   #msg为发过来的命令
            if not msg: break #这里用于如果客户端强行断开不断发空就跳出
            res=subprocess.Popen(msg.decode('gbk'),shell=True, #使用cmd执行
                                 # 把命令执行的结果放进管道
                                 stderr=subprocess.PIPE,  #出错的放进管道stderr
                                 stdout=subprocess.PIPE, #结果输出口stdout
                                 stdin=subprocess.PIPE  #结果放进管道
                                 )
            err=res.stderr.read()  #查看有没有出错
            if err:
                cmd_res=err #如果有错就是接收err的内容
            else:
                cmd_res=res.stdout.read()#没错就读取程序在cmd运行的结果
            conn.send(cmd_res)   #注意发送和接收的数据必须是二进制的数据
        except Exception:
            break    #用于如果客户端强行断掉不会报错
    #以上为发送消息
    conn.close()
tcp_serve.close()

 

客户端程序:
from socket import *
ip_port=('10.38.237.150',5038)
devicenum=5
buffer_size=1024
tcp_client=socket(AF_INET,SOCK_STREAM)#第一个参数代表基于网络通信,第二个参数代表基于tcp协议
tcp_client.connect(ip_port)
while 1:
    neirong=input('请输入发送内容')
    if neirong =='':   #防止输入为空,导致程序卡死
        continue
    if neirong =='quit': break
    tcp_client.send(bytes(neirong,encoding='gbk'))
    print('客户端已经发送消息')
    data=tcp_client.recv(buffer_size)
    print('服务端发来的消息是',data.decode('gbk'))
tcp_client.close()

 

4.如果重启服务端的时候显示地址已经被使用,可以加入socke配置,重用ip和端口

tcp_serve=socket(AF_INET,SOCK_STREAM)
tcp_serve=setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是这句话加在bind前进行ip端口重用
tcp_serve.bind(ip_port)

基于udp的套接字

1.简单的实现

udp服务端程序
from socket import *
ip_port=('10.38.237.150',5038)
buffsize=1024
udp_server=socket(AF_INET,SOCK_DGRAM) #使用udp套接字
udp_server.bind(ip_port)
while 1:
    data,addr=udp_server.recvfrom(buffsize)
    print('接收到的数据',data) #可见接收到的是一个元组,元组的第一位是发的数据
    #第二位是客户端的ip地址和端口
    print('客户端的地址',addr)
    udp_server.sendto(data.upper(),addr)

 

udp客户端程序
from socket import *
ip_port=('10.38.237.150',5038)
buffsize=1024
udp_client=socket(AF_INET,SOCK_DGRAM) #数据报
while 1:
    neirong=input('请输入内容:')
    udp_client.sendto(bytes(neirong,encoding='utf8'),ip_port)
    data,addr=udp_client.recvfrom(buffsize)
    print(data.decode('utf8'))

 

2.udp的优势:

udp可以与同时和多个客户端进行沟通。
而tcp时间内只能和一个客户端进行沟通,其他那些进来的客户端会被挂
起,等到正在沟通的客户端断联其他的客户端才能进去沟通

3.使用udp实现ntp

udp服务端程序
from socket import *
import time
ip_port=('10.38.237.150',5038)
buffsize=1024
udp_server=socket(AF_INET,SOCK_DGRAM) #使用udp套接字
udp_server.bind(ip_port)
while 1:
    data,addr=udp_server.recvfrom(buffsize)
    print('接收到的数据',data) #可见接收到的是一个元组,元组的第一位是发的数据
    #第二位是客户端的ip地址和端口
    print('客户端的地址',addr)
    backtime=time.strftime('%Y-%m-%d-%X')
    udp_server.sendto(backtime.encode('utf8'),addr)

 

udp客户端程序
from socket import *
ip_port=('10.38.237.150',5038)
buffsize=1024
udp_client=socket(AF_INET,SOCK_DGRAM) #数据报
while 1:
    neirong=input('请输入内容:')
    udp_client.sendto(bytes(neirong,encoding='utf8'),ip_port)
    data,addr=udp_client.recvfrom(buffsize)
    print(data.decode('utf8'))

 

 

posted @ 2020-10-21 21:42  克莱比-Kirby  阅读(118)  评论(0)    收藏  举报