TCP 编程

TCP 编程

个人计算机或者服务器上通常会运行多个应用程序, 但是我们只需要一条网线就能连接互联网, 去访问互联网的资源。如果有一个公网的 IP 可以被其他用户访问到, 这是由于所有的程序都共享了这一个线路, 为了实现共享, TCP 通过把要发送的数据流分解成小的信息包在网络上传输, 这些信息包到了接收者那里会被重组。

IP 地址

TCP 协议要把信息包投递给正确的接收者, 需要识别对方的服务器, 并且知道和这个服务器上的哪个进程通信, 为了实现这个目的, 需要使用 IP 和端口。

IP 地址有 4 组数字, 每组数字最多 3 位, 中间用点隔开, 每组数字的范围是 0-255

下面是几个 IP 地址的例子:

192.168.0.1 (私有 IP)

172.16.22.19 (私有 IP)

8.8.8.8

123.22.122.1

私有 IP 范围

下面三种范围内的 IP 地址都是私有的, 是在局域网里面可以随意使用的。而公网 IP 需要到电信运营商申请才可以使用。

10.0.0.0 - 10.255.255.255

172.16.0.0 - 172.31.255.255

192.168.0.0 - 192.168.255.255

端口号

每个程序都用到唯一的端口, 端口是一个数字, 从 1 - 65535。

1024 以下的端口号也叫做知名端口号, 是那些由互联网名称与数字地址分配机构(ICANN)预留给传输控制协议(TCP)的, 这些端口都代表了具体的约定的用法, 不应该滥用。剩下的端口号叫动态端口号或私有端口号, 比较自由, 可以按自己的业务需要约定使用。

IPv6

Ipv6 是网际协议(IP)的最新版本, 用作互联网的网络层协议, 用它来取代 IPv4 主要是为了解决 IPv4 地址枯竭等问题。

IPv6 二进制位下为 128 位长度, 以 16 位为一组, 每组以冒号(:)隔开, 可以分为 8 组, 每组以 4 位十六进制方式表示, 如下:

2001:0db8:85a3:08d3:1319:8a2e:0370:7344

DNS

由于 IP 地址是一堆数字, 不方便记忆, 所以通常使用域名来代替 IP 地址, 用户只需要输入域名就能访问对应的 IP 地址。保存域名和 IP 地址对应关系的网络服务就是 DNS。

路由

一旦应用程序请求操作系统向某一特定 IP 地址发送数据, 操作系统就需要决定如何使用该机器连接的某一个物理网络来传输数据。这个决定, 也就是根据目的 IP 地址选择将 IP 数据包发往何处, 即路由。

套接字(Socket)

socket 是一种操作系统提供的进程间通信机制。

它是网络通信过程中端点的抽象表示, 包含进行网络通信必须的 5 种信息:

  • 连接使用的协议

  • 本地主机的 IP 地址

  • 本地进程的协议端口

  • 对方主机的 IP 地址

  • 对方进程的协议端口

简单的说, socket 就是通信双方的一种约定, 用 socket 相关的函数去完成通信过程。

在同一台计算机上, TCP 和 UDP 协议可以同时使用相同的端口而互相不会干扰(比如 DNS 就用到了 TCP 和 UDP, 端口都是 53), 操作系统根据 socket 的地址可以决定将数据送达特定的进程或线程。

套接字类型

可以把套接字理解为程序在本地或者跨互联网来回传递数据的通信通道的一个端点, 套接字有两个控制发送数据的基本属性:

  • 地址家族: 控制 OSI 网络层的协议。

  • 套接字(Socket) 类型: 控制传输层的协议。

下面是套接字(Socket)类型:

  1. 流式套接字(SOCK_STREAM)。用于通过面向连接可靠的数据传输服务, 这个服务可以保证数据能够实现无差错无重复的发送, 并且按顺序地接收。流式套接字(SOCK_STREAM)之所以能够提供这种可靠的数据服务, 原因在于它使用了 TCP 协议。

  2. 数据报套接字(SOCK_DGRAM)。提供了一种无连接的服务, 这个服务并不能保证数据传输的可靠性, 数据有可能在传输过程中丢失或者出现数据重复, 而且也无法保证顺序地接收数据。数据报套接字(SOCK_DGRAM)使用的是 UDP 协议, 对于有可能出现的数据丢失的问题, 需要在程序里面做相关的处理。

  3. 原始套接字(SOCK_RAW)。允许对较底层的协议直接访问(比如 IP、ICMP 协议)。

套接字地址家族(Address Family)

Python 支持非常多的地址家族, 最常见的 3 种如下:

  1. AF_INET。表示 IPv4 的互联网寻址。

  2. AF_INET6。表示 IPv6 的互联网寻址。

  3. AF_UNIX。用于在同一个服务器上的进程间的通信。

使用 socket 模块

Python 中网络编程主要使用 socket 模块, 要创建一个套接字, 需要使用 socket.socket() 函数:

# 一般语法
socket(socket_family, socket_type, protocol=0)

