Python——socket&socketserver(网络编程)

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket和file的区别:

  • file模块是针对某个指定文件进行【打开】【读写】【关闭】
  • socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

在Scoket里的表达方式:

Socket Families(地址簇) IP层

  • socket.AF_UNIX unix本机进程间通信
  • socket.AF_INET IPV4 
  • socket.AF_INET6 IPV6

Socket Types 传输层

  • socket.SOCK_STREAM #for tcp
  • socket.SOCK_DGRAM #for udp

socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。

特点:

1. 默认socket.socket()为:IPv4地址与TCP的传输

2. 2.X版本能发字符串,比特,字节。但在3.X中只能发bit类型。

3. 需要注意的是粘包问题(就是两个发送的数据全部在一起)

4. DUP传输时,需要注意防火墙是否允许通过。

粘包:

发生在发送端的粘包:

  由于两个数据的发送时间间隔短+数据的长度小,再加上TCP的优化机制(将两个较小数据和发送时间间隔很短的合并在一起发送,这样会减少TCP对数据的确认包)

发送在接收端的粘包:

  由于TCP协议中所传输的数据无边界,所以来不及接收的多条数据会在接收方的内核缓存并粘起来。

可以通过struct模块来解决。

import struct

value = 345223123
value_struct = struct.pack('i',value)  #通过pack来将字节数控制到4个字节,那么接收方先进行4字节的接收,再接收其他的就不存在粘包问题了。
print(value_struct)  #b'\xd3\xaf\x93\x14'
import struct

client_data = {'client_info':'login',#用户信息
               'client_status':False,#用户状态
               'client_name':None,#用户名
               'client_pwd':None,#密码
               'server_con':None,#服务器回复信息
               'data_name':None,#如有数据,将写数据的名字
               'data_size':None,#如有数据,将写数据的大小
               }

data_len = len(client_data)
print(data_len)
data_len_struct = struct.pack('i',data_len)
print(data_len_struct)
print(struct.unpack('i',data_len_struct)[0])

标准配置:
1. 可以允许多个用户接入,但只有一个能进行数据传输,只有一个断了,下一个才能进行数据传输。

2. 服务器一直在监听状态。

3. 用户无法输入空的字符串

#Server
import socket
server = socket.socket()
server.bind(('127.0.0.1',8888))
server.listen(2)   #需要监听用户的个数
while True:
    conn,addr = server.accept()   #接收一个新的连接。conn是每进来一个连接,都为其创建一个实例,addr为连接进来的IP地址。
    while True:
        data = conn.recv(8192)    #监听应该为连接进来的实例,多大为10M左右,官方建议最多不超过8192K。
        if not data:break    #为了解决客户端一断开就进入死循环。因为客户端一断开就会发送空数据。
        print('recv:',data.decode())
        conn.send(data.upper())
server.close()
#Client
import socket
client = socket.socket()
client.connect(('127.0.0.1',8888))
while True:
    choice = input('==>:')
    if len(choice) == 0: continue
    client.send(choice.encode())     #把目前的unicode字符编码转换为utf-8字符编码
    data = client.recv(1024)
    print(data.decode())        #将utf-8字符编码进行转换
client.close()


一个简单的SSH:

存在的问题:

1. 在检查字符长度时,可能存在中文和英文长度上的差异,就是server发过来的要比实际发送数据过来的要小。

__author__="XinB"
#Server
import socket,os
server = socket.socket()      #将socket模块导入并赋值
server.bind(('localhost',9999))   #写服务器的Ip地址和接收的端口号
server.listen(2)                   #开始监听,并设置同时进入的客户端数量。
while True:
    conn,addr = server.accept()      #开始接收客户端(会卡到该过程,等待用户接入)
    while True:
        data = conn.recv(1024)      #如果用户接入,接收发过来的信息。
        if not data:break          #如果用户发过来的信息是空,那么将结束用户并断开该用户。
        cmd_recv = os.popen(data.decode()).read()  #如果不为空,那么将用户传输过来的,应用到dos系统命令中。
        if len(cmd_recv) == 0:        #判断如果返回的值为空(用户输入错误将会返回为空),那么将打印。
            cmd_recv  =  '无效输出'
        conn.send(str(len(cmd_recv.encode('utf-8'))).encode('utf-8'))  #将系统返回的值,计算值的大小,将大小发送给用户。
        conn.send(cmd_recv.encode('utf-8'))       #再将值发送给客户端。
server.close()
__author__="XinB"
#Client
import socket
client = socket.socket()   #将socket模块导入并赋值
client.connect(('localhost',9999))   #客户端填写服务器端Ip地址和端口号
while True:
    choice = input('>>>:').strip()    #循环用用户输入字符串
    if len(choice) == 0: continue    #如果用户输入的为空将返回重新输入。
    client.send(choice.encode())    #如果不为空,将发送给服务器端
    cmd_res = client.recv(1024)   #第一次接收的是将要发过来数据的长度。
    resved_size = 0    #创建一个初始为0的变量,将判断接收的数据是否已经满足要求。
    resved_data = ''   #创建一个空字符,将显示所返回的结果。
    while resved_size < int(cmd_res.decode()):  #循环在resved_size 小于服务器返回值的时候。
        data = client.recv(1024)        #开始接收数据
        resved_size+=len(data)         #将接收数据的大小添加到空变量
        resved_data += data             #将接收到的数据添加到空字符中。
    else:
        print(resved_data.decode())     #如果全部接收完毕,将所有的数据打印出来。
client.close()

socket-server
特点:

  • socket的再封装版本。使其更加简化
  • 可以实现多用户接入,socket-server主要用于server端,client端使用socket写即可。
  • 必须继承socket.BaseRequestHandler的父类。
  • 所有的客户端请求,全部由handle方法处理。
  • 每一个客户端请求,都会实例化一个server classes。
  • 3.Xpython 由于客户端断开后会报连接错误,所以需要try一下。
  • 请求过来之前可以使用setup,请求处理中可以用handle,请求处理之后可以使用finish。

socket-server分类:
1. class socketserver.TCPServer()
使用与TCP的连接使用
2. class socketserver.UDPServer()
使用与UDP的连接使用,所使用的语法和TCPserver是一样的。
3. class socketserver.UnixStreamServer()
在本机,用于两个不同进程之间的通信使用。

初始化案例:

import socketserver

class Myserver(socketserver.BaseRequestHandler):                                        #必须要继承该类
    def handle(self):                                                                   #必须填写方法
        '''
        1. handle方法针对的是每一个客户端都会独立出来一个。而不是公用这一个handle方法。
        2. handle必须的原因是父类有相同方法,如果要自定义就要自己写此方法。
        3. self.client_address为客户端的Ip地址加端口。
        4. self.server为服务器的IP地址。
        5. self.request为客户端连接信息。(跟socket的conn一样)
        :return:
        '''
        print(self.request)
        self.request.send()                                                             #发送
        self.request.recv()                                                             #接收

server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),Myserver)
server.serve_forever()

 

 

posted @ 2017-12-27 16:48  新兵蛋Z  阅读(102)  评论(0编辑  收藏  举报