54----socket

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/c_first/article/details/82291255

C/S架构

客户端与服务端

硬件C/S架构:如电脑与打印机

软件C/S架构:web服务

B/S架构

实际就是C/S,只不过客户端是浏览器

 

服务端运行,等待客户端连接,为其提供服务


socket层

  socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

  我们无需深入理解tcp/udp等等,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的


套接字工作流程

服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束


套接字函数

服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来
 
客户端套接字函数
s.connect()     主动初始化TCP服务器连接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
 
公共用途的套接字函数
s.recv()            接收TCP数据
s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     连接到当前套接字的远端的地址
s.getsockname()     当前套接字的地址
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字
 
面向锁的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操作的超时时间
s.gettimeout()      得到阻塞套接字操作的超时时间
 
面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件

基于TCP的套接字

tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端

tcp是双向链接,就像打电话

流式协议,没有开头结束之说

服务端

import socket
ip_port=('127.0.0.1',9000)  #电话卡
BUFSIZE=1024                #收发消息的尺寸
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(ip_port) #手机插卡
s.listen(5)     #手机待机
 
 
conn,addr=s.accept()            #手机接电话
# print(conn)
# print(addr)
print('接到来自%s的电话' %addr[0])
 
msg=conn.recv(BUFSIZE)             #听消息,听话
print(msg,type(msg))
 
conn.send(msg.upper())          #发消息,说话
 
conn.close()                    #挂电话
 
s.close()                       #手机关机

客户端

import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 
s.connect_ex(ip_port)           #拨电话
 
s.send('linhaifeng nb'.encode('utf-8'))         #发消息,说话(只能发送字节类型)
 
feedback=s.recv(BUFSIZE)                           #收消息,听话
print(feedback.decode('utf-8'))
 
s.close()                                       #挂电话

循环通信:  

服务端

import socket
ip_port=('127.0.0.1',8081)#电话卡
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
s.bind(ip_port) #手机插卡
s.listen(5)     #手机待机
 
 
while True:                         #新增接收链接循环,可以不停的接电话
    conn,addr=s.accept()            #手机接电话
    # print(conn)
    # print(addr)
    print('接到来自%s的电话' %addr[0])
    while True:                         #新增通信循环,可以不断的通信,收发消息
        msg=conn.recv(BUFSIZE)             #听消息,听话
 
        # if len(msg) == 0:break        #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生
 
        print(msg,type(msg))
 
        conn.send(msg.upper())          #发消息,说话
 
    conn.close()                    #挂电话
 
s.close()                       #手机关机

客户端

import socket
ip_port=('127.0.0.1',8081)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 
s.connect_ex(ip_port)           #拨电话
 
while True:                             #新增通信循环,客户端可以不断发收消息
    msg=input('>>: ').strip()
    if len(msg) == 0:continue
    s.send(msg.encode('utf-8'))         #发消息,说话(只能发送字节类型)
 
    feedback=s.recv(BUFSIZE)                           #收消息,听话
    print(feedback.decode('utf-8'))
 
s.close()                                       #挂电话

重启时遇到的问题:

因为链接仍然存在,未断开,服务端仍然存在四次挥手的time_wait状态在占用地址

解决方法:

phone_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 加上这句,表示重用
 
phone_server.bind(('127.0.0.1',8080))

linux中解决

发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
vi /etc/sysctl.conf
 
编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
  
然后执行 /sbin/sysctl -p 让参数生效。
  
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
 
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
 
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
 
net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

模拟SSH远程执行命令:

 服务端:

import socket,subprocess
phone_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 基于网络通信,(SOCK_STREAM流式,也就是TCP)(SOCK_DGRAM,数据报形式,报文,也就是UDP)
phone_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 
phone_server.bind(('127.0.0.1',8080)) # 元组形式:('ip',端口)
phone_server.listen(5) # 最大挂起的连接数5
print('开始执行----')
while True:
    conn,client_addr=phone_server.accept()   # accept等待链接:conn:链接;client_addr:客户端地址
    print('客户端:',client_addr)
    while True: # 通信循环
        try:
            cmd=conn.recv(1024) # 接收消息,1024字节
            res=subprocess.Popen(cmd.decode('utf-8'),
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
 
            conn.sendall(stdout+stderr)
            print(stdout+stderr)
        except Exception:
            break
    conn.close()    # 断开链接
phone_server.close() # 关闭服务

远程端

import socket
phone_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone_client.connect(('127.0.0.1',8080))
while True:
    cmd=input('>>>:').strip()
    if not cmd:continue
    phone_client.send(cmd.encode('utf-8')) # 只能发字节
    # print("发空格")
    cmd_res=phone_client.recv(1024)
    print(cmd_res.decode('gbk'))
phone_client.close()    # 关闭

猜你喜欢

转载自blog.csdn.net/c_first/article/details/82291255
54