1 引言

本篇主要对Python下网络编程中用到的socket模块进行初步总结。首先从网络基础理论出发,介绍了TCP协议和UDP协议;然后总结了socket中的常用函数;最后通过实际代码展示基本函数的应用。

2 网络基础

  要想理解socket,首先得熟悉一下TCP/IP协议族。TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何在它们之间传输的标准,从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。下面对TCP/IP协议族中与socket紧密相关的的TCP协议和UDP协议进行介绍。

  TCP是流协议,而UDP是数据报协议。换句话说,TCP在客户机和服务器之间建立持续的开放连接,在该连接的生命期内,字节可以通过该连接写出(并且保证顺序正确)。然而,通过 TCP 写出的字节没有内置的结构,所以需要高层协议在被传输的字节流内部分隔数据记录和字段。

  UDP是数据报协议,不需要在客户机和服务器之间建立连接,它只是在地址之间传输报文。UDP的一个很好特性在于它的包是自分隔的(self-delimiting),也就是一个数据报都准确地指出它的开始和结束位置。然而,UDP的一个可能的缺点在于,它不保证包将会按顺序到达,甚至根本就不保证。不过,UDP有一个很大的有点就是效率高。

  TCP与UDP直接的对比就好似手机通信和邮寄信件通信。TCP犹如手机通信机制,当呼叫者通过手机拨打接受者手机,只有接受者按下接听键,两方才算建立起了连接,且只要没人挂断,连接就一直是存活的,也只有在连接存活情况下两者才能通话。UDP就如有邮局系统,只有有人来寄信,邮局就回帮他寄,但是不会去管收件人是否存在、也不管收件人什么时候能收到信,如果寄件人陆续寄出多封信,收件人收的的信先后顺序是混乱的,如果有的信没有送达,那这封信就此消失在历史的尘埃中。

  通常,人们用socket来建立计算机网络中多个主机(或进程)TCP/IP间的连接。

3 socket介绍

Socket(中文译为套接字)是操作系统内核中的一个数据结构,它几乎是所有网络通信的基础。网络通信,归根到底还是进程间的通信(不同计算机上的进程间通信, 又称为网络通信, IP协议进行的主要是端到端通信)。在网络中,每一个节点(计算机或路由)都有一个网络地址,也就是IP地址。两个进程通信时,首先要确定各自所在的网络节点的网络地址。但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接字中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一 一对应的关系。

socket使用(IP地址,协议,端口号)来标识一个进程。所以,使用端口号和网络地址的组合可以唯一的确定整个网络中的一个网络进程。端口号的范围从0~65535,一类是由互联网指派名字和号码公司ICANN负责分配给一些常用的应用程序固定使用的“周知的端口”,其值一般为0~1023, 用户自定义端口号一般大于等于1024。

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。每一个socket都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过socket描述符来实现的。以TCP协议中socket建立连接的“三次握手”为例,其过程如下:

  如图所示,当客户端通过socket调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用socket的accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

两个socket通过“网络”交互数据进行数据交互时只负责两件事:建立连接,传递数据;同时socket在收发数据时遵循的原则:有发就有收,收发必相等!

4 socket基本使用

1)socket函数

  功能:使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。

  格式:socket.socket([family[, type[, proto]]])

  参数:

    family : AF_INET (默认ipv4),AF_INET6(ipv6) , AF_UNIX(Unix系统进程间通信).

    type : SOCK_STREAM (TCP), SOCK_DGRAM(UDP) .

    protocol : 一般为0或者默认

  备注:如果socket创建失败会抛出一个socket.error异常

2)服务器端函数

 a)bind函数

  格式:s.bind(address)

  功能:将地址address绑定到套接字, 地址以元组(host,port)的形式表示。

  参数:

    address为元组(host,port)

    host: ip地址, 为一个字符串

    post: 自定义主机号, 为整型

b)listen函数

  格式:s.listen(backlog)

  功能:使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接。

  参数:backlog : 操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了

c)accept函数

  格式:s.accept()

  功能:接受远程计算机的连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。

  返回值:返回一个数组(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址

3)客户端函数

  a)connect函数

  格式:s.connect(address)

  功能:用来请求连接远程服务器

  参数:address为远程服务器地址, 格式为元组(hostname,port),如果连接出错,返回socket.error错误

  b)connect_ex函数

  格式:s.connect_ex(address)

  备注:connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

4)通用函数

  a)recv函数

  格式:s.recv(bufsize[,flag])

  功能:接收远端主机传来的数据

  参数:

    bufsize : 指定要接收的数据大小

    flag : 提供有关消息的其他信息,通常可以忽略

  返回值:返回值为数据以字符串形式</code>

  b)send函数

  格式:s.send(string[,flag])

  功能:发送数据给指定的远端主机

  参数:

    string : 要发送的字符串数据

    flag : 提供有关消息的其他信息,通常可以忽略

  返回值:返回值是要发送的字节数量,该数量可能小于string的字节大小。

