python网络编程基础1:网络客户端

一,理解socket

socket是操作系统中I/O系统的延伸部分, 它可以使进程和机器之间的通信成为可能。

当前经常使用的socket最早起源于BSD UNIX类的操作系统。 在UNIX系统上, 比如BSD,有一些现有的、 和文件描述符一起工作的系统调用, 其中包括 open()read()write()close()。文件描述符一般是指一个文件或某个类似文件的实体。

把对网络的支持加入操作系统, 是以一种扩展现有文件描述符结构的方法来实现的。新的系统调用被加入并和socket 一起工作, 而很多现有的系统调用同样能和socket 一起工作。因此,一个socket允许使用标准的操作系统和其他的计算机, 以及自己机器上的不同进程来通信。

socket可以被看成一个标准的文件描述符。在类UNIX的平台上,read()、write()、dup()、dup2()和close()这样的系统调用会像为标准文件描述符那样为socket工作。

很多文件是通过调用open()函数来打开的,但socket是通过调用socket()函数来建立的, 并且还需要另外的调用来连接激活它们。

Python通过socket模块提供访问操作系统socket库的接口。

二,建立socket

对于一个客户端程序来说, 建立一个socket需要两个步骤:

  1. 建立一个实际的socket对象
  2. 把它连接到远程服务器上

建立socket对象,调用socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)方法。

  1. family地址簇:指明用什么地址簇来传输数据。地址簇的选项包括IPv4 对应的AF_INTE、IPv6 对应的 AF_INET6等选项。
  2. type套接字类型:指明用什么协议来传输数据。协议的选项包括TCP 协议对应的 SOCK_STREAM、UDP 协议对应的 SOCK_DGRAM等选项。

连接socket,调用socket.connect(address)方法连接到 address 处的远程套接字。

  • 需要提供一个包含远程主机名或IP地址和远程端口的元组: s.connect(("www.baidu.com", 80))

完整的例子:

import socket

print('Creating socket...')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('done!')

print('Connecting to remote host...')
s.connect(("www.baidu.com", 80))
print('done!')

1,寻找端口号

Python的socket库包含一个getservbyname()的函数, 它可以自动地查询已知服务器端口号的列表。
为了查询这个列表, 需要两个参数: 协议名端口名

import socket

print('Creating socket...')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('done!')

print('Looking up port number ...')
port = socket.getservbyname('http', 'tcp')
print('done!')

print('Connecting to remote host on port %d' % port)
s.connect(("www.baidu.com", port))
print('done!')
  • 不需要事先知道HTTP使用80端口, 尽管可以查找并直接把端口号写在程序中,但是对于交互式程序,让用户能看到文字形式的端口描述将是非常好的。

2,从socekt获取信息

一旦建立了一个socket连接。就可以从它那里得到一些有用的信息:

import socket

print('Creating socket...')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('done!')

print('Looking up port number ...')
port = socket.getservbyname('http', 'tcp')
print('done!')

print('Connecting to remote host on port %d' % port)
s.connect(("www.baidu.com", port))
print('done!')

print('Connected from ' + str(s.getsockname()))
print('Connected to ' + str(s.getpeername()))

更多socket对象方法:socket-objects

三,利用socket通信

Python提供了两种方法来利用socket发送和接收数据:

  1. socket对象:提供了操作系统的 send()、 sendto()、 recv() 和 recvfrom() 调用的接口。
  2. 文件类对象:提供了 read()、 write() 和 readline() 这些更典型的Python接口。

两种方法的选择:

  1. 读写数据时、 需要协议可以详细地控制时、 使用二进制协议传送固定大小数据时、 数据超时需要特殊处理时、再或者是任何不止需要简单读写时。 当您编写UDP程序的时候, socket对象同样是很好的选择。
  2. 文件类对象一般用于面向线性的协议, 因为它能通过提供的 readline() 函数自动地为您处理大多数的解析。 然而,文件类对象一般只对TCP连接工作得很好, 对UDP连接反而不是很好。这是因为TCP连接的行为更像是标准的文件, 它们保证数据接收的精确性, 并且和文件一样是以字节流形式运转的。 而UDP并不像文件那样以字节流形式运转。 相反, 它是一种基于信息包的通信。 文件类对象没有办法操作每个基本的信息包。

