python套接字编程

目录

socket是什么

套接字的工作流程

基于tcp的套接字

基于udp的套接字

  

socket是什么

客户端/服务器架构(C/S架构)

服务端:提供服务的一端
客户端:请求服务的一端
互联网中处处是C/S架构
腾讯作为服务端为你提供视频,你得下个腾讯视频客户端才能看它的视频)
C/S架构与socket的关系:
我们学习socket就是为了完成C/S架构的开发

  

socket是什么

Socket是应用层与TCP/IP协议通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数
据,以符合指定的协议。所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的的、规定去编程,写的程序自然就是遵循tcp/udp标准的。
套接字最早起源于Unix系统,一开始使用与一台电脑之间的进程间通讯。 套接字分为面向连接的和无连接的: 第一种是指TCP类型套接字,在通信前需要建立连接,这种连接是较为可靠的,使用的套接字类型是SOCK_STREAM,TCP传输控制协议,经常和IP协议一起使用,称为TCP/IP协议
第二种主要指UDP类型的套接字,无需连接就可以进行通讯,所有速度较快,但是可靠性不高。而且数据是整个发送,不会分成小块。使用的套接字类型是SOCK_DGRAM,UDP协议通常也与IP协议一起使用 套接字分为两种:分别是基于文件型和基于网络型的: 基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切的皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一文件系统间接完成通信 基于网络类型的套接字家族
套接字家族的名字:AF_INET
AF_INET是使用最广泛的一个,python支持很多地址家族,但是由于我们只关心网络编程,所以大部分时候我们只使用AF_INET

  

osi七层

更多的知识请阅读下面的内容:

http://www.cnblogs.com/linhaifeng/articles/5937962.html

一个从应用层发出的信息,往下封装处理数据的过程

socket层

常用方法

服务端套接字函数

s.bind()    绑定(主机,端口号)到套接字

s.listen()  开始TCP监听

s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

客户端套接字函数

s.connect()     主动初始化TCP服务器连接

s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数

s.recv()            接收TCP数据

s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)

s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)

s.recvfrom()        接收UDP数据

s.sendto()          发送UDP数据

s.getpeername()     连接到当前套接字的远端的地址

s.getsockname()     当前套接字的地址

s.getsockopt()      返回指定套接字的参数

s.setsockopt()      设置指定套接字的参数

s.close()           关闭套接字

面向锁的套接字方法

s.setblocking()     设置套接字的阻塞与非阻塞模式

s.settimeout()      设置阻塞套接字操作的超时时间

s.gettimeout()      得到阻塞套接字操作的超时时间

面向文件的套接字的函数

s.fileno()          套接字的文件描述符

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

  

套接字的工作流程

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,
这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

  

基于tcp的套接字

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

  

tcp的套接字模板

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

基于TCP的套接字

注意:
1.不能把程序名称命名为socket

2.发出去的信息使用utf-8编码,解码也使用utf-8解码

3.每一次获得连接,都会获得一个连接对象和地址

#服务端
import socket def main():
s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于tcp的网络通信,第一个参数是地址家族,第二个参数是地址类型
s1.bind(('127.0.0.1',4444)) #绑定ip和端口
s1.listen(5) #最多连接几个客户端
print('-->')
conn,addr = s1.accept() #等待客户端连接连接,如果客户端连接,就会获取到一个连接he和一个地址 data=conn.recv(1024) #接收客户端信息,参数为接收客户端信息的大小限制
print('服务端收到的信息:',data.decode('utf-8')) conn.send(data.upper()) #发送信息 conn.close() #关闭连接 s1.close() #关闭 if __name__=='__main__':
main() #服务端改进后的代码
import socket
def main():
ip_port= ('127.0.0.1',4444)
back_log=5
buffer_size=1024 s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于tcp的网络通信
s1.bind(ip_port) #绑定ip和端口
s1.listen(back_log) #最多连接几个客户端
print('-->')
conn,addr = s1.accept() #等待客户端连接连接,如果客户端连接,就会获取到一个连接he和一个地址 data=conn.recv(buffer_size) #接收客户端信息,参数为接收客户端信息的大小限制
print('服务端收到的信息:',data.decode('utf-8')) conn.send(data.upper()) #发送信息 conn.close() #关闭连接 s1.close() #关闭 if __name__=='__main__':
main()
#客户端
import socket def main():
s2 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s2.connect(('127.0.0.1',4444)) #连接服务端 s2.send('hello'.encode('utf-8')) reponse = s2.recv(1024) print('客户端收到信息:',reponse.decode('utf-8'))
  
  s2.close() if __name__=='__main__':
