Python 之 Socket编程(TCPUDP)

Python017

Python 之 Socket编程(TCPUDP),第1张

socket(family,type[,protocal]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。

有效的端口号: 0~ 65535

但是小于1024的端口号基本上都预留给了操作系统

POSIX兼容系统(如Linux、Mac OS X等),在/etc/services文件中找到这些预留端口与的列表

面向连接的通信提供序列化、可靠的和不重复的数据交付,而没有记录边界。意味着每条消息都可以拆分多个片段,并且每个消息片段都能到达目的地,然后将它们按顺序组合在一起,最后将完整的信息传递给等待的应用程序。

实现方式(TCP):

传输控制协议(TCP), 创建TCP必须使用SOCK_STREAM作为套接字类型

因为这些套接字(AF_INET)的网络版本使用因特网协议(IP)来搜寻网络中的IP,

所以整个系统通常结合这两种协议(TCP/IP)来进行网络间数据通信。

数据报类型的套接字, 即在通信开始之前并不需要建议连接,当然也无法保证它的顺序性、可靠性或重复性

实现方式(UDP)

用户数据包协议(UDP), 创建UDP必须使用SOCK_DGRAM (datagram)作为套接字类型

它也使用因特网来寻找网络中主机,所以是UDP和IP的组合名字UDP/IP

注意点:

1)TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的,每次发送要指定是发给谁。

2)服务端与客户端不能直接发送列表,元组,字典。需要字符串化repr(data)。

TCP的优点: 可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。

TCP的缺点: 慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

什么时候应该使用TCP : 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输.

UDP的优点: 快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击……

UDP的缺点: 不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。

什么时候应该使用UDP: 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP ……

优点:

(1)TCP是面向连接的运输层协议;

(2)每一条TCP连接只能有两个端点(即两个套接字),只能是点对点的;

(3)TCP提供可靠的传输服务。传送的数据无差错、不丢失、不重复、按序到达;

(4)TCP提供全双工通信。允许通信双方的应用进程在任何时候都可以发送数据,因为两端都设有发送缓存和接受缓存;

(5)面向字节流。虽然应用程序与TCP交互是一次一个大小不等的数据块,但TCP把这些数据看成一连串无结构的字节流,它不保证接收方收到的数据块和发送方发送的数据块具有对应大小关系,例如,发送方应用程序交给发送方的TCP10个数据块,但就受访的TCP可能只用了4个数据块久保收到的字节流交付给上层的应用程序,但字节流完全一样。

缺点:

慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接。事实上,每个连接都会占用系统的CPU、内存等硬件资源。因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

TCP的应用场景:

当对网络通讯质量有要求的时候。例如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用。如:用于文件传输(FTP HTTP 对数据准确性要求高,速度可以相对慢),发送或接收邮件(POP IMAP SMTP 对数据准确性要求高,非紧急应用),远程登录(TELNET SSH 对数据准确性有一定要求,有连接的概念)等等。

优点:

(1)UDP是无连接的传输层协议;

(2)UDP使用尽最大努力交付,不保证可靠交付;

(3)UDP是面向报文的,对应用层交下来的报文,不合并,不拆分,保留原报文的边界;

(4)UDP没有拥塞控制,因此即使网络出现拥塞也不会降低发送速率;

(5)UDP支持一对一 一对多 多对多的交互通信;

(6)UDP的首部开销小,只有8字节.

缺点:

不可靠,不稳定。 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。

UDP的应用场景:

当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 UDP一般用于即时通信(QQ聊天 对数据准确性和丢包要求比较低,但速度必须快),在线视频(RTSP 速度一定要快,保证视频连续,但是偶尔花了一个图像帧,人们还是能接受的),网络语音电话(VoIP 语音数据包一般比较小,需要高速发送,偶尔断音或串音也没有问题)等等。

(1)TCP面向连接(如打电话要先拨号建立连接)UDP是无连接的,即发送数据之前不需要建立连接

(2)TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达UDP尽最大努力交付,即不保证可靠交付

(3)TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

(4)每一条TCP连接只能是点到点的UDP支持一对一,一对多,多对一和多对多的交互通信

(5)TCP首部开销20字节UDP的首部开销小,只有8个字节

(6)TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

HTTP、HTTPS、FTP、TELNET、SMTP(简单邮件传输协议)协议基于可靠的TCP协议。TFTP、DNS、DHCP、TFTP、SNMP(简单网络管理协议)、RIP基于不可靠的UDP协议

Python中函数参数的传递是通过“赋值”来传递的。但这条规则只回答了函数参数传递的“战略问题”,并没有回答“战术问题”,也就说没有回答怎么赋值的问题。函数参数的使用可以分为两个方面,一是函数参数如何定义,二是函数在调用时的参数如何解析的。而后者又是由前者决定的。函数参数的定义有四种形式

