• python 网络编程(三)---TCP 服务器端客户端实现


    客户端

    客户端主要有4个步骤:

    1)创建一个socket以连接服务器。

         socket = socket.socket(family, type),family参数代表地址家族,可为AF_INET(包括Internet地址,和IPV4对应)或AF_UNIX(用于同一台机器上的进程间通信)。type,家族协议,协议家族一般表示TCP通信的SOCK_STREAM或者表示UDP通信的SOCK_DGRAM。

    2)使用socket的connect方法连接服务器。

         对于AF_INET家族,连接格式如下:socket.connect(host, port) ,host代表服务器主机名或IP,port代表服务器进程所绑定的端口号。

    3)处理阶段,客户和服务器将通过send方法和recv方法通信。如:

        tcpCliSock.send(data)  
           data = tcpCliSock.recv(BUFSIZE)  #从 socket 中接收数据,最多 BUFSIZE个字符

    4)传输结束,客户通过调用socket的close方法关闭连接。

        tcpCliSock.close()

    代码如下:

    #from socket import *
    import socket
    HOST='localhost'      #服务器ip或者名,由于我们在同一台电脑上进行测试,所以 HOST里放的是本机的主机名
    PORT= 20000          #服务器端口号,端口号要与服务器上的设置完全相同(不然就没办法通讯)。
    BUFSIZE = 1024 
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)   #建立socket
    s.connect((HOST,PORT))             #连接服务器socket
    
    '''
    客户端也有一个无限循环,
    客户端的循环在以下两个条件的任意一个发生后就退出:
    用户没有输入任何内容,或服务器由于某种原因退出,
    导致 recv()函数失败。否则,在一般情况下,客户端会把用户输入的字符串
    发给服务器进行处理,然后接收并显示服务器传回来的,加了时间戳的字符串。
    '''
    
    while True:                        #通过send和recv与服务器进行通信
        data=raw_input('Enter a string that you want to send:')
        if not data:
            break
        s.send(data)           
        data=s.recv(BUFSIZE)
        if not data:
            break
        print data
    s.close()                          #关闭socket

    服务器端

    服务器端主要是有6个步骤:

    1)创建一个socket对象,方式如客户端。

    2)将socket绑定到指定地址。这是通过socket对象的bind方法来实现的:socket.bind(address) 。由AF_INET所创建的套接字,address地址必须是一个双元素元组,格式是(host,port)。如果所绑定的地址或者端口正在使用,那么将会出错。

    3)使用socket套接字的listen方法接收连接请求。socket.listen(backlog)   backlog指定最多允许多少个客户连接到服务器。listen()函数的参数只是表示最多允许多少个连接同时连进来,后来的连接就会被拒绝掉。

    4)第四步是服务器套接字通过socket的accept方法等待客户请求一个连接。connection, address = socket.accept()  。调用accept方法时,socket会进入“waiting”状态。第一个元素connection是新的socket对象,服务器必须通过它与客户通信;第二个元素 address是客户的Internet地址。

    5)处理阶段,服务器和客户端通过send和recv方法进行数据的传输。服务器调用send,并采用字符串形式向客户发送信息。send方法返回已发送的字符个数。服务器使用recv方法从客户接收信息。

    调用recv时,服务器必须指定一个整数,它对应于可通过本次方法调用来接收的最大数据量。recv方法在接收数据时会进入“blocked”状态,最后返回一个字符串,用它表示收到的数据。(怎么理解?)

    6)传输结束,调用socket的close方法关闭连接。

    代码如下:

    import socket
    from time import ctime 
    HOST = ''                                      #HOST 变量为空,表示 bind()函数可以绑定在所有有效的地址上。 
    PORT =20000                                    #选用了一个随机生成的未被占用的端口号。
    BUFSIZE =1024 #1kb 
    tcpSerSock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #建立socket
    tcpSerSock.bind((HOST,PORT))                   #绑定客户端的socket
    tcpSerSock.listen(5)                           #监听的客户端数目限制
    
    while True:                                    #这个while是对接收的客户端循环,数目为0时候停止。
        print 'waiting for connection...' 
        tcpCliSock,CliAddr=tcpSerSock.accept()     #通过socket的accept方法等待客户请求一个连接
        print '...connected from :', CliAddr
        while True:                                #开始进行数据传输
            data= tcpCliSock.recv(BUFSIZE)
            if not data:
                break
            print '[%s] %s' % (ctime(), data)      #得到客户的消息后,我们在消息前加一个时间戳然后返回
            tcpCliSock.send('[%s] %s' % (ctime(), data))   
        tcpCliSock.close()
    tcpSerSock.close()

    运行:

    存放在指定文件夹下,打开并运行(F5),记住:一定要先运行服务器端!

    客户端:

    image

    服务器端:

    image

    由上面的结果,过程是:

    服务器一直处于等待时间,客户端通过ip和端口号找到服务器,通过三次握手建立连接。接下来客户端将数据发送给服务器,服务器收到后进行确认,在数据上加上时间戳,回显给客户端。,客户端也会无线循环,直到不再输入任何数据。其中服务器端永远不会关闭,一直处于等待状态,tcpSerSock.close()不会执行。

    ps:执行

    tcpCliSock.close()有如下几种情况:
    1)输入空的字符
    2)关掉客户端
    当重新进行连接的时候,每次的客户端端口都是不一样的随机的。这是由系统随机分配的。

    增加超时机制:clientsock.settimeout(t)

    调用socket的settimeout()函数,向其传递参数,表明超时时间设置。当访问一个socket,如果经过了参数设定的时间后,什么都没有发生,则会产生一个socket.timeout异常。

     1 import socket,traceback
     2 host=''
     3 port=12345
     4 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
     5 s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
     6 s.bind((host,port))
     7 s.listen(1)
     8 
     9 while 1:
    10     try:
    11         clientsock,clientaddr=s.accept()
    12     except KeyboardInterrupt:
    13         raise
    14     except:
    15         traceback.print_exc()
    16         continue
    17     clientsock.settimeout(5)
    18     try:
    19         print "连接来自:",clientsock.getpeername()
    20         while 1:
    21             data=clientsock.recv(4096)
    22             if not len(data):
    23                 break
    24             clientsock.sendall(data)
    25             clientsock.sendall("
    I get it!
    ")
    26 ##            t=raw_input('input the word:')
    27 ##            clientsock.sendall(t)
    28     except (KeyboardInterrupt,SystemExit):
    29         raise
    30     except socket.timeout:
    31         print '连接超时'
    32         pass
    33     except:
    34         traceback.print_exc()
    35 
    36     try:
    37         clientsock.close()
    38     except KeyboardInterrupt:
    39         raise
    40     except:
    41         traceback.print_exc()

     半开放socket:

    通过调用shutdown()使得socket半开放,它可以为了检查错误、安全防护和防止缓存溢出等原因而关闭一个方向的通信。

    附录1.

    以上步骤可以简化为:

    image

    http://www.cnblogs.com/xiaowuyi/archive/2012/08/06/2625509.html

    附录2.

    socket库中利用getservbyname()函数可以查询端口号:

    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    port=socket.getservbyname('http','tcp')
    port的返回值为80。若改为:
    port=socket.getservbyname('smtp','tcp')
    port的返回值为25。

    建立socket连接后,可以通过getsockname()获取本身的ip地址和端口号,也可以通过getpeername()显示远程机器的ip地址和端口号。

    Socket 模块的类方法
    类方法 说明
    Socket 低层网络接口(每个 BSD API)
    socket.socket(family, type) 创建并返回一个新的 socket 对象
    socket.getfqdn(name) 将使用点号分隔的 IP 地址字符串转换成一个完整的域名
    socket.gethostbyname(hostname) 将主机名解析为一个使用点号分隔的 IP 地址字符串
    socket.fromfd(fd, family, type) 从现有的文件描述符创建一个 socket 对象

    Socket 模块的实例方法
    实例方法 说明
    sock.bind( (adrs, port) ) 将 socket 绑定到一个地址和端口上
    sock.accept() 返回一个客户机 socket(带有客户机端的地址信息)
    sock.listen(backlog) 将 socket 设置成监听模式,能够监听 backlog 外来的连接请求
    sock.connect( (adrs, port) ) 将 socket 连接到定义的主机和端口上
    sock.recv( buflen[, flags] ) 从 socket 中接收数据,最多 buflen 个字符
    sock.recvfrom( buflen[, flags] ) 从 socket 中接收数据,最多 buflen 个字符,同时返回数据来源的远程主机和端口号
    sock.send( data[, flags] ) 通过 socket 发送数据
    sock.sendto( data[, flags], addr ) 通过 socket 发送数据
    sock.close() 关闭 socket
    sock.getsockopt( lvl, optname ) 获得指定 socket 选项的值
    sock.setsockopt( lvl, optname, val ) 设置指定 socket 选项的值

    http://www.cnblogs.com/xiaowuyi/archive/2012/08/06/2625509.html

  • 相关阅读:
    iframe高度自适应方法
    mysql left join对于索引不生效的问题
    禁止百度转码和百度快照缓存的META声明
    使用graphviz绘制流程图
    安装php扩展sphinx-1.2.0.tgz和libsphinxclient0.9.9
    5种主要的编程风格和它们使用的抽象
    Nodejs调用Aras Innovator服务,处理AML并返回AML
    使用Rancher管理Docker
    docker容器间通信
    使用Portainer管理Docker
  • 原文地址:https://www.cnblogs.com/menghuizuotian/p/3764492.html
Copyright © 2020-2023  润新知