main() #客户端改进后的代码
import socket def main():
ip_port=('127.0.0.1',4444)
data='hello'
buffer_size=1024 s2 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s2.connect(ip_port) #连接服务端 s2.send(data.encode('utf-8')) reponse = s2.recv(buffer_size) print('客户端收到信息:',reponse.decode('utf-8')) s2.close() if __name__=='__main__':
main()

在继续改进代码前,我们应该知道,服务端可以先发送信息

注意:
收发数据都是从自己的内核缓存区
操作系统来具体控制对硬件的操作

现在我们需要解决的问题是

服务端与客户端只能进行一次通信,而且没有交互

#服务端
import socket def main():
ip_port= ('127.0.0.1',4444)
back_log=5
buffer_size=1024 s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于tcp的网络通信
s1.bind(ip_port) #绑定ip和端口
s1.listen(back_log) #最多连接几个客户端
print('-->')
conn,addr = s1.accept() #等待客户端连接连接,如果客户端连接,就会获取到一个连接he和一个地址 while True:
data=conn.recv(buffer_size) #接收客户端信息,参数为接收客户端信息的大小限制
print('前任:',data.decode('utf-8'))
data1 = input('我:')
conn.send(data1.encode('utf-8')) #发送信息 conn.close() #关闭连接
s1.close() #关闭 if __name__=='__main__':
main()

  

#客户端
import socket def main():
ip_port = ('127.0.0.1', 4444) buffer_size = 1024 s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s2.connect(ip_port) # 连接服务端 while True:
data = input('我:')
s2.send(data.encode('utf-8')) reponse = s2.recv(buffer_size) print('前任:', reponse.decode('utf-8')) s2.close() if __name__ == '__main__':
main()

  

解决两个问题:
1.客户端发送空信息,如果客户端发送空信息,服务端会一直接收不到信息,收信息是从自己内核缓冲区中接收信息,就会一直接收。

2.服务端只能对一个客户端提供服务,如果一个客户端异常断开连接,服务端也会挂掉,我们没有考虑正常中断,所以等会这也是个问题

3.解决服务端如果重启,显示端口被占用的问题 在服务端代码中加入s1.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1),bing()函数前面

#服务端
import socket def main():
ip_port= ('127.0.0.1',4444)
back_log=5
buffer_size=1024 s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于tcp的网络通信
s1.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s1.bind(ip_port) #绑定ip和端口
s1.listen(back_log) #最多连接几个客户端
print('-->')
#循环对多个人接收信息
while True:
conn,addr = s1.accept() #等待客户端连接连接,如果客户端连接,就会获取到一个连接he和一个地址 #循环对一个人接收信息
while True:
try:#只要有客户端断开连接 ,就会结束这个循环
data=conn.recv(buffer_size) #接收客户端信息,参数为接收客户端信息的大小限制
print('前任:',data.decode('utf-8'))
data1 = input('我:')
conn.send(data1.encode('utf-8')) # 发送信息
except Exception:
break
conn.close() #关闭连接 s1.close() #关闭服务端套接字 if __name__=='__main__':
main()

  

#客户端
import socket def main():
ip_port = ('127.0.0.1', 4444) buffer_size = 1024 s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s2.connect(ip_port) # 连接服务端 while True:
data = input('我:')
if not data:continue#如果客户端输入信息为空,则继续下一次循环
s2.send(data.encode('utf-8')) reponse = s2.recv(buffer_size) print('前任:', reponse.decode('utf-8')) s2.close() if __name__ == '__main__':
main()

  

运行演示:

demo4为服务端

demo7,demo10为客户端

现在所谓的对多个客户端提供服务,其实不是并发,服务端只能在同一时间里,对同一个客户端提供服务,只要当其中一个客户端异常断开后,其他客户端才能与服务端建立万完整的三次连接

我们再演示一下客户端正常断开连接时(客户端正常断开连接在代码加一条判断,输入quite)

服务端有什么显示

为什么会出现这样的显示

我们应该怎么改进服务端代码

#服务端
import socket def main():
ip_port= ('127.0.0.1',4444)
back_log=5
buffer_size=1024 s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于tcp的网络通信
s1.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s1.bind(ip_port) #绑定ip和端口
s1.listen(back_log) #最多连接几个客户端
print('-->')
#循环对多个人接收信息
while True:
conn,addr = s1.accept() #等待客户端连接连接,如果客户端连接,就会获取到一个连接he和一个地址 #循环对一个人接收信息
while True:
try:#只要有客户端断开连接 ,就会结束这个循环
data=conn.recv(buffer_size) #接收客户端信息,参数为接收客户端信息的大小限制
print('前任:',data.decode('utf-8'))
data1 = input('我:')
conn.send(data1.encode('utf-8')) # 发送信息
except Exception:
break
conn.close() #关闭连接 s1.close() #关闭服务端套接字 if __name__=='__main__':
main()

  