四,处理错误

1,socket异常

不同的网络调用会产生不同的异常。

#!/usr/bin/python3.6

import socket
import sys

host = sys.argv[1]
textport = sys.argv[2]
filename = sys.argv[3]

try:
    print('Creating socket...')
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print('done!')
except socket.error as e:
    print('Strange error creating socket: %s' % e)
    sys.exit(1)

# Try parsing it as a numeric port number,
try:
    port = int(textport)
except ValueError:
    # That didn't work, so it's probably a protocol name.
    # Look it up instead.
    try:
        print('Looking up port number ...')
        port = socket.getservbyname(textport, 'tcp')
        print('done!')
    except socket.error as e:
        print("Couldn't find your port: %s" % e)
        sys.exit(1)

try:
    print('Connecting to remote host %s on port %d' % (host, port))
    s.connect((host, port))
    print('done!')
except socket.gaierror as e:
    print("Address-related error connecting to server: %s" % e)
    sys.exit(1)
except socket.error as e:
    print("Connection error: %s" % e)
    sys.exit(1)

try:
    http_content = ("GET %s HTTP/1.1\r\n\r\n" % filename).encode('utf-8')
    print('Sending HTTP request %s to remote host on port %d' % (http_content, port))
    s.sendall(bytes(http_content))
    print('done!')
except socket.error as e:
    print("Error sending data: %s", e)
    sys.exit(1)

while 1:
    try:
        buf = s.recv(2048)
    except socket.error as e:
        print("Error receiving data: %s", e)
        sys.exit(1)
    if not len(buf):
        break
    sys.stdout.write(buf.decode('utf-8'))
  • Ubuntu查看python路径:whereis python
  • 注意编码问题:TypeError: string argument without an encoding
  • 确定HTTP请求的正确格式。
  • 可先使用curl进行测试:C:\Users\PC>curl --verbose http://book.douban.com/subject/34662170/ curl doc.
    Linux环境下连接任意一个本地服务器,运行:
./errors.py localhost 8765 /test-ignore.html

2,一个遗漏的错误

在客户端连接与服务器写客户端请求的这段时间里, 如果远程服务器断开连接, 就会出现通信出了问题, 但是却没有产生异常, 这是因为没有从操作系统传回错误。这里后面对recv()的调用就接收不到数据(因为服务器已经关闭了连接),程序会成功终止。 这是误解最多的地方。
为了解决这个问题, 一旦结束写操作, 应该立刻调用shutdowrx()函数。 这样就会强制清除缓存里面的内容。

try:
    s.shutdown(1)
except socket.error as e:
    print('Error sending data (detected by shutdown):%s'% e)
    sys.exit(1)

五,使用UDP

UDP通信几乎不使用文件类对象, 因为它们往往不能为数据如何发送和接收提供足够的控制。

import socket, sys

host = sys.argv[1]
textport = sys.argv[2]
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
    port = int(textport)
except ValueError:
    # That didn1t work. Look it up instead.
    port = socket.getservbyname(textport, 'udp')

s.connect((host, port))
print('Enter data to transmit:')
data = sys.stdin.readline().strip()
s.sendall(bytes(data))
print('Looking for replies; press Ctrl-C or Ctrl-Break to stop.')

while 1:
    buf = s.recv(2048)
    if not len(buf):
        break
    sys.stdout.write(buf)

UDP与TCP在使用上的区别:

  1. 套接字类型不同。
  2. socket.getservbyname()寻找的端口号不同,当然也可以使用同一端口号。
  3. UDP的不确定性与TCP的可靠性在应对传输错误中的不同表现。

六,总结

网络通信的基本接口是socket,它扩展了操作系统的基本I/O到网络通信。
socket可以通过socket() 函数来建立, 通过connect()函数来连接。得到 socket对象后,就可以确定本地和远程端点的IP地址端口号,完成通信。
错误检查是很重要的。绝大多数与网络相关的调用都会产生异常, 虽然有时候这些异常不是马上出现。 使用shutdown()可以确保当有写错误发生时, 就能获得提醒。
Python提供了两种和socket工作的接口:用于UDP和高级TCP目的的标准socket接口, 以及用于简单TCP通信的文件类接口

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值