c)sendall函数

  格式:s.sendall(string[,flag])

  功能:内部调用了send函数,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。

  参数:同send函数

  返回值 : 成功返回None,失败则抛出异常。

d)close函数

  格式:s.close()

  功能:关闭套接字

e)recvfrom函数

  格式:s.recvfrom(bufsize[.flag])

  功能:与recv()类似,区别是返回值不同

  返回值:返回一个数组(data,address),其中data是包含接收数据的字符串,address是发送数据的套接字地址。

f)sendto函数

  格式:s.sendto(string[,flag],address)

  功能:将数据发送到套接字

  参数:

    string : 要发送的字符串数据

    flag : 提供有关消息的其他信息,通常可以忽略

    address是形式为(ipaddr,port)的元组,指定远程地址

  返回值:返回值是要发送的字节数量

  备注:该函数主要用于UDP协议。

g)settimeout函数

  格式:s.settimeout(timeout)

  功能:设置套接字操作的超时期

  参数:timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

h)getpeername函数

  格式:s.getpeername()

  功能:获取连接套接字的远程地址

  返回值:返回值通常是元组(ipaddr,port)。

i)getsockname函数

  格式:s.getsockname()

  功能:获取套接字自己的地址

  返回值:通常是一个元组(ipaddr,port)

  socket中常用的函数就上面这些了。先用上面这些函数尝试TCP协议下的socket通信。

  服务器端代码如下:

import socket

sk = socket.socket()

sk.bind(('127.0.0.1' ,8088))

sk.listen(5)

print('正在等待客户端连接……')

conn , addr = sk.accept()

print('客户端已连接到服务器……')

mes_from_client = conn.recv(1024).decode('utf-8')

print(mes_from_client)

mes_to_server = '你好,客户端,已收到你的信息!'.encode('utf-8')#发送的数据必须是byte类型

conn.send(mes_to_server)

conn.close()

sk.close()

  客户端代码:

import socket

sk = socket.socket()

sk.connect(('127.0.0.1',8088))

mes_to_server = '你好,服务器!'.encode('utf-8')#发送的数据必须是byte类型

sk.send(mes_to_server)

mes_from_server = sk.recv(1024).decode('utf-8')

print(mes_from_server)

sk.close()

  注意:上述两代码块必须放在两不同的py文件中,且必须先运行服务器代码,然后在开启客户端。开启服务器后,首先输出“正在等待客户端连接……”,然后进程会阻塞在accept函数中,下面的代码不会被执行,知道有客户端连接过来。开启客户端后,服务器端会先收到客户端发来的信息,然后客户端也会受到服务器发来的信息。

  上面的例子中,服务器和客户端都是收发了一条信息后socket关闭,如果要保持连接进行长时间通信呢?那么,我们可以把收发函数放入一个“while True”循环中:

  服务器端代码:

import socket

BUF_SIZE = 1024  #设置缓冲区大小

server_addr = ('127.0.0.1', 8089)  #IP和端口构成表示地址

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #生成一个新的socket对象

server.bind(server_addr)  #绑定地址

print("socket与地址绑定完成……")

server.listen(5)  #监听, 最大监听数为5

print("socket监听开始……")

client, client_addr = server.accept()  #接收TCP连接, 并返回新的套接字和地址, 阻塞函数

print("报告:有客户端请求连接,正在连接……")

print('客户端地址为:{}'.format( client_addr))

while True :

    mes_from_client = client.recv(BUF_SIZE)  #从客户端接收数据

    mes = mes_from_client.decode('utf-8')

    print('客户端说:{}'.format(mes))

    mes = input('回复客户端的信息>')

    mes_to_client = mes.encode('utf-8')

    client.sendall(mes_to_client)  #发送数据到客户端

server.close()

  客户端代码:

import socket

BUF_SIZE = 1024  #设置缓冲区的大小

server_addr = ('127.0.0.1', 8089)  #IP和端口构成的服务器地址

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #返回新的socket对象

client.connect(server_addr)  #连接服务器

while True:

    mes = input("发送给服务器的信息> ")

    mes_to_server = mes.encode('utf-8')

    client.sendall(mes_to_server)  #发送数据到服务器

    mes_from_server = client.recv(BUF_SIZE)  #从服务器端接收数据

    mes = mes_from_server.decode('utf-8')

    print(mes)

client.close()

  运行上述代码后,客户端和服务器可以长时间维持通信。不过,使用socket时一定要注意,有发才有收,收发必相等,否则,就回出现异常。如果需要求换其他客户端与当前服务器进行通信,必须先断开当前客户端的连接。

  再来尝试UDP协议下socket通信:

  服务器端代码:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

sk.bind(('127.0.0.1' , 8090))

print('等待客户端发来消息……')

msg , addr = sk.recvfrom(1024) # 此处会阻塞

print(msg.decode('utf-8'))

mes_to_server = '你好,客户端,已收到你的信息!'.encode('utf-8')#发送的数据必须是byte类型

sk.sendto(mes_to_server,addr)

sk.close()

  客户端代码:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ('127.0.0.1' , 8090)

