1. import socket
  2. '''
  3. socket.socket(socket_family,socket_type,protocal=0)
  4. socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
  5.  
  6. 获取tcp/ip套接字
  7. tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  8.  
  9. 获取udp/ip套接字
  10. udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  11.  
  12. 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
  13. 例如tcpSock = socket(AF_INET, SOCK_STREAM)
  14. '''

基于TCP的套接字

tcp服务端

  1. 1 ss = socket() #创建服务器套接字
  2. 2 ss.bind() #把地址绑定到套接字
  3. 3 ss.listen() #监听链接
  4. 4 inf_loop: #服务器无限循环
  5. 5 cs = ss.accept() #接受客户端链接
  6. 6 comm_loop: #通讯循环
  7. 7 cs.recv()/cs.send() #对话(接收与发送)
  8. 8 cs.close() #关闭客户端套接字
  9. 9 ss.close() #关闭服务器套接字(可选)

tcp客户端

  1. 1 cs = socket() # 创建客户套接字
  2. 2 cs.connect() # 尝试连接服务器
  3. 3 comm_loop: # 通讯循环
  4. 4 cs.send()/cs.recv() # 对话(发送/接收)
  5. 5 cs.close() # 关闭客户套接字

socket通信流程与打电话流程类似,我们就以打电话为例来实现一个low版的套接字通信

  1. #_*_coding:utf-8_*_服务端
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. ip_port=('127.0.0.1',9000) #电话卡
  5. BUFSIZE=1024 #收发消息的尺寸
  6. s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
  7. s.bind(ip_port) #手机插卡
  8. s.listen(5) #手机待机
  9.  
  10. conn,addr=s.accept() #手机接电话
  11. # print(conn)
  12. # print(addr)
  13. print('接到来自%s的电话' %addr[0])
  14.  
  15. msg=conn.recv(BUFSIZE) #听消息,听话
  16. print(msg,type(msg))
  17.  
  18. conn.send(msg.upper()) #发消息,说话
  19.  
  20. conn.close() #挂电话
  21.  
  22. s.close() #手机关机
  1. #_*_coding:utf-8_*_客户端
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. ip_port=('127.0.0.1',9000)
  5. BUFSIZE=1024
  6. s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  7.  
  8. s.connect_ex(ip_port) #拨电话
  9.  
  10. s.send('linhaifeng nb'.encode('utf-8')) #发消息,说话(只能发送字节类型)
  11.  
  12. feedback=s.recv(BUFSIZE) #收消息,听话
  13. print(feedback.decode('utf-8'))
  14.  
  15. s.close() #挂电话
  16. '''
  17. 上述流程的问题是,服务端只能接受一次链接,然后就彻底关闭掉了,实际情况应该是,服务端不断接受链接,然后循环通信,通信完毕后只关闭链接,服务器能够继续接收下一次链接,下面是修改版
  18. '''
  1. #_*_coding:utf-8_*_
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. ip_port=('127.0.0.1',8081)#电话卡
  5. BUFSIZE=1024
  6. s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
  7. s.bind(ip_port) #手机插卡
  8. s.listen(5) #手机待机
  9.  
  10. while True: #新增接收链接循环,可以不停的接电话
  11. conn,addr=s.accept() #手机接电话
  12. # print(conn)
  13. # print(addr)
  14. print('接到来自%s的电话' %addr[0])
  15. while True: #新增通信循环,可以不断的通信,收发消息
  16. msg=conn.recv(BUFSIZE) #听消息,听话
  17.  
  18. # if len(msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生
  19.  
  20. print(msg,type(msg))
  21.  
  22. conn.send(msg.upper()) #发消息,说话
  23.  
  24. conn.close() #挂电话
  25.  
  26. s.close() #手机关机
  27.  
  28. 服务端改进版
  1.  
  1. #_*_coding:utf-8_*_
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. ip_port=('127.0.0.1',8081)
  5. BUFSIZE=1024
  6. s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  7.  
  8. s.connect_ex(ip_port) #拨电话
  9.  
  10. while True: #新增通信循环,客户端可以不断发收消息
  11. msg=input('>>: ').strip()
  12. if len(msg) == 0:continue
  13. s.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型)
  14.  
  15. feedback=s.recv(BUFSIZE) #收消息,听话
  16. print(feedback.decode('utf-8'))
  17.  
  18. s.close() #挂电话
  19.  
  20. 客户端改进版
  1. 问题:
  2. 有时在重启服务端时可能会遇到

  3. 这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
  1. 解决方法:
  2. #加入一
  3. #调整socket配置,重用ip和端口
  4.  
  5. phone=socket(AF_INET,SOCK_STREAM)
  6. phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
  7. phone.bind(('127.0.0.1',8080))
  8.  
  9. 方法二
  10.  
  11. 发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
  12. vi /etc/sysctl.conf
  13.  
  14. 编辑文件,加入以下内容:
  15. net.ipv4.tcp_syncookies = 1
  16. net.ipv4.tcp_tw_reuse = 1
  17. net.ipv4.tcp_tw_recycle = 1
  18. net.ipv4.tcp_fin_timeout = 30
  19.  
  20. 然后执行 /sbin/sysctl -p 让参数生效。
  21.  
  22. net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
  23.  
  24. net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
  25.  
  26. net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
  27.  
  28. net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

