1 原理

1.1 模型

  应用层协议需要必须传输数据,需要把数据封装为TCP/UDP包来传输,这个对TCP/UDP的封装就是socket通信。在socket里,包括send和receive。

  一个服务器上最多开通的port为65535个,一个ServerAPP监听在它的ip:port上,然后client 给这个ip:port发送socket send通信,信息里封装自己的ip:port(client的port是随机分配的),ServerAPP收到后, 返回信息给client的ip:port。clinet就收到了消息。

  

1.2 socket families

  socket.AF_UNIX  本机之间通信

  socket.AF_INET  ipv4网络通信

1.3 socket types

  socket.SOCK_STREAM  tcp通信,最常用。

  socket.SOCK_DGRAM   udp通信

  socket.SOCK_RAW    原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。就是说前两个是在传输层,这个是在网络层,甚至可以伪造ip地址。

1.4 基本实现

客户端:

先声明一个socket实例

然后连接到一个server

然后发数据。

然后接受server发过来的数据(是bytes类型,需要解码)

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('localhost',3939))
client.send(b'in client,to server')
data = client.recv(1024)
print(data.decode())
client.close()

服务端:

先声明socket实例(地址族和类型)

accept后就处于阻塞状态,等待client。conn就是为连接过来的client开启的实例。

(这时客服端断开,server会收到一个null,会进入死循环。)

然后使用recv方法,将接受的数据赋值给data。recv(n)中n的值,官方建议最大8192(bytes)。

服务端后面把相关数据send给client的conn实例。

>>>

这样的服务端只能并发接受一个client的连接,并且这个连接断开后服务端自己也停止运行了。

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('localhost',3939))
server.listen()
conn,addr = server.accept()
print(conn,addr)
data = conn.recv(1024)
print('receive: %s' %(data))
send_data = '在服务器,给客户端'.encode()
conn.send(send_data)

  

1.5 服务端响应多个client(还是单并发)

二层循环中,if not data是为了应对client断开时发送过来的null导致的死循环

外层循环是为了当一个client断开后,服务端重新accept阻塞,然后把新client请求复制给conn。

在bind时,0.0.0.0代表本机的ip和127.0.0.1。如果bind ip,则客户端listen 127.0.0.1无法访问。如果bind 127.0.0.1,则网络中其他ip无法访问。0.0.0.0则都能。

服务端接受到的数据,用subprocess.Popen调用shell。

服务端:

import socket
import subprocess server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('0.0.0.0',3937))
server.listen()
while True:
conn,addr = server.accept()
while True:
data = conn.recv(1024)
if not data:
print('conn lost')
break
print('执行指令: %s' % (data))
p = subprocess.Popen(data.decode(), stdout=subprocess.PIPE, shell=True)
stdout = p.stdout.read()
if len(stdout) == 0:
send_data = 'cmd has no output...'
else:
send_data = stdout
print('data size: %d' %(len(send_data)))
conn.send(str(len(send_data)).encode())
conn.send(send_data)

  

1.6 根据数据包长度接受多包完整数据

客户端:

#!/usr/bin/env python
import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('192.168.10.12',3937)) while True:
msg = input('>>').strip()
if len(msg) == 0:continue #防止空输入产生卡死
client.send(msg.encode()) #bytes类型发送
rece_total_size = int(client.recv(1024).decode('gbk')) #返回数据的长度
#print(rece_total_size)
rece_current_size = 0 #计算已经收到的长度
rece_current_data = b'' #收集已经接受的数据段 while rece_current_size != rece_total_size: #每个数据包接受后都要进行判断,长度一致说明接受已完成。
data = client.recv(1024)
rece_current_data += data
rece_current_size += len(data)
#print('rece_current_size: %d' %(rece_current_size)) print(rece_current_data.decode('gbk')) #打印最终接受的数据

  

1.7 粘包

  1.5中这两条send语句,在win没有问题,但在linux上会被合并,然后从缓冲区里一次性发给client。这种紧挨着的send语句被一次性发送的现象叫粘包。

  一种解决办法是加入防粘包信号

服务端:

import socket
import subprocess server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('localhost',3937))
server.listen()
while True:
conn,addr = server.accept()
while True:
data = conn.recv(1024)
if not data:
print('conn lost')
break
print('执行指令: %s' % (data))
p = subprocess.Popen(data.decode(), stdout=subprocess.PIPE, shell=True)
stdout = p.stdout.read()
if len(stdout) == 0:
send_data = 'cmd has no output...'
else:
send_data = stdout
print('data size: %d' %(len(send_data)))
conn.send(str(len(send_data)).encode())
conn.recv(1024) #接受防粘包信息
conn.send(send_data)

