socket简介

Python 提供了两个基本的 socket 模块。

第一个是 Socket,它提供了标准的 BSD Sockets API。

第二个是 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发。

socket通信套路

当通过socket建立起2台机器的连接后,本质上socket只干2件事,一是收数据,一是发数据,没数据时就等着。

socket 建立连接的过程跟我们现实中打电话比较像,打电话必须是打电话方和接电话方共同完成的事情,我们分别看看他们是怎么建立起通话的

1、Socket 类型

套接字格式:

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

socket类型

描述

socket.AF_UNIX

只能够用于单一的Unix系统进程间通信

socket.AF_INET

服务器之间网络通信

socket.AF_INET6

IPv6

socket.SOCK_STREAM

流式socket , for TCP

socket.SOCK_DGRAM

数据报式socket , for UDP

socket.SOCK_RAW

原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

socket.SOCK_SEQPACKET

可靠的连续数据包服务

创建TCP Socket:

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

创建UDP Socket:

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


2、Socket 函数

注意点:

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

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

socket函数

描述

服务端socket函数

s.bind(address)

将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址.

s.listen(backlog)

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

s.accept()

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

客户端socket函数

s.connect(address)

连接到address处的套接字。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

s.connect_ex(adddress)

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

公共socket函数

s.recv(bufsize[,flag])

接受TCP套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。

s.send(string[,flag])

发送TCP数据。将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。

s.sendall(string[,flag])

完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

s.recvfrom(bufsize[.flag])

接受UDP套接字的数据。与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

s.sendto(string[,flag],address)

发送UDP数据。将数据发送到套接字,address是形式为(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表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())

s.gettimeout()

返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。

s.fileno()

返回套接字的文件描述符。

s.setblocking(flag)

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

s.makefile()

创建一个与该套接字相关连的文件

3、socket编程思路

TCP服务端:

1 创建套接字,绑定套接字到本地IP与端口

# socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind()

2 开始监听连接                   #s.listen()

3 进入循环,不断接受客户端的连接请求              #s.accept()

4 然后接收传来的数据,并发送给对方数据         #s.recv() , s.sendall()

5 传输完毕后,关闭套接字                     #s.close()

TCP客户端:

1 创建套接字,连接远端地址

# socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.connect()

2 连接后发送数据和接收数据          # s.sendall(), s.recv()

3 传输完毕后,关闭套接字          #s.close()



用程序实现
服务端程序
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 生成套接字类型,这里 指定了链接方式 AF_INET(ipv4),tcp链接类型(SOCK_STREAM) phone.bind(('127.0.0.1', 8080)) # 绑定服务端IP及端口 ,端口范围0-65535 1-1024 为系统使用,定义端口需要避开常用端口,防止冲突 phone.listen(10) # 最大链接数
print('starting....')
#phone.accept() # 程序进行监听,捕捉客户端发起的链接请求
conn,client_addr = phone.accept() #获取客户端链接套接字并赋值给 conn , 客户端信息赋值给 client_addr data=conn.recv(1024) #接收数据最大值,单位 bytes, 对应客户端的发送信息 conn.send('服务端的信息'.encode('utf-8')) #发送值,对应客户端的收取 conn.close() #关闭链接 phone.close() #关闭程序 #输出
starting....

客户端程序

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定义与服务端一样的通信类型 phone.connect(('127.0.0.1',8080)) #填写服务端IP地址及端口,进行链接 phone.send('客户端的信息'.encode('utf-8'))#因为传输都是已bytes 进行传输,所以先要进行解码, 这里为什么不用使用别的套接字进行传输,\
# 是因为之前已经指定了服务端的信息, 所以phone可以直接用来发送接收数据 data = phone.recv(1024) #同理接收 服务器端信息 print(data.decode('utf-8')) #同样编译 phone.close() #关闭链接 #输出 服务端的信息

远端执行命令小程序

服务端

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import subprocess
ip_port=('127.0.0.1',888)
tcp_socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)
while True:
conn,client_addr = tcp_socket_server.accept()
print('客户端',client_addr)
while True:
cmd = conn.recv(1024)#获取用户输入
if len(cmd) == 0:break
print('recv cmd ',cmd)
res= subprocess.Popen(cmd.decode('uft-8'),shell=True, #将用户输入创建进程,将 标准输入/
#标准输出,错误输出,分别赋值。
stdout=subprocess.PIPE,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
stderr =res.stderr.read()#读取错误输出到变量
stdout =res.stdout.read()#读取标准输出到变量
print('res length ',len(stdout))
conn.send(stderr)#发送信息
conn.send(stdout)

客户端

#!/usr/bin/env python
# -*- coding:utf-8 -*- import socket
ip_port =('172.16.0.99',888)
# ip_port =('127.0.0.1',888) s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) res = s.connect_ex(ip_port) #功能与connect 相同 但是成功返回0 不成功返回error
print('建立连接')
while True:
msg = input('>>> ').strip()
if len(msg) == 0:continue
if msg == 'quit':break
s.send(msg.encode('utf-8'))
act_res =s.recv(1024)
print(act_res.decode('utf-8'))

总结

  1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
  2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
  3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略

解决方法

    问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据

这里就需要另外一个模块  struct

有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体.

struct模块中最重要的三个函数是pack(), unpack(), calcsize()

pack(fmt, v1, v2, ...)     按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)

unpack(fmt, string)       按照给定的格式(fmt)解析字节流string,返回解析出来的tuple

calcsize(fmt)                 计算给定的格式(fmt)占用多少字节的内存