基于UDP的套接字

  1. udp服务端
  2.  
  3. 1 ss = socket() #创建一个服务器的套接字
  4. 2 ss.bind() #绑定服务器套接字
  5. 3 inf_loop: #服务器无限循环
  6. 4 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
  7. 5 ss.close() # 关闭服务器套接字
  8.  
  9. udp客户端
  10.  
  11. cs = socket() # 创建客户套接字
  12. comm_loop: # 通讯循环
  13. cs.sendto()/cs.recvfrom() # 对话(发送/接收)
  14. cs.close() # 关闭客户套接字

udp服务端、客户端

  1. udp套接字简单示例
  2.  
  3. #_*_coding:utf-8_*_
  4. __author__ = 'Linhaifeng'
  5. import socket
  6. ip_port=('127.0.0.1',9000)
  7. BUFSIZE=1024
  8. udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  9.  
  10. udp_server_client.bind(ip_port)
  11.  
  12. while True:
  13. msg,addr=udp_server_client.recvfrom(BUFSIZE)
  14. print(msg,addr)
  15.  
  16. udp_server_client.sendto(msg.upper(),addr)
  17.  
  18. #_*_coding:utf-8_*_
  19. __author__ = 'Linhaifeng'
  20. import socket
  21. ip_port=('127.0.0.1',9000)
  22. BUFSIZE=1024
  23. udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  24.  
  25. while True:
  26. msg=input('>>: ').strip()
  27. if not msg:continue
  28.  
  29. udp_server_client.sendto(msg.encode('utf-8'),ip_port)
  30.  
  31. back_msg,addr=udp_server_client.recvfrom(BUFSIZE)
  32. print(back_msg.decode('utf-8'),addr)
  33.  
  34. qq聊天(由于udp无连接,所以可以同时多个客户端去跟服务端通信)
  1. #_*_coding:utf-8_*_
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. ip_port=('127.0.0.1',8081)
  5. udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机
  6. udp_server_sock.bind(ip_port)
  7.  
  8. while True:
  9. qq_msg,addr=udp_server_sock.recvfrom(1024)
  10. print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
  11. back_msg=input('回复消息: ').strip()
  12.  
  13. udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
  1. #_*_coding:utf-8_*_
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. BUFSIZE=1024
  5. udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  6.  
  7. qq_name_dic={
  8. '狗哥alex':('127.0.0.1',8081),
  9. '瞎驴':('127.0.0.1',8081),
  10. '一棵树':('127.0.0.1',8081),
  11. '武大郎':('127.0.0.1',8081),
  12. }
  13.  
  14. while True:
  15. qq_name=input('请选择聊天对象: ').strip()
  16. while True:
  17. msg=input('请输入消息,回车发送: ').strip()
  18. if msg == 'quit':break
  19. if not msg or not qq_name or qq_name not in qq_name_dic:continue
  20. udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
  21.  
  22. back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
  23. print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
  24.  
  25. udp_client_socket.close()
  1. #_*_coding:utf-8_*_
  2. __author__ = 'Linhaifeng'
  3. import socket
  4. BUFSIZE=1024
  5. udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  6.  
  7. qq_name_dic={
  8. '狗哥alex':('127.0.0.1',8081),
  9. '瞎驴':('127.0.0.1',8081),
  10. '一棵树':('127.0.0.1',8081),
  11. '武大郎':('127.0.0.1',8081),
  12. }
  13.  
  14. while True:
  15. qq_name=input('请选择聊天对象: ').strip()
  16. while True:
  17. msg=input('请输入消息,回车发送: ').strip()
  18. if msg == 'quit':break
  19. if not msg or not qq_name or qq_name not in qq_name_dic:continue
  20. udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
  21.  
  22. back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
  23. print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
  24.  
  25. udp_client_socket.close()