客户端:

#!/usr/bin/env python
import socket client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('192.168.10.12',3935)) while True:
msg = input('>>').strip()
if len(msg) == 0:continue #防止空输入产生卡死
client.send(msg.encode()) #bytes类型发送
rece_total_size = int(client.recv(1024).decode()) #返回数据的长度
client.send(b'1') #发送防粘包信号
#print(rece_total_size)
rece_current_size = 0 #计算已经收到的长度
rece_current_data = b'' #收集已经接受的数据段 while rece_current_size != rece_total_size: #每个数据包接受后都要进行判断,长度一致说明接受已完成。
data = client.recv(1024)
rece_current_data += data
rece_current_size += len(data)
#print('rece_current_size: %d' %(rece_current_size)) print(rece_current_data.decode()) #打印最终接受的数据

  

1.8 传文件

  socket文件传输效率低,容易丢包,实际上传文件用RabbitMQ消息队列。

2 socketserver模块

  实际上就是对socket的封装,这里模块里包含一些简化TCP、UDP操作的类。

  class socketserver.TCPServer()   #使用tcp协议,在服务端和客户端之间提供连续的数据流。单线程。

  class socketserver.ForkingTCPServer()  #多进程

  class socketserver.ThreadingTCPServer()   #多线程

  

2.1 TCPServer

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):    #首先要定义一个处理基类
def handle(self): #对这个基类进行重写
while True:
try:
self.data = self.request.recv(1024).strip() #self是一个实例,下面有requset这个子类,它里面有recv和send方法。
print(self.client_address[0]) #实例的连接客户端地址,client_address[0]是ip,[1]是port print(self.data) #recv的内容赋值给data
self.request.send(self.data.upper())
except: #当客户端断开时会抛出一个异常,所以要进行异常处理
print('%s disconnected..' %(self.client_address[0]))
break if __name__ == '__main__':
HOST,PORT = '0.0.0.0',3934
conn = socketserver.TCPServer((HOST,PORT),MyTCPHandler) #每个请求过来,TCPServer就会去实例化MyTCPHandler这个基类,然后用它的handler去和客户端交互。
conn.serve_forever() #serve_forver表示不停息的响应。

  

2.2 ForkingTCPServer

if __name__ == '__main__':
HOST,PORT = '0.0.0.0',3934
conn = socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandler)
conn.serve_forever()

没有启动客户端时:

# ps aux | grep python3 | grep -v grep
root 40981 0.0 0.7 132580 7340 pts/1 S+ 14:38 0:00 python3 ts.py

启动每个客户端,就会多一条进程

[root@yhzk01 scripts]# ps aux | grep python3 | grep -v grep
root 40981 0.0 0.7 132580 7340 pts/1 S+ 14:38 0:00 python3 ts.py
root 41069 0.0 0.4 132580 4820 pts/1 S+ 14:46 0:00 python3 ts.py
root 41070 0.0 0.4 132580 4820 pts/1 S+ 14:46 0:00 python3 ts.py
root 41071 0.0 0.4 132580 4820 pts/1 S+ 14:46 0:00 python3 ts.py

  

2.3 ThreadingTCPServer

if __name__ == '__main__':
HOST,PORT = '0.0.0.0',3934
conn = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
conn.serve_forever()

 

