美文网首页
(1-2)socket -- client

(1-2)socket -- client

作者: 马梦里 | 来源:发表于2017-12-04 17:14 被阅读0次

socket简述

    socket英文是“孔”或“插座”的意思,也称为套接字,用于描述 IP 地址端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在 internet 上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个socket,并绑到一个端口上,不同的端口对应不同的服务。所以正如英文愿意那样,像一个多孔插座一个主机犹如布满插座的房间,每个插座都有一个编号(端口号),有的插座提供220v交流电,有的提供110v电流,有的则提供有线电视节目。客户端将插头插到不同编号的插座,就可以得到不同的服务了。

图片.png

连接原理:

套接字之间的连接可以分为三个步骤:服务器监听、客户端请求、连接确认

  1. 服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
  2. 客户端请求:客户端的套接字提出连接目标服务器端的套接字请求,为此,客户端必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后向服务器端套接字提出连接请求。
  3. 连接确认:当服务器端的套接字监听或者说接收客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的进程,把服务器端套接字的描述发送给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

一、初步

    在客户端,实现socket连接代码如下:

import socket
import ssl
from utils import log

s = socket.socket()

host = 'www.zhihu.com'
port = 80

# 参数为元祖
s.connect((host, port))

(ip, port) = s.getsockname()
log(ip, port)

http_request = 'GET / HTTP/1.1\r\nhost:{}\r\n\r\n'.format(host)

rqu = http_request.encode()

s.send(rqu)

res = s.recv(1024)
  1. 实例化socket
  2. 确定连接的服务器和端口,利用connect()函数进行连接,
  3. 发送请求:send()函数;
  4. 接收响应:recv()函数,参数为接收的字节数。(这里应该建立一个循环)

注意:

  • connect()的参数为元祖;
  • 发送请求前,字符串需要经过 encode() 方法编码,接收响应后再通过decode()方法解码,因为电脑是通过二进制传输的;
  • https的话,包裹一层ssls = ssl.wrap_socket(socket.socket())

二、升级

import socket
import ssl

def parsed_url(url):
    if 'http://' in url:
        protocol = 'http'
        u = url[7:]
    else:
        protocol = 'https'
        u = url[8:]

    if '/' in u:
        host_port, path = u.split('/', 1)
        path = '/' + path
    else:
        path = '/'
        host_port = u

    if ':' in host_port:
        host, port = host_port.split(':', 1)
    else:
# 这里端口的获取根据前面的protocol来
        p = {
        'http': 80,
        'https': 443
        }
        host = host_port
        port = p[protocol]

    return protocol, host, port, path

def response_by_socket(s):
    response = b''
    buffer_size = 1024
    while True:
        r = s.recv(buffer_size)
        response += r
        if len(r) < buffer_size:
            return response

def parsed_response(r):
    header, body = r.split('\r\n\r\n', 1)
    # h是一个列表
    h = header.split('\r\n')
    status_code = h[0].split()[1]
    status_code = int(status_code)
    headers = {}
    for line in h[1:]:
        k, v = line.split(':', 1)
        headers[k] = v
    return status_code, headers, body

// 包装起来
def socket_by_protocol(protocol):
    s = socket.socket()
    if protocol == 'https':
        s = ssl.wrap_socket(s)
    else:
        s = socket.socket()
    return s

def get(url):
    protocol, host, port, path = parsed_url(url)
    s = socket_by_protocol(protocol)
    s.connect((host, port))
    
    request = "GET {} HTTP/1.1\r\nhost:{}\r\n\r\n".format(path, host)
    http_req = request.encode()
    s.send(http_req)

    response = response_by_socket(s)
    r = response.decode()
    status_code, headers, body = parsed_response(r)

    if status_code == 301:
        url = headers['Location']
        return get(url)
    else:
        return response, status_code

if __name__ == '__main__':
    url = 'https://movie.douban.com/top250'
    get(url)

逻辑:

  • 定义get()函数,发送请求,接收响应
  • 发送请求前,根据parsed_url()函数,获得protocol、host、port、path,生成请求头
  • 根据response_by_socket()函数,接收响应
  • 根据parsed_response()函数判断是否有重定向
    小知识点:
  1. split()函数可以实现字符串的切割,变量要么是一个,那么生成的就是一个列表,要么与切割开的字符串一样多。
  2. path最前面必须带:“/”,就是这地方出错了;
  3. __name__,在当前文件运行时,name = main,被其他脚本调用时,neme为其他脚本的文件名。

测试函数

def test_parsed_url():
    http = 'http'
    https = 'https'
    port = 80
    host = 'g.cn'

    examples = [
        ('https://g.cn', (https, host, 443, '/')),
        ('http://g.cn', (http, host, port, '/')),
        ('http://g.cn/majun', (http, host, port, '/majun')),
        ('https://g.cn:8000/majun', (https, host, '8000', '/majun')),
    ]
    for line in examples:
        url, ans = line
        u = parsed_url(url)
        e = 'Pased Error: ({}), ({}), ({})'.format(url, u, ans)
        assert u == ans, e

就是拿解析的结果和正确答案进行比较,这里用到了assert,如果不合适就打印出错误的地方。
注意:解析出来的端口是个字符串


相关文章

网友评论

      本文标题:(1-2)socket -- client

      本文链接:https://www.haomeiwen.com/subject/vkexixtx.html