# Socket是网络编程的一个抽象概念。通常我们用一个Socket表示‘打开了一个网络链接’，而打开一个Socket需要知道目标计算
# 机的IP地址和端口号，再指定协议类型即可

# 客户端
# 大多数连接都是可靠的TCP连接。创建TCP连接时，主动发起连接的叫客户端，被动响应连接的叫服务器。

# 举个例子，当我们在浏览器中访问新浪时，我们自己的计算器就是客户端，浏览器会主动向新浪发起连接。如果一切顺利，新浪
# 的服务器接受了我们的连接，一个TCP连接就建立起来的，后面的通信就是发送网页内容。

# 所有，我们要创建以恶搞基于TCP连接的Socket,可以这样做。

# 导入socket库
import socket

# 创建一个socket：
# AF_INET指定使用IPv4协议，如果要使用更先进的IPv6，就指定AF_INET6
# SOCK_STREAM指定使用面向流的TCP协议
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 建立连接
s.connect(('www.sina.com.cn', 80))
# 这样一个Socket对象就创建成功，但是还没建立连接。

# 客户端要主动发起TCP连接，必须知道服务器的IP地址和端口号。新浪网站的IP地址可以用域名www.sina.com.cn自动转换到IP
# 地址，但是怎么知道新浪服务器的端口号呢？

# 答案是作为服务器，提供什么样的服务，端口就必须固定下来。由于我们想要访问网页，因此新浪提供网页服务的服务器必须
# 把端口固定在80端口，因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号。例如SMTP服务是25端口，FTP服务
# 是21端口，等等。端口号小于1024的是Internet标准服务的端口。端口号大于1024的，可以任意使用。

# 因此，我们连接新浪服务器的代码如下：
# s.connect(('www.sina.com.cn',80))
# 注意参数是一个tuple,包含地址和端口。
# 建立TCP连接后，我们就可以向新浪服务器发送请求，要求返回首页的内容。
# 发送数据
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# TCP连接创建的是双向通道，双方都可以同时给对方发数据。但是谁先发谁后发，怎么协调，要根据具体的协议来决定。
# 例如，HTTP协议规定客户端必须先发请求给服务器。服务器收到后才发数据给客户端。

# 发送的文本格式必须符合HTTP标准，如果格式没有问题，接下来就可以接收新浪服务器返回的数据了；
# 接收数据
buffer = []
while True:
    # 每次最多接收1K字节
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
    data = b''.join(buffer)

# 接收数据时，调用recv(max)方法，一次最多接收指定的字节数，因此，在一个while循环中反复接收，直到recv()返回空数据
# 表示接收完毕，退出循环。
# 当我们接收完数据后，调用close()方法关闭Socket,这样，一次完整的网络通信就结束了；
# 关闭连接
s.close()
# 接收到的数据包括HTTP头和网页本身，我们只需要把HTTP头和网页分离一下，把HTTP头打印出来，网页内容保存到文件。

header,html = data.split(b'\r\n\r\n',1)
print(header.decode('utf-8'))
# 把接收的数据写入文件；
with open('sina.html', 'wb') as f:
    f.write(html)