# 其中, socket_family 是套接字地址家族: AF_UNIX、AF_INET、AF_INET6
# socket_type 是套接字类型: SOCK_STREAM 或 SOCK_DGRAM
# protocol(协议) 通常省略, 默认为 0

# 创建 TCP 套接字:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Socket 的方法

服务端使用方法

s.bind(address): 将套接字绑定到地址, 在 AF_INET 下, 以 tuple(host, port) 的方式传入, 如s.bind((host, port))。

s.listen(backlog): 开始监听 TCP 传入连接, backlog 指定在拒绝链接前, 操作系统可以挂起的最大连接数, 该值最少为 1, 大部分应用程序设为 5 就够用了。

s.accept(): 接受 TCP 链接并返回(conn, address), 其中 conn 是新的套接字对象, 可以用来接收和发送数据, address 是链接客户端的地址。

客户端使用方法

s.connect(address): 链接到 address 处的套接字, 一般 address 的格式为 tuple(host, port), 如果链接出错, 则返回 socket.error 错误。

s.connect_ex(address): 功能与 s.connect(address) 相同, 但成功返回 0, 失败返回 errno 的值。

通用的方法

s.recv(bufsize[, flag]): 接受 TCP 套接字的数据, 数据以字符串形式返回, buffsize 指定要接受的最大数据量, flag 提供有关消息的其他信息, 通常可以忽略。

s.send(string[, flag]): 发送 TCP 数据, 将字符串中的数据发送到链接的套接字, 返回值是要发送的字节数量, 该数量可能小于 string 的字节大小。

s.sendall(string[, flag]): 完整发送 TCP 数据, 将字符串中的数据发送到链接的套接字, 但在返回之前尝试发送所有数据。成功返回 None, 失败则抛出异常。

s.recvfrom(bufsize[, flag]): 接受 UDP 套接字的数据, 与 recv() 类似, 但返回值是 tuple(data, address)。其中 data 是包含接受数据的字符串, address 是发送数据的套接字地址。

s.sendto(string[, flag], address): 发送 UDP 数据, 将数据发送到套接字, address 形式为 tuple(ipaddr, port), 指定远程地址发送, 返回值是发送的字节数。

s.close(): 关闭套接字。

s.getpeername(): 返回套接字的远程地址, 返回值通常是一个包含 ipaddr, port 的元组。

s.getsockname(): 返回套接字自己的地址, 返回值通常是一个包含 ipaddr, port 的元组。

s.setsockopt(level, optname, value): 设置给定套接字选项的值。

s.getsockopt(level, optname[, buflen]): 返回套接字选项的值。

s.settimeout(timeout): 设置套接字操作的超时时间, timeout 是一个浮点数, 单位是秒, 值为 None 则表示永远不会超时。一般超时期应在刚创建套接字时设置, 因为他们可能用于连接的操作, 如s.connect()。

s.gettimeout(): 返回当前超时值, 单位是秒, 如果没有设置超时则返回 None。

s.fileno(): 返回套接字的文件描述。

s.setblocking(flag): 如果 flag 为 0, 则将套接字设置为非阻塞模式, 否则将套接字设置为阻塞模式(默认值)。非阻塞模式下, 如果调用 recv() 没有发现任何数据, 或 send() 调用无法立即发送数据, 那么将引起 socket.error 异常。

s.makefile(): 创建一个与该套接字相关的文件。

服务端例子

网络编程包含两个部分: 服务器端和客户端。

服务端例子:

# tcp_server.py

import socket

HOST = '127.0.0.1'
PORT = 5000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建套接字
s.bind((HOST, PORT)) # 绑定套接字到本地 IP 与端口
s.listen(5) # 监听连接


print(f'Server start at: {HOST}:{PORT}')

while True: # 服务器可以无限循环地接收内容
    conn, addr = s.accept() # 接受客户端连接
    print(f'Connectd by {addr}')

    while True: # 通信循环, 因为一次发送的内容可能很大
        data = conn.recv(1024).decode('utf-8') # 接收 1024 字节的内容
        print(data)

        # 给客户端发送数据, send() 方法接收 byte 类型的数据
        conn.send(bytes(f'Server received {data}', encoding='utf-8'))
    conn.close() # 关闭客户端套接字

s.close() # 关闭服务器套接字

客户端例子

# tcp_client.py

import socket


HOST = '127.0.0.1'
PORT = 5000


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT)) # 尝试连接到服务器


while True:
    cmd = input('Input your msg:')
    s.send(bytes(cmd, encoding='utf-8'))
    data = s.recv(1024).decode('utf-8')
    print(data)

启动服务端和客户端

启动服务端:

> python tcp_server.py
Server start at: 127.0.0.1:5000
Connectd by ('127.0.0.1', 9474) # 客户端启动后请求的端口是 9474
你好 # 客户端发来的内容
哈哈哈 # 客户端发来的内容

启动客户端:

> python tcp_client.py
Input your msg:你好 # 输出要发送的内容
Server received 你好 # 服务端返回的内容
Input your msg:哈哈哈
Server received 哈哈哈
Input your msg:
# 断开连接按 ctrl+c
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容