博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python网络编程(1)---套接字, IPv4, 简单的Client/Server程序
阅读量:6971 次
发布时间:2019-06-27

本文共 9761 字,大约阅读时间需要 32 分钟。

  这一部分主要介绍python中socket模块的相关内容,socket即套接字。

  socket是使用TCP/IP协议的应用程序通常采用的应用编程接口,它位于运输层和应用层之间,起源于UNIX,由于遵从UNIX“一切皆文件的”思想故socket可看作一种特殊的文件,对其的操作基本可以视为读写I/O、打开、关闭。关于套接字的基本概念@吴秦的写的很详细,大家可以参考。

  在下面列出的各个部分中我将先贴出代码,然后对其进行解释。

  • 通过python3获得本机名和ip地址

  

1 >>> import socket2 >>> host_name = socket.gethostname()3 >>> print "Host name: %s" %host_name4 Host name: debian65 >>> print "IP address: %s" %socket.gethostbyname(host_name)6 IP address: 127.0.1.1

  上面代码中gethostname方法返回字符串形式的当前主机名,gethostbyname方法返回对应主机的IPv4地址,注意,这里的host可以是类似‘www.baidu.com’等因特网上主机名。

gethostname()    -> hostgethostbyname(host)    -> address

  代码很简单明了,不详细说了。

  • 获得远端计算机的Ip地址

    将前面的gethostbyname(host)方法的参数host设为'www.baidu.com'格式的远端主机名就能获得其IP地址。

1 import socket2    def get_remote_machine_info():3        remote_host = 'www.baidu.com'4        try:5            print("IP address: %s" % socket.gethostbyname(remote_host))6        except socket.error as err_msg:7            print("%s: %s" %(remote_host, err_msg))8    if __name__ == '__main__':9        get_remote_machine_info()
  • 转换IPv4地址的格式

    处理到底层网络时,通常用到的是32位二进制形式的而不是字符串形式的IP地址,socket库提供了相互转换的方法:inet_aton()和inet_ntoa()

1 socket.inet_aton(string) #将字符串形式的IP地址转换为用于底层网络的32位形式地址 2 socket.inet_ntoa(packed_ip) -> ip_address_string #将32位形式ip地址转化为字符串形式

>>> socket.inet_aton('127.0.0.1')

b'\x7f\x00\x00\x01'

>>> socket.inet_ntoa(b'\x7f\x00\x00\x01')

'127.0.0.1'

 

  • 通过端口和协议获得服务名称
getservbyport(port[, protocolname]) -> string

    getservbyport方法可以方便的通过查看指定端口对应的服务名称,其中port为端口号,可选参数protocolname为使用协议,以下例子获取80 25 53端口对应的服务:

import socketdef find_service_name():    protocolname = 'tcp'    for port in [80, 25]:        print("Port: %s => service name: %s" % (port, socket.getservbyport(port, protocolname)))    print("Port: %s => service name: %s" % (53, socket.getservbyport(53,'udp')))if __name__ == '__main__':    find_service_name()

[output]

Port: 80 => service name: http

Port: 25 => service name: smtp

Port: 53 => service name: domain

  • 转换整数的本地和网络字节顺序

    

def convert_integer():    """将数据字节在本地顺序和网络顺序间转换"""    data = 1234    #将data转换为32位长格式    print("Original: %s => Long host byte order: %s, Network byte order: %s" % (data, socket.ntohl(data),socket.htonl(data)))    #将data转换为16位短格式    print("Original: %s => Short host byte order: %s, Network byte order: %s" % (data, socket.ntohs(data), socket.htons(data)))

Original: 1234 => Long host byte order: 3523477504, Network byte order: 3523477504

Original: 1234 => Short host byte order: 53764, Network byte order: 53764

 

  • 获得和设置socket的超时时间
def test_socket_timeout():    """通过gettimeout和settimeout方法获取和设置timeout时间."""    # first args of s is socket family, second args is socket type.    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    print("Default socket timeout: %s" % s.gettimeout())    s.settimeout(100)    print("Current socket timeout: %s" % s.gettimeout())

Default socket timeout: None

Current socket timeout: 100.0

  • 修改socket的发送和接受缓存大小