#客户端
import socket def main():
ip_port = ('127.0.0.1', 4444) buffer_size = 1024 s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s2.connect(ip_port) # 连接服务端 while True:
data = input('我:')
if not data:continue#如果客户端输入信息为空,则继续下一次循环
if data =='quite':break
s2.send(data.encode('utf-8')) reponse = s2.recv(buffer_size) print('前任:', reponse.decode('utf-8')) s2.close() if __name__ == '__main__':
main()

  

运行结果:

 这里我们可以看出来,在前面客户端异常中断的时候,我们加了异常处理,服务端结束里层循环,服务端继续等待下次连接

而客户端我们在前面加上了处理,客户端是输入不了空信息的,服务端也是接收不到空信息的,怎么正常中断连接,服务端接收到了空信息

首先,客户端是已经断开连接了,前面我们已经知道,如果连接断了,服务端的recv是没有意义的,服务端就会相当于接收空信息,我们怎么处理呢“?

只需要在服务端加上一条判断语句,我们只需要知道,正常情况服务端是不可能接收到空信息的,因为在客户端已经做了处理,只要当客户端正常中断请求才会,

所以我们在服务端加的代码就是如果接收的信息为空,就结束里层循环,说明客户端已经断开连接,服务端可以等待下次连接

  

#服务端最后的修订版
import socket def main():
ip_port= ('127.0.0.1',4444)
back_log=5
buffer_size=1024 s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于tcp的网络通信
s1.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s1.bind(ip_port) #绑定ip和端口
s1.listen(back_log) #最多连接几个客户端
print('-->')
#循环对多个人接收信息
while True:
conn,addr = s1.accept() #等待客户端连接连接,如果客户端连接,就会获取到一个连接he和一个地址 #循环对一个人接收信息
while True:
try:#只要有客户端断开连接 ,就会结束这个循环
data=conn.recv(buffer_size) #接收客户端信息,参数为接收客户端信息的大小限制
if not data:break
print('前任:',data.decode('utf-8'))
data1 = input('我:')
conn.send(data1.encode('utf-8')) # 发送信息
except Exception:
break
conn.close() #关闭连接 s1.close() #关闭服务端套接字 if __name__=='__main__':
main()

  

基于udp套接字

基于UDP的套接字

udp是无链接的,先启动哪一端都不会报错

udp服务端

1 ss = socket()   #创建一个服务器的套接字

2 ss.bind()       #绑定服务器套接字

3 inf_loop:       #服务器无限循环

4     cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)

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

udp客户端

cs = socket()   # 创建客户套接字

comm_loop:      # 通讯循环

      cs.sendto()/cs.recvfrom()   # 对话(发送/接收)

cs.close()                      # 关闭客户套接字

  

注意:
不管tcp还是udp收取信息都是从自己的内核缓存中读取信息,tcp的话,如果内核缓存区为空,则recv就会收不到信息,就会阻塞,但是udp能够接收空信息!
而且tcp中只有一个客户端端开连接,后面的才能通信,但是udp由于不需要连接,所以可以同时与多个客户端通信,实现并发

#客户端

from socket import *

def main():
ip_port = ('127.0.0.1', 4444)
buffer_size = 1024 s2 = socket(AF_INET,SOCK_DGRAM) #数据报 while True:
data = input('-->:')
s2.sendto(data.encode('utf--8'),ip_port) #udp发信息没有链接,所以每一个发送信息都需要指定ip和端口 data1,addr=s2.recvfrom(buffer_size) print(data1.decode('utf-8')) if __name__ =='__main__':
main() #服务端 from socket import * def main():
ip_port = ('127.0.0.1', 4444)
buffer_size = 1024 s1 = socket(AF_INET,SOCK_DGRAM) #数据报
s1.bind(ip_port) while True:
data,addr= s1.recvfrom(buffer_size)
print(data.decode('utf-8'),addr) s1.sendto(data.upper(),addr) if __name__ =='__main__':
main()

  