socket通信模块的更多相关文章

  1. Jmeter socket接口测试

    一.Socket简介 什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把 TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程 ...

  2. ASP.NET平台下从浏览器地址栏输入之后发生的事

    浏览器一般内嵌两个模块: Socket通信模块 → 浏览器将地址栏的数据及其他的数据放入http协议的请求头文件中,Socket将此http请求数据发送到远程服务器端 浏览器引擎渲染模块 → 浏览器接 ...

  3. python项目开发:ftp server开发

    程序要求: 1.用户加密认证 (对用户名密码进行MD5验证)2.允许同时多用户登陆 (使用socket server方法,为每个用户都创建一个信息文件)3.每个用户有自己的家目录,且只能访问自己的家目 ...

  4. 利用WebSocket和EventSource实现服务端推送

    可能有很多的同学有用 setInterval 控制 ajax 不断向服务端请求最新数据的经历(轮询)看下面的代码: setInterval(function() { $.get('/get/data- ...

  5. Socket异步通信学习一

    最近在做一个频谱管理项目,负责通信模块,自己也是小白,重头学起,直至今天通信基本框架已经完成,把自己在学习中的心得与大家分享一下,做一个socket系列的博文,顺便加固一下自己对socket通信的认识 ...

  6. 【socket-python应用】控制泓格ET-7044通信模块输入DI输出DO

    socket-python应用:控制泓格ET-7044通信模块输入DI输出DO 本节主要内容: 1.socket-python建立TCP通信 2.配合泓格通信模块说明书,查看输入输出寄存器地址,发送指 ...

  7. 【Socket】linux黑客之网络嗅探底层原理

      1.mystery引入 1)网络嗅探属于网络攻防类的安全软件,其基于原始套接字技术开发的 2)原始套接字是一种套接字底层技术,它工作在网络层 3)谈到网络安全,刚好本学期学过这门课程,这里myst ...

  8. 第08课:【实战】Redis网络通信模块源码分析(1)

    我们这里先研究redis-server端的网络通信模块.除去Redis本身的业务功能以外,Redis的网络通信模块实现思路和细节非常有代表性.由于网络通信模块的设计也是Linux C++后台开发一个很 ...

  9. 合宙Luat | Cat.1 Socket数据收不到?学会两招不掉线

    1 服务器收不到Socket数据的原因 Socket是大家使用Cat.1模块常用的功能之一,但Cat.1模块不是直接跟服务器连接,而是通过NAT(即网络地址转换)与服务器连接. 一个会话建立后会在NA ...

随机推荐

  1. C++游戏系列2:角色装备武器

    很多其它见:C++游戏系列文件夹 知识点:类的组合,A类的数据成员.是B类的对象,或B类的对象.做A类的数据成员 [项目-带武器的游戏角色] 设计一个武器类,其数据成员至少要有武器名.威力,还能够加上 ...

  2. android 图片内存问题

    在android开发过程中,经常遇到oom的问题,原因有很多种,其中最难处理的是图片造成的oom,于是在网络上对比了几个图片加载的库,最终选择了Facebook的fresco库(github地址:ht ...

  3. C语言文件读写Demo

    CIODemo.c #include <stdio.h> #include <time.h> #define INPUT_BUFFER_SIZE 100 * 1024 int ...

  4. cocos2d-x交叉编译到安卓

    ccocos2d-x是一个基于MIT协议的开源框架,用于构建游戏.应用程序和其它图形界面交互应用. 它的最大特点就是跨平台性,支持IOS, Android.Windows, WindowsPhone等 ...

  5. Linux epoll 源码注释

    https://www.cnblogs.com/stonehat/p/8613505.html 这篇文章值得好好读,先留个记录,回头看. IO多路复用之epoll总结 - Anker's Blog - ...

  6. js 时间戳精确值的问题

    最近做一个多图上传的功能,通过name + 时间戳命名,结果发现时间戳竟然一样,一直以为是代码逻辑的问题,结果出错在时间戳的获取上了. 关于时间戳的获取方式: 1.Date.parse() var d ...

  7. SDUT OJ 2892 A (字典树问题-输出出现次数最多的字符串的出现次数,60ms卡时间,指针+最后运行完释放内存)

    A Time Limit: 60ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给出n(1<= n && n <= 2*10^6)个字 ...

  8. contents属性

    CALayer 有一个属性叫做contents,这个属性的类型被定义为id,意味着它可以是任何类型的对象.在这种情况下,你可以给contents属性赋任何值,你的app仍然能够编译通过.但是,在实践中 ...

  9. codeforces 460A Vasya and Socks 解题报告

    题目链接:http://codeforces.com/problemset/problem/460/A 题目意思:有一个人有 n 对袜子,每天早上会穿一对,然后当天的晚上就会扔掉,不过他会在 m 的倍 ...

  10. hdu 4302 Holedox Eating(优先队列/线段树)

    题意:一只蚂蚁位与原点,在x轴正半轴上会不时地出现一些蛋糕,蚂蚁每次想吃蛋糕时选取最近的去吃,如果前后距离相同,则吃眼前的那一块(即方向为蚂蚁的正前),求最后蚂蚁行进距离. 思路:优先队列q存储蚂蚁前 ...