1. F(arg1,arg2,...)

2. F(arg2=<value>,arg3=<value>...)

3. F(*arg1)

4. F(**arg1)

第1 种方式是最“传统”的方式:一个函数可以定义不限个数参数,参数(形式参数)放在跟在函数名后面的小括号中,各个参数之间以逗号隔开。用这种方式定义的函数在调用的时候也必须在函数名后的小括号中提供相等个数的值(实际参数),不能多也不能少,而且顺序还必须相同。也就是说形参和实参的个数必须一致,而且想给形参1的值必须是实参中的第一位,形参与实参之间是一一对应的关系,即“形参1=实参1 形参2=实参2...”。很明显这是一种非常不灵活的形式。比如:"def addOn(x,y): return x + y",这里定义的函数addOn,可以用addOn(1,2)的形式调用,意味着形参x将取值1,主将取值2。addOn(1,2,3)和addOn (1)都是错误的形式。

第2种方式比第1种方式,在定义的时候已经给各个形参定义了默认值。因此,在调用这种函数时,如果没有给对应的形式参数传递实参,那么这个形参就将使用默认值。比如:“def addOn(x=3,y=5): return x + y”,那么addOn(6,5)的调用形式表示形参x取值6,y取值5。此外,addOn(7)这个形式也是可以的,表示形参x取值7,y取默认值5。这时候会出现一个问题,如果想让x取默认值,用实参给y赋值怎么办?前面两种调用形式明显就不行了,这时就要用到Python中函数调用方法的另一大绝招 ──关健字赋值法。可以用addOn(y=6),这时表示x取默认值3,而y取值6。这种方式通过指定形式参数可以实现可以对形式参数进行“精确攻击”,一个副带的功能是可以不必遵守形式参数的前后顺序,比如:addOn(y=4,x=6),这也是可以的。这种通过形式参数进行定点赋值的方式对于用第1种方式定义的函数也是适用的。

上面两种方式定义的形式参数的个数都是固定的,比如定义函数的时候如果定义了5个形参,那么在调用的时候最多也只能给它传递5个实参。但是在实际编程中并不能总是确定一个函数会有多少个参数。第3种方式就是用来应对这种情况的。它以一个*加上形参名的方式表示,这个函数实际参数是不一定的,可以是零个,也可以是N个。不管是多少个,在函数内部都被存放在以形参名为标识符的tuple中。比如:

对这个函数的调用addOn() addOn(2) addOn(3,4,5,6)等等都是可以的。

与第3种方式类似,形参名前面加了两个*表示,参数在函数内部将被存放在以形式名为标识符的dictionary中。这时候调用函数必须采用key1=value1、key2=value2...的形式。比如:

1. def addOn(**arg):

2. sum = 0

3. if len(arg) == 0: return 0

4. else:

5. for x in arg.itervalues():

6. sum += x

7. return sum

那么对这个函数的调用可以用addOn()或诸如addOn(x=4,y=5,k=6)等的方式调用。

上面说了四种函数形式定义的方式以及他们的调用方式,是分开说的,其实这四种方式可以组合在一起形成复杂多样的形参定义形式。在定义或调用这种函数时,要遵循以下规则:

1. arg=<value>必须在arg后

2. *arg必须在arg=<value>后

3. **arg必须在*arg后

在函数调用过程中,形参赋值的过程是这样的:

首先按顺序把“arg”这种形式的实参给对应的形参

第二,把“arg=<value>”这种形式的实参赋值给形式

第三,把多出来的“arg”这种形式的实参组成一个tuple给带一个星号的形参

第四,把多出来的“key=value”这种形式的实参转为一个dictionary给带两个星号的形参。

例子:

1. def test(x,y=5,*a,**b):

2. print x,y,a,b

就这么一个简单函数,来看看下面对这个函数调用会产生什么结果:

test(1) ===>1 5 () {}

test(1,2) ===>1 2 () {}

test(1,2,3) ===>1 2 (3,) {}

test(1,2,3,4) ===>1 2 (3,4)

test(x=1) ===>1 5 () {}

test(x=1,y=1) ===>1 1 () {}

test(x=1,y=1,a=1) ===>1 1 () {'a':1}

test(x=1,y=1,a=1,b=1) ===>1 1 () {'a':1,'b':1}

test(1,y=1) ===>1 1 () {}

test(1,2,y=1) ===>出错,说y给赋了多个值

test(1,2,3,4,a=1) ===>1 2 (3,4) {'a':1}

test(1,2,3,4,k=1,t=2,o=3) ===>1 2 (3,4) {'k':1,'t':2,'o':3}