recv与recvfrom的区别

  1. '''
  2. ============part1:须知============
  3.  
  4. 收发消息的原理须知晓--->请见十一的图:发消息,都是将数据发送到己端的发送缓冲中,收消息都是从己端的缓冲区中收
  5.  
  6. 1. tcp:send发消息,recv收消息
  7.  
  8. 2. udp:sendto发消息,recvfrom收消息
  9.  
  10. ============part2:send与sendinto============
  11.  
  12. tcp是基于数据流的,而udp是基于数据报的:
  13.  
  14. send(bytes_data):发送数据流,数据流bytes_data若为空,自己这段的缓冲区也为空,操作系统不会控制tcp协议发空包
  15. sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包。
  16. ============part3:recv与recvfrom============
  17.  
  18. 1.tcp协议:
  19.  
  20. (1)如果收消息缓冲区里的数据为空,那么recv就会阻塞(阻塞很简单,就是一直在等着收)
  21.  
  22. (2)只不过tcp协议的客户端send一个空数据就是真的空数据,客户端即使有无穷个send空,也跟没有一个样。
  23.  
  24. (3)tcp基于链接通信
  25.  
  26. 基于链接,则需要listen(backlog),指定半连接池的大小
  27. 基于链接,必须先运行的服务端,然后客户端发起链接请求
  28. 对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)
  29. 对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)
  30.  
  31. 2.udp协议
  32.  
  33. (1)如果如果收消息缓冲区里的数据为“空”,recvfrom也会阻塞
  34.  
  35. (2)只不过udp协议的客户端sendinto一个空数据并不是真的空数据(包含:空数据+地址信息,得到的报仍然不会为空),所以客户端只要有一个sendinto(不管是否发送空数据,都不是真的空数据),服务端就可以recvfrom到数据。
  36.  
  37. (3)udp无链接
  38.  
  39. 无链接,因而无需listen(backlog),更加没有什么连接池之说了
  40. 无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失
  41. recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错
  42. 只有sendinto发送数据没有recvfrom收数据,数据丢失
  43.  
  44. ==============注意:===============
  45.  
  46. 1.你单独运行上面的udp的客户端,你发现并不会报错,相反tcp却会报错,因为udp协议只负责把包发出去,对方收不收,我根本不管,而tcp是基于链接的,必须有一个服务端先运行着,客户端去跟服务端建立链接然后依托于链接才能传递消息,任何一方试图把链接摧毁都会导致对方程序的崩溃。
  47.  
  48. 2.上面的udp程序,你注释任何一条客户端的sendinto,服务端都会卡住,为什么?因为服务端有几个recvfrom就要对应几个sendinto,哪怕是sendinto(b'')那也要有。
  49.  
  50. '''

什么是粘包(解法:定长报头)

须知:只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来

首先需要掌握一个socket收发消息的原理

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

Python(socket编程——2)的更多相关文章

  1. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  2. python/socket编程之粘包

    python/socket编程之粘包 粘包 只有TCP有粘包现象,UDP永远不会粘包. 首先需要掌握一个socket收发消息的原理 发送端可以是1k,1k的发送数据而接受端的应用程序可以2k,2k的提 ...

  3. PYTHON SOCKET编程简介

    原文地址: PYTHON SOCKET编程详细介绍   Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 Soc ...

  4. python socket编程笔记

    用python实现一个简单的socket网络聊天通讯 (Linux --py2.7平台与windows--py3.6平台) 人生苦短之我用Python篇(socket编程) python之路 sock ...

  5. [Python_7] Python Socket 编程

    0. 说明 Python Socket 编程 1. TCP 协议 [TCP Server] 通过 netstat -ano 查看端口是否开启 # -*-coding:utf-8-*- "&q ...

  6. Python Socket 编程示例 Echo Server

    简评:我们已经从「Python Socket 编程概览」了解了 socket API 的概述以及客户端和服务器的通信方式,接下来让我们创建第一个客户端和服务器,我们将从一个简单的实现开始,服务器将简单 ...

  7. Python Socket 编程——聊天室演示样例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和client的代码了解主要的 Python Socket 编程模型.本文再通过一个样例来加强一下对 Socket ...

  8. python socket编程入门(编写server实例)+send 与sendall的区别与使用方法

    python 编写server的步骤: 1. 第一步是创建socket对象.调用socket构造函数.如: socket = socket.socket( family, type ) family参 ...

  9. 第九章:Python高级编程-Python socket编程

    第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...

  10. python socket编程详细介绍

    Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...

随机推荐

  1. C++ map修改指定key的value

    对于修改C++指定key的value,网上查了很多,都说直接insert就会覆盖原来的值,是否是这样的呢?  C++ Code  12345678910111213141516171819202122 ...

  2. Gradle -- buildScript块与allprojects块及根级别的repositories区别

    http://blog.sina.com.cn/s/blog_72ef7bea0102vvg3.html

  3. easyui上传文件

    效果图: 代码: <form id="importFileForm" method="post" enctype="multipart/form ...

  4. 剑指 offer set 16 数字在排序数组中出现的次数

    总结 1. Leetcode 上有一道题, 求某一个数字在有序数组中出现的最左位置和最右位置, 而这道题就是那题的变形

  5. 状态栏,ActionBar,工具栏高度调整

    1.在属性中可以这样设置更改ActionBar的高度android:layout_marginTop="?android:attr/actionBarSize" Rect fram ...

  6. jquery与javescript的区别(一)

    一.找元素: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&g ...

  7. poj_2486 动态规划

    题目大意 N个节点构成一棵树,每个节点上有一个权重val[i], 从根节点root出发在树上行走,行走的时候只能沿着树枝行进.最多在树上走k步,每第一次到达某个节点j,可以获得val[j]的收益,求从 ...

  8. maven项目引入jar包

    今天看一下maven项目的创建和具体操作.

  9. you *might* want to use the less safe log_bin_trust_function_creators variable

    报错:you *might* want to use the less safe log_bin_trust_function_creators variable 解决方法如下: 1. mysql&g ...

  10. Maven的使用入门

    0.什么是maven? 它是一个软件开发管理工具,主要管理工作是:依赖管理,项目一键构建 1.我们为什么要使用maven? 使用maven构建的项目不包含jar包文件,所以整个项目的体积非常小 mav ...