服务端

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket,struct,json #socket
import subprocess ip_port=('172.16.0.99',888)
tcp_socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #设置socket状态
#、一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(1000)
while True:
print('开始监听。。。。:')
conn,client_addr = tcp_socket_server.accept()
print('客户端',client_addr)
while True:
cmd = conn.recv(1024)#获取用户输入
print(cmd)
if not cmd :break
print('recv cmd ',cmd) res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr = res.stderr.read()#读取错误输出到变量
print(stderr)
if stderr: #这里判断是否有错误输出
back_msg=stderr
else:
back_msg=res.stdout.read()
headers={'data_size':len(back_msg)} #获取输出的长度
head_json=json.dumps(headers)#序列化
head_json_bytes = bytes(head_json,encoding='utf-8')#转换为bytes
conn.send(struct.pack('i',len(head_json_bytes)))#发送 i(4字节),这里的i就是指定报头的长度
conn.send(head_json_bytes)#发送报头
conn.send(back_msg)#发送真实数据

客户端

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct,json ip_port =('172.16.0.99',888)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res = s.connect_ex(ip_port) #功能与connect 相同 但是成功返回0 不成功返回error print('连接状态',res)
while True:
msg = input('>>> ').strip()
if len(msg) == 0:continue
if msg == 'quit':break
s.send(bytes(msg.encode('utf-8')))
head = s.recv(4)#接收4字节报头
head_json_len = struct.unpack('i',head)[0] #反解出报头的长度
head_json = json.loads(s.recv(head_json_len).decode('utf-8'))#拿到报头
data_len=head_json['data_size'] #取出报头信息 #接收数据
recv_size = 0
recv_data = b''
while recv_size < data_len: #判断数据是否全部接收
recv_data += s.recv(1024) #接收数据
recv_size +=len(recv_data) #接收长度进行判断
print(recv_data.decode('utf-8')) #将接收值进行打印

网络编程(socket简介)的更多相关文章

  1. linux网络编程-socket(37)

    在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项 第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参 ...

  2. 网络编程socket基本API详解(转)

    网络编程socket基本API详解   socket socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket ...

  3. Android 网络编程 Socket

    1.服务端开发 创建一个Java程序 public class MyServer { // 定义保存所有的Socket,与客户端建立连接得到一个Socket public static List< ...

  4. 网络编程Socket之TCP之close/shutdown具体解释(续)

    接着上一篇网络编程Socket之TCP之close/shutdown具体解释 如今我们看看对于不同情况的close的返回情况和可能遇到的一些问题: 1.默认操作的close 说明:我们已经知道writ ...

  5. 铁乐学Python_Day33_网络编程Socket模块1

    铁乐学Python_Day33_网络编程Socket模块1 部份内容摘自授课老师的博客http://www.cnblogs.com/Eva-J/ 理解socket Socket是应用层与TCP/IP协 ...

  6. Python网络编程socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

  7. java网络编程socket\server\TCP笔记(转)

    java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04|  分类: Socket |  标签:java  |举报|字号 订阅     1 TCP的开销 a ...

  8. 网络编程——socket编程

    一.客户端/服务端架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网企业处处是C/S架构 C/S架构与socket的关系:学习socket就是为了完成C/S架构的开发 二.OSI七层 一个 ...

  9. python网络编程-socket编程

     一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...

  10. Python开发【第八篇】:网络编程 Socket

    Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...

随机推荐

  1. 自动化运维工具-Ansible之4-变量

    自动化运维工具-Ansible之4-变量 目录 自动化运维工具-Ansible之4-变量 变量概述 变量的定义和调用 变量优先级测试 变量优先级测试二 变量注册 facts缓存 变量概述 ​ 变量提供 ...

  2. Kubernetes官方java客户端之二:序列化和反序列化问题

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. python常用操作和内置函数

    一.常用数据处理方法. 1.索引:按照号码将对应位置的数据取出使用 2.list将任意类型数据用逗号分割存在列表中 3.range:产生一堆数字(顾头不顾尾) 4.切片:可以从复制数据的一部分,不影响 ...

  4. Apache下的配置文件httpd.conf、httpd-vhosts.conf 转

    Apache下的配置文件httpd.conf.httpd-vhosts.conf(windows) 2013-05-24 22:09 by youxin, 58 阅读, 0 评论, 收藏, 编辑 ht ...

  5. Lesson_strange_words5

    certain 一些 the company's 公司的 implement 实现 plane 平面 sluggishly 迟缓地,缓慢地 frustrated 懊恼的 profiler 分析器,分析 ...

  6. Mirai框架qq机器人教程 新版

    Mirai框架qq机器人教程 新版 前言 资料列表 1.准备 i. 配置java环境 ii. 配置IDE iii. 下载mirai-console-loader(mcl)作为启动器 2.创建mirai ...

  7. MyBatis 查询数据时属性中多对一的问题(多条数据对应一条数据)

    数据准备 数据表 CREATE TABLE `teacher`( id INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY ( ...

  8. Windows DHCP最佳实践(四)

    这是Windows DHCP最佳实践和技巧的最终指南. 如果您有任何最佳做法或技巧,请在下面的评论中发布它们. 在本指南(四)中,我将分享以下DHCP最佳实践和技巧. 使用DHCP中继代理 防止恶意D ...

  9. 浅入深出了解XXE漏洞

    环境搭建 https://github.com/c0ny1/xxe-lab 为了更深入的理解,我准备理论和实际相结合的了解XXE! 浅谈XML 初识XML 一个好的代码基础能帮助你更好理解一类漏洞,所 ...

  10. 发票校验BAPI_INCOMINGINVOICE_CREATE

    CALL FUNCTION 'BAPI_INCOMINGINVOICE_CREATE'    EXPORTING      headerdata                = headerdata ...