getsockopt(level, option[, buffersize]) -> valuesetsockopt(level, option, value)

    socket.socket.getsocket方法可以获得当前缓存大小信息,socket.socket.setsocket方法可以重新设置缓存大小:

import socketSEND_BUF_SIZE = 4096RECV_BUF_SIZE = 4096def modify_buff_size():    """修改发送和接收缓存的大小"""    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    # 获得发送缓存大小    bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)    print("Buffer size [Before]:%d" % bufsize)    sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)    sock.setsockopt(            socket.SOL_SOCKET,            socket.SO_SNDBUF,            SEND_BUF_SIZE)    sock.setsockopt(            socket.SOL_SOCKET,            socket.SO_RCVBUF,            RECV_BUF_SIZE)    bufsize = sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)    print("Buffer size [After]:%d" % bufsize)if __name__ == '__main__':    modify_buff_size() [output]

Buffer size [Before]:131072

Buffer size [After]:4096

  • 改变socket为阻塞或非阻塞模式

    socket默认为阻塞模式,即调用connect等API时程序会被阻塞直到操作完成,然而很多时候这不是我们所希望的,幸运的是可以通过socket.socket.setblocking来设置:

setblocking(flag)    Set the socket to blocking (flag is true) or non-blocking (false).    setblocking(True) is equivalent to settimeout(None);    setblocking(False) is equivalent to settimeout(0.0).

    下面的例子将socket设为了非阻塞模式:

import socketdef test_socket_modes():    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    s.setblocking(0)    s.settimeout(0.5)    s.bind(("127.0.0.1", 0))    socket_address = s.getsockname()    print("Trivial Server launched on socket: %s" % str(socket_address))    while True:        s.listen(1)if __name__ == '__main__':    test_socket_modes()
  • 重用socket地址和端口

  当我们关闭某个特定端口上的python服务端后如果试图重新在这个端口上打开它系统将会提示“Address already in use”错误,然而很多情况下客户端都需要通过某个特定的端口连接服务程序,这可以通过开启socket地址和端口重用实现。

import socketdef reuse_socket_addr():    """ 使端口在关闭或者发生异常而退出时能重新使用"""    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #获得SO_REUSEADDR状态    old_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)    print("Old sock state: %s" % old_state)    #设置端口能够被重用    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)    new_state = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)    print(" New sock state: %s" % new_state)    local_port = 8282    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)    srv.bind(('', local_port))    srv.listen(1)    print(" Listening on port: %s" % local_port)    while True:        try:            connection, addr = srv.accept()            print("Connected by %s: %s" % (addr[0], addr[1]))        except KeyboardInterrupt:            break        except socket.error as e:            print('%s' % (e,))if __name__ == '__main__':    reuse_socket_addr()
  • 从因特网获得当前时间(ntp)

  很多程序的正常运行依赖于精确的时间,这就需要使本地时间与网络时间进行同步以保持其精确性。NTP(Network Time Protocol, 网络时间协议)就是用来使网络中各个计算机时间同步的一种协议,下面就是一个简单的同步时间程序,在运行前请先安装ntplib库:

pip3 install ntplib
import ntplibfrom time import ctimedef print_time():    """First: pip3 install ntplib, NTP(Network Time Protocol)"""    ntp_client = ntplib.NTPClient()    response = ntp_client.request('pool.ntp.org')    print(ctime(response.tx_time))if __name__ == '__main__':    print_time()
  • 练习:编写一个简单的C/S架构的“回声”应用

  在介绍完前面的一些基本的socket API后,我们来动手写一个简单的“回声”程序来进行巩固、复习。

  在这个应用中,服务端和客户端都通过argparse模块得到port参数,在服务端,首先建立一个基于TCP的套接字,并设置其可以被重用使服务端程序能打开任意次,然后将它绑定在本机上命令行指定的端口上;接着,在监听阶段,我们设置backlog参数使得服务端能同时监听多个客户端连接,最后等待客户端连接进来并发送一些数据,在接收到这些数据之后,再将其原封不动地返回回去。

注意: 由于python 2.x和python3.x编码类型地不同,python2.x中直接s.send(data)就行,然而,socket中还不支持python3的编码,因此,python3中需要先对str型的data进行encode, 在从socket接受下来的数据打印前也需要先decode处理!否则会报错。