mes_to_server = '你好,服务器!'.encode('utf-8')#发送的数据必须是byte类型

sk.sendto(mes_to_server , ip_port)

ret , addr = sk.recvfrom(1024)

print(ret.decode('utf-8'))

sk.close()

  如果需要不停收发消息,代码更改如下:

  服务器端代码:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

sk.bind(('127.0.0.1' , 8090))

print('等待客户端发来消息……')

while True:

    msg , addr = sk.recvfrom(1024) # 此处会阻塞

    print('收到{}发来的信息,内容是:{}'.format(addr , msg.decode('utf-8')))

    mes_to_server = input('>>>').encode('utf-8')#发送的数据必须是byte类型

    sk.sendto(mes_to_server,addr)

sk.close()

  客户端代码:

import socket

sk = socket.socket(type=socket.SOCK_DGRAM)

ip_port = ('127.0.0.1' , 8090)

while True:

    mes_to_server = input('>>>').encode('utf-8')#发送的数据必须是byte类型

    sk.sendto(mes_to_server , ip_port)

    ret , addr = sk.recvfrom(1024)

    print(ret.decode('utf-8'))

sk.close()

  使用socket进行UDP协议下通信时,可以多个客户端与服务器通信,也就是说,上面客户端代码你可以另开一个进程与服务器通信,且不需要关闭当前客户端,这是TCP协议与UDP协议下socket通信的一个不同之处。

5 总结

  本篇初步总结了Python网络编程中socket模块的使用,事实上只是大致总结了基本函数的用法。其中诸多内容借鉴了一下两篇博客,感谢两位博主。

  参考资料:

  https://www.cnblogs.com/maociping/p/5112019.html

  https://www.cnblogs.com/zhangyux/p/6109284.html

Python网络编程之socket应用的更多相关文章

  1. Python网络编程之Socket的简单实现

    一.引入 关于Python的网络编程,最基础莫过于socket了. socket,又称“套接字”,网络上的两个程序如果想要实现双向的数据通信,需要建立连接,这个连接的一端就称为一个socket. py ...

  2. python网络编程之socket

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  3. Python网络编程之socket编程

    什么是Socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面 ...

  4. python 网络编程之socket开发和socketserver模块

    一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务端为你提供视频 ...

  5. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

  6. Python网络编程之TCP套接字简单用法示例

    Python网络编程之TCP套接字简单用法示例 本文实例讲述了Python网络编程之TCP套接字简单用法.分享给大家供大家参考,具体如下: 上学期学的计算机网络,因为之前还未学习python,而jav ...

  7. 网络编程之Socket & ServerSocket

    网络编程之Socket & ServerSocket Socket:网络套接字,网络插座,建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP ...

  8. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  9. [深入浅出Cocoa]iOS网络编程之Socket

    http://blog.csdn.net/kesalin/article/details/8798039 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   [深入浅出Co ...

随机推荐

  1. github pages 正确访问方式

    ❌ http://username.github.io ✅ http://username.github.io. github pages 绑定域名后,由于浏览器dns缓存,需要重启一下

  2. 【转】C#中Graphics的画图代码

    C#中Graphics的画图代码[转] 架上图片了你就可以在画板上涂改了啊 我要写多几个字上去string str = "Baidu"; //写什么字?Font font = Fo ...

  3. 【leetcode 简单】 第九十题 字符串中的第一个唯一字符

    给定一个字符串,找到它的第一个不重复的字符,并返回它的索引.如果不存在,则返回 -1. 案例: s = "leetcode" 返回 0. s = "loveleetcod ...

  4. HDU 3449 Consumer (背包问题之有依赖背包)

    题目链接 Problem Description FJ is going to do some shopping, and before that, he needs some boxes to ca ...

  5. C. Connect Three(构造)

    题目链接:http://codeforces.com/contest/1087/problem/C 题目大意:给你三个点的坐标,让你用尽可能少的方块,让这三个点连起来. 具体思路: 我们先对这三个点进 ...

  6. TCP确认延时和Nagle算法

    TCP确认延时和Nagle算法 nagle 算法是   发送端 收到前一个报文的确认然后再发送下一个tcp数据.这样可以避免大量的小数据. TCP_NODELAY选项控制. Delay ACK是   ...

  7. win10定时关机

    CMD  输入shutdown -s -t 7200这个命令,大家可以自已设置7200这个时间,自己算一下60分钟=3600秒:

  8. perl6 Net::HTTP 不能发送https请求

    如下命安装必要的包: sudo apt install libssl1.0.0 libssl-dev zef install IO::Socket::SSL zef install Net::HTTP

  9. 【Python项目】简单爬虫批量获取资源网站的下载链接

    简单爬虫批量获取资源网站的下载链接 项目链接:https://github.com/RealIvyWong/GotDownloadURL 1 由来 自己在收集剧集资源的时候,这些网站的下载链接还要手动 ...

  10. 渗透测试===kali linux的安装

    方法一: kali linux 安装在本地的vitural box 或者 wm ware中 方法二: 安装在移动硬盘或者储存卡中,插到电脑就能用