day31,socket 客户端 服务端

                                               网络编程之socket

socket 是应用层tcp和ip协议的通信,是一种中间的抽象层,它就像是一个接口,里面封装了很多的协议,(也可以自己重写socket)

socket中的套接字分为两种

    1.AF_UNIX 是一个套接字

         AF_UNIX  是一种基于文件的套接字  

         unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,

         两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

    2,AF_INET是一种套接字

         (还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,

         要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中, AF_INET是使用最广泛的一个,

          python支持很多种地址家族,但是由于大部通讯都是网络通讯,所以大部分时候使用AF_INET)

扫描二维码关注公众号,回复: 6295842 查看本文章

基于tcp的socket 就是客服端和服务端,使用socket中的tcp协议来完成客服端和服务端的连接

     什么是客服端:

         客服端就是用户 使用的程序通过用户使用程序来传输信息的这个介子,这个就是客服端

     什么是服务端:

         服务端就是给客户端提供需要的数据的,就相当于是一个库

如何使用socket来连接客户端和服务端

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('接到来自%s的电话' %addr[0])

msg=conn.recv(BUFSIZE)             #听消息,听话
print(msg,type(msg))

conn.send(msg.upper())          #发消息,说话

conn.close()                    #挂电话

s.close()                       #手机关机

服务端
View Code

TCP客户端低级版

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()                                       #挂电话
View Code

这样的客户端和服务端不是完善的所以就优化了客户端和服务端(不完善是因为有太多的错误)

客户端和服务端的常见的错误

在调试过程中,可能会遇见以下错误:

问题发生原因:

1.可能是由于你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用导致!

2.三次握手或四次挥手时,发生了异常导致对方程序已经结束而服务器任然处于time_wait状态导致!

3.在高并发的场景下,由于链接的客户端太多,也会产生大量处于time_wait状态的链接

解决的方案:

第1种原因,很简单关闭之前运行的服务器即可

第2,3中原因导致的问题,有两种解决方案:

1.设置服务器重用端口

#加入一条socket配置,重用ip和端口
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,必须在bind前加
phone.bind(('127.0.0.1',8081))

2.通过调整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 时间

强行关闭链接

发生错误演示,运行上面的改进版的服务器与客户端,链接成功后直接停止客户端程序)

当客服端与服务器链接成功后,如果一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止),都会导致另一方发送问题,

在windows下,接收数据的一方在recv函数处将抛出异常

Traceback (most recent call last):
  File "C:/Users/jerry/PycharmProjects/untitled/TCP/server.py", line 9, in <module>
    conn.recv(1024)
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。

linux下,不会抛出异常会导致接收数据的一方,recv方法不断的收到空消息,造成死循环

要使应用程序能够在不同平台正常工作,那需要分别处理这两个问题

解决方案如下:

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()           
    while True:                         
        try:
            msg=conn.recv(BUFSIZE)             
            #linux不会抛出异常,会接收到空消息,这里加以判断
            if not msg:
                conn.close()
                break
            print(msg,type(msg))
            conn.send(msg.upper())        
       except ConnectionResetError:
            #只要异常发生则意味着对方以及关闭了,服务器也相应的关闭该链接
            conn.close()
            break
    conn.close()              
s.close()   
View Code

至此TCP通讯模板程序就完成了,可以不断的接收新的链接,不断的收发消息,并且不会因为客户端强制关闭而异常退出!

然后就优化客户端和服务端

改进版服务器端

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)             #听消息,听话
        print(msg,type(msg))
        conn.send(msg.upper())          #发消息,说话
    conn.close()                    #挂电话
s.close()                       #手机关机
View Code

改进版客户端

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()                                       #挂电话
View Code

猜你喜欢

转载自www.cnblogs.com/WBaiC1/p/10940495.html