[服务端程序]

import socketimport argparseHOST = 'localhost'DATA_PAYLOAD = 2048#The max number of clients.BACKLOG = 5def echo_server(port):    """A simple echo server"""    #Create a TCP socket    serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #Enable reuse address/port    serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)    #Bind the socket to the port    server_address = (HOST, port)    print("Starting up echo server on %s port %s" % server_address)    serv.bind(server_address)    #Listen to clients, backlog argumen specifies the max no. of queued connections    serv.listen(BACKLOG)    while True:        print("waiting to receive message from client")        client, address = serv.accept()        data = client.recv(DATA_PAYLOAD)        if data:            print("Data: %s" % data.decode())            client.send(data)            print("sent %s bytes back to %s" % (data.decode(), address))        #end connection        client.close()if __name__ == '__main__':    parser = argparse.ArgumentParser(description='Socket Server Example')    parser.add_argument("--port", action = 'store', dest='port', type = int, required=True)    given_args = parser.parse_args()    port = given_args.port    echo_server(port)

  在client端,我们使用命令行参数获得的server端口号创建socket并且与server连接,成功后向服务器发送信息,之后马上按一次16字节接收server回传的信息。

import socketimport argparseHOST = 'localhost'def echo_client(port):    """A simple echo client"""    # Create a TCP/IP socket    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    # Connect the socket to the server    server_address = (HOST, port)    print("Connecting to %s port %s" % server_address)    client.connect(server_address)    try:        #Send data        message = " Test message. This will be echoed"        print("Sending %s" % message)        client.sendall(message.encode())        # Receive from server.        amount_received = 0        amount_excepted = len(message)        while amount_received < amount_excepted:            data = client.recv(16)            amount_received += len(data)            print("Received: %s" % data.decode())    except socket.error as e:        print("Socket error %s" % str(e))    except Exception as e:        print("Other exception: %s" % str(e))    finally:        client.close()if __name__ == '__main__':    parser = argparse.ArgumentParser(description='Socket Client Example')    parser.add_argument('--port',action='store', dest='port', type=int,required=True)    given_args = parser.parse_args()    port = given_args.port    echo_client(port)

[output]

$ python3 1_13a_echo_server.py --port=9900Starting up echo server  on localhost port 9900Waiting to receive message from clientNow, run the client from another terminal as follows:$ python3 1_13b_echo_client.py --port=9900Connecting to localhost port 9900Sending Test message. This will be echoedReceived: Test message. ThReceived: is will be echoeReceived: dClosing connection to the serverUpon connecting to the localhost, the client server will also print the following message:Data: Test message. This will be echoedsent Test message. This will be echoed bytes back to ('127.0.0.1', 42961)Waiting to receive message from client

 

转载于:https://www.cnblogs.com/weixinbupt/p/4666063.html

你可能感兴趣的文章
java版b2b2c社交电商spring cloud分布式微服务(七)springboot开启声明式事务
查看>>
品质生活正式开启,欧普照明&华为智选再创智能家居里程碑式突破
查看>>
【本人秃顶程序员】分库分表怎么才能无限扩容,看这篇文章就对了
查看>>
迁移潮来袭!数十个项目宣布即将停止支持 Python 2
查看>>
Javascrip—装饰器(7)
查看>>
Java 11 已发布,String 还能这样玩!
查看>>
(一)java版电子商务spring cloud分布式微服务b2b2c社交电商-服务的注册与发现(Eureka)...
查看>>
使用DataV制作实时销售数据可视化大屏
查看>>
Hello Juejin
查看>>
AndroidStudio导入或者新建项目一直build
查看>>
laravel项目
查看>>
Azure 文档 (SQL 数据仓库, Azure SQL 数据库文档)
查看>>
基于arm的多路温度采集控制系统(4)菜单界面
查看>>
大数据存储管理大趋势
查看>>
我的友情链接
查看>>
R478规划及实施—理想丰满、现实骨感
查看>>
FreeBSD scp xftp 无法使用时,考虑sftp。
查看>>
使用计划任务定时重启Server
查看>>
RedisCluster工具类
查看>>
我的友情链接
查看>>