Python网络编程之基本架构与socket模块

两种架构

我们了解的涉及到两个程序之间通讯的应用大致可以分为两种:

第一种是应用类:qq、微信、网盘、优酷这一类是属于需要安装的桌面应用。

第二种是web类:比如百度、知乎、谷歌等使用浏览器访问就可以直接使用的应用。

这些应用的本质其实都是两个程序之间的通讯,而这两个分类又对应了两个软件开发的架构。

C/S架构

Client/Server,客户端和服务器结构。这种架构也是从用户层面(也可以是物理层面)来划分的。这里的客户端一般泛指客户端应用程序exe文件等,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。

B/S架构

Browser/Server,浏览器端和服务器架构。这种架构是从用户层面来划分的,Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。

socket模块

socket

中文:套接字。套接字是计算机网络数据结构,它体现了上节中所描述的“通信端点”的概念。

套接字的起源可以追溯到 20 世纪 70 年代,它是加利福尼亚大学的伯克利版本 UNIX(称 为 BSD UNIX)的一部分。因此,有时你可能会听过将套接字称为伯克利套接字或 BSD 套接 字。套接字最初是为同一主机上的应用程序所创建,使得主机上运行的一个程序(又名一个 进程)与另一个运行的程序进行通信。这就是所谓的进程间通信(Inter Process Communication, IPC)。有两种类型的套接字:基于文件的和面向网络的。

Python只支持 AF_UNIX、AF_NETLINK、AF_TIPC 和 AF_INET 家族。

AF_UNIX:基于文件 系统支持它们的底层基础结构,是套接字的第一个家族。

AF_NETLINK:支持无连接服务,允许使用标准的 BSD 套接字接口进行用户级别和内核级别代码之间的 IPC。

AF_TIPC:支持透明的进程间通信(TIPC)协议,允许计算机集群之中的机器相互通信,而无须使用基于 IP 的寻址方式。

AF_INET:基于网络,是使用得最广泛的。

套接字地址:主机  + 端口号

面向网络两种方式连接操作步骤如图:

socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。

基于TCP的socket

tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端。

server端:

import socket

HOST = '127.0.0.1'  # loop back host
PORT = 8080  # the port number must be greater than or equal to 1024
ADDRESS = (HOST, PORT)  # must be a tuple

ss = socket.socket()  # create socket
ss.bind(ADDRESS)  # bind ADDRESS
ss.listen()  # listen
con, add = ss.accept()  # accept a tuple (socket_object,address)
print(con, add)
res = con.recv(1024)
print(res)
con.send(b'Hello')  # send binary

con.close()  # close con
ss.close()  # close socket_object

client端:

import socket

HOST = '127.0.0.1'  # loop back host
PORT = 8080  # the port number must be greater than or equal to 1024
ADDRESS = (HOST, PORT)  # must be a tuple

ss = socket.socket()  # create socket
ss.connect(ADDRESS)  # connect ADDRESS
ss.send(b'This is Python')  # only send binary
res = ss.recv(1024)  # the size every time can receive
print(res)

ss.close()

执行方法:先打开服务器,然后打开客户端。

结果:

server端:

<socket.socket fd=464, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 1122)> ('127.0.0.1', 1122)
b'This is Python'

Process finished with exit code 0

client端:

b'Hello'

Process finished with exit code 0

基于UDP协议的socket

udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接。

server端:

import socket

HOST = '127.0.0.1'  # loop back host
PORT = 8080  # the port number must be greater than or equal to 1024
ADDRESS = (HOST, PORT)  # must be a tuple

ss = socket.socket(type=socket.SOCK_DGRAM)  # for UDP, type=socket.SOCK_DGRAM
ss.bind(ADDRESS)  # bind ADDRESS
msg, add = ss.recvfrom(1024)  # the size every time can receive
print(msg)

ss.sendto(b'Hello', add)  # binary parameter, ADDRESS
ss.close()

client端:

import socket

HOST = '127.0.0.1'  # loop back host
PORT = 8080  # the port number must be greater than or equal to 1024
ADDRESS = (HOST, PORT)  # must be a tuple