python套接字编程基础的更多相关文章

  1. python套接字编程实现ntp服务和远程命令执行

    python套接字编程实现ntp服务和远程命令执行 目录 基于udp实现ntp服务 基于tcp实现远程命令执行 基于udp实现远程命令执行 tcp与udp的比较 前面关于套接字基础请查阅 https: ...

  2. Python套接字编程(1)——socket模块与套接字编程

    在Python网络编程系列,我们主要学习以下内容: 1. socket模块与基本套接字编程 2. socket模块的其他网络编程功能 3. SocketServer模块与简单并发服务器 4. 异步编程 ...

  3. Python黑帽编程2.8 套接字编程

    Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...

  4. 【Python网络编程】利用Python进行TCP、UDP套接字编程

    之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接 ...

  5. Python 3中套接字编程中遇到TypeError: 'str' does not support the buffer interface的解决办法

    转自:http://blog.csdn.net/chuanchuan608/article/details/17915959 目前正在学习python,使用的工具为python3.2.3.发现3x版本 ...

  6. UDP,TCP的套接字编程的Python实现

    UDP,TCP的套接字编程的Python实现 套接字:连接应用层和运输层,应用层的网络应用程序使用IP地址+端口号来标识自己,然后通过套接字调用运输层为其服务,网络应用程序只能指定自己要使用的网络类型 ...

  7. 入门级:怎么使用C#进行套接字编程(一)

    翻译一篇简单的文章学习下基础,此文针对我等对socket只听说未尝试阶段的水平. How to C# Socket programming C#通过他的命名空间像System.Net和System.N ...

  8. (转载)Linux 套接字编程中的 5 个隐患

    在 4.2 BSD UNIX® 操作系统中首次引入,Sockets API 现在是任何操作系统的标准特性.事实上,很难找到一种不支持 Sockets API 的现代语言.该 API 相当简单,但新的开 ...

  9. 套接字编程(VC_Win32)

    简介(源于维基) Berkeley套接字(也作BSD套接字应用程序接口)刚开始是4.2BSD Unix操作系统(于1983发布)的一套应用程序接口.然而,由于AT&T的专利保护着UNIX,所以 ...

随机推荐

  1. D - Maximizing Advertising

    题目链接:https://cn.vjudge.net/contest/250168#problem/D 题目大意:给你一些点的坐标,这些点属于两个帮派,让你将这些点分进两个不能重叠的矩形中,问你最多两 ...

  2. mysql 案例 ~ pt修复工具的使用

    简介:今天咱们来聊聊PT修复工具pt-table-sync 注意事项:   1 表要有主键或者唯一键   2 针对每一个chunk加的是for update锁   3 修复过程中不能容忍从库延迟 如果 ...

  3. mysql 原理 ~ change buffer

    一 简介:今天咱们来聊聊mysql的change buffer二 详细说明   1 +-change Buffer和数据页一样,也是物理页的一个组成部分,数据结构也是一颗B+树,这棵B+树放在共享表空 ...

  4. Shiro的三种授权(十二)

    前提就是在Realm的授权方法中查询出权限并返回List<String>形式 @Override protected AuthorizationInfo doGetAuthorizatio ...

  5. Python|绝不乱入的靠谱书单

  6. ES系列十四、ES聚合分析(聚合分析简介、指标聚合、桶聚合)

    一.聚合分析简介 1. ES聚合分析是什么? 聚合分析是数据库中重要的功能特性,完成对一个查询的数据集中数据的聚合计算,如:找出某字段(或计算表达式的结果)的最大值.最小值,计算和.平均值等.ES作为 ...

  7. java中printf()方法简单用法

    %n 换行 相当于 \n %c 单个字符 %d 十进制整数 %u 无符号十进制数 %f 十进制浮点数 %o 八进制数 %x 十六进制数 %s 字符串 %% 输出百分号 > 在printf()方法 ...

  8. oracle instantclient_11_2插件安装

    1.安装plsql 2.instantclient_11_2下载,解压到目录 D:\DevTools\instantclient_11_2 3.打开plsql, 点击“取消” 4.选择“工具”--&g ...

  9. 《TCP/IP 详解 卷1:协议》第 3 章:链路层

    在体系结构中,我们知道:链路层(或数据链路层)包含为共享相同介质的邻居建立连接的协议和方法,同时,设计链路层的目的是为 IP 模块发送和接受 IP 数据报,链路层可用于携带支持 IP 的辅助性协议,例 ...

  10. ubuntu 14.04 上配置vlc组播源

    VLC:  Video LAN多媒体播放器,是一个跨平台开源的软件,支持主流的编码格式MPEG-2.H.264等. (1)ubuntu上安装vlc: sudo  apt-get install vlc ...