ss = socket.socket(type=socket.SOCK_DGRAM)  # for UDP, type=socket.SOCK_DGRAM
ss.sendto(b'This is Python', ADDRESS)  # binary parameter, ADDRESS
msg, add = ss.recvfrom(1024)  # the size every time can receive
print(msg.decode('utf-8'), add)  # must be decode
ss.close()

执行方法:先打开服务器,然后打开客户端。

结果:

server端:

b'This is Python'

Process finished with exit code 0

client端:

Hello ('127.0.0.1', 8080)

Process finished with exit code 0

简单QQ聊天程序

服务端:

import socket

HOST = '127.0.0.1'
PORT = 8080
ADDRESS = (HOST, PORT)
BUFF_SIZE = 1024

ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ss.bind(ADDRESS)
while 1:
    c_msg, add = ss.recvfrom(BUFF_SIZE)
    print("FROM[%s : %s]:\033[1;44m%s\033[0m" % (add[0], add[1], c_msg.decode('utf-8')))
    s_msg = input("REPLY:").strip()
    ss.sendto(s_msg.encode('utf-8'), add)

客户端:

import socket

HOST = '127.0.0.1'
PORT = 8080
ADDRESS = (HOST, PORT)
BUFF_SIZE = 1024

sc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
name_dict = {
    'Mike': ADDRESS,
    'Nick': ADDRESS,
    'Jane': ADDRESS,
    'Mary': ADDRESS
}

while 1:
    choose_name = input("Please input a person you want to talk:")
    if choose_name not in name_dict.keys():
        print("Please input correct name,initial capital.")
        continue
    while 1:
        c_msg = input("Please input message,press 'Enter' to send, 'Q' for exit:").strip()
        if c_msg == 'Q':
            break
        if not c_msg:
            print("Please input something:")
            continue
        sc.sendto(c_msg.encode('utf-8'), name_dict[choose_name])
        s_msg, add = sc.recvfrom(BUFF_SIZE)
        print("FROM[%s : %s]:\033[1;44m%s\033[0m" % (add[0], add[1], c_msg.encode('utf-8')))
    sc.close()

执行方法:先打开服务器,然后打开客户端。

结果:

(略)

时间服务器(为发送至服务器的内容打上时间)

服务端:

import socket
import time

HOST = '127.0.0.1'
PORT = 8080
ADDRESS = (HOST, PORT)
BUFF_SIZE = 1024

ss = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ss.bind(ADDRESS)

while 1:
    msg, add = ss.recvfrom(BUFF_SIZE)
    time_format = '%Y-%m-%d %X'
    t = time.strftime(time_format)
    back_msg = (t + '  ').encode('utf-8') + msg

    if msg:
        ss.sendto(back_msg, add)
    else:
        ss.sendto((t + "  Empty string invalid").encode('utf-8'), add)

客户端:

import socket

HOST = '127.0.0.1'
PORT = 8080
ADDRESS = (HOST, PORT)
BUFF_SIZE = 1024

sc = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while 1:
    msg = input("Please input anything you want:")
    sc.sendto(msg.encode('utf-8'), ADDRESS)
    re_msg, add = sc.recvfrom(BUFF_SIZE)
    print(re_msg.decode('utf-8'))
sc.close()

结果:

服务端:

(无结果,等待状态)

客户端:

Please input anything you want:Hello
2018-08-15 13:55:19  Hello
Please input anything you want:This is Python
2018-08-15 13:55:26  This is Python
Please input anything you want:
2018-08-15 13:55:28  Empty string invalid
Please input anything you want:

...

socket参数详解

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)

socket 参数说明
familyAF_INET(默认值) AF_INET6(IPV6使用) AF_UNIX(Unix早期文件通信)AF_CAN(原始套接字,操作底层)和AF_RDS。
typeSOCK_STREAM(默认值,TCP) SOCK_DGRAM(UDP) SOCK_RAW(IP、ICMP等)或其它。
proto协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。
fileno如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的,这有助于使用socket.close()关闭一个独立的插座。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值