参考:

  通过编写聊天程序来熟悉python中多线程和socket的用法:https://www.cnblogs.com/mingjiatang/p/4905395.html

  python socket通信:https://yq.aliyun.com/articles/40745?spm=5176.100239.blogcont40768.17.FIFTZv

1.socket使用方法

  a.在python中使用socket时要iamport socket

  b.在使用socket中又服务器端和客户端之分

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

  socket起源于Unix,而Unix/Linux基本哲学之一就是:一切皆文件,即都可以用“打开open—>读写write/read—>关闭close”模式来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。

  可以看下面的图示来形象说明:

服务器:

1、建立一个基于tcp协议的socket类

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

其中AF_INET指定的ipv4的协议,也可以使用AF_INET6指定ipv6的协议,而STREAM是指定面向流的tcp协议。

2、s.bind(‘', 8089))

绑定一个端口号,其中'127.0.0.1'是客户端的ip地址,可以使用’0.0.0.0’来绑定网络中所有的ip,8089是指定的端口,其中端口在小于1024的时候需要有管理员的权限才能绑定。

3、s.listen(5)

开始实行监听参数:代表连接的最大数量

4、sock, addr = s.accept()

接受一个客户端的连接,返回的是一个与客户端保持连接的socket对象以及客户端的ip地址和端口。该方法也会阻塞线程,直到获得客户端的连接。

客户端:

1、s.connect(('127.0.0.1', 80))

连接到服务器,其中'www.baidu.com’也可以是服务器的ip地址。

2、s.send('hello')

发送数据’hello’。TCP连接创建的是双向通道,双方都可以同时给对方发数据。但是谁先发谁后发,怎么协调,要根据具体的协议来决定。

3、s. recv(1024)

接受连接的对方发来的数据。该方法会阻塞当前线程,所以需要一个专门的线程来接受数据。

2.socket 案例

a.socket单线程的阻塞

# 本机实现的单线程非交互式的socket程序
# #### server ###########################################################################################
import socket #导入socket类 HOST ='' #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有,也可以写成0.0.0.0
PORT = 50007 #Server端开放的服务端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #选择Socket类型和Socket数据包类型
s.bind((HOST, PORT)) #绑定IP地址和端口
s.listen(1) #定义侦听数开始侦听(实际上并没有效果)
conn, addr = s.accept() #定义实例,accept()函数的返回值可以看上面的socket函数说明 print 'Connected by', addr
while 1:
data = conn.recv(1024) #接受套接字的数据
if not data:break #如果没有数据接收,则断开连接
print 'revc:',data #发送接收到的数据
conn.sendall(data) #发送接收到的数据
conn.close() #关闭套接字 # #### client ###########################################################################################
import socket HOST = '192.168.1.13' #定义目标主机名
PORT = 50007 #定义目标主机开放服务端口号
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #选择Socket类型和Socket数据包类型
s.connect((HOST, PORT)) #连接到目标主机的socket(套接字)中 s.sendall('Hello, world!') #发送数据
data = s.recv(1024) #接收数据
s.close() #关闭socket
print 'Received', repr(data)
# 单线程+阻塞+交互式socket程序
# #### server:内容同上个例子#######################################################################33######
import socket #导入socket类 HOST ='' #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有,也可以写成0.0.0.0
PORT = 50007 #Server端开放的服务端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #选择Socket类型和Socket数据包类型
s.bind((HOST, PORT)) #绑定IP地址和端口
s.listen(1) #定义侦听数开始侦听(实际上并没有效果)
conn, addr = s.accept() #定义实例,accept()函数的返回值可以看上面的socket函数说明 print 'Connected by', addr
while 1:
data = conn.recv(1024) #接受套接字的数据
if not data:break #如果没有数据接收,则断开连接
print 'revc:',data #发送接收到的数据
conn.sendall(data) #发送接收到的数据
conn.close() #关闭套接字 # #### client ##########################################################################################
import socket HOST = '192.168.1.13'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT)) while True:
user_input = raw_input('msg to send:').strip() #由User输入要发送的数据
s.sendall(user_input)
data = s.recv(1024)
print 'Received', repr(data) s.close()

单线程,即数据的串行发送,会导致阻塞,如果要解决这个问题,当然在Server端就需要支持多线程,即数据折并发。

b.socket(多线程聊天程序实例)

  该程序实现的是一个相对比较简单的聊天程序,由于是基于控制台实现的,所只设计容许两个人聊天,另外消息的编码的分割符为|

# 首先建立一个User的数据结构
import socket class User:
def __init__(self,skt,username='none'):
self.skt=skt
self.username=username
def send_msg(self,msg):
self.skt.send(msg)
def logout(self):
self.skt.close()
# server
import sys
import socket
import threading,time
import User #global variable
userlist=[] def hand_user_con(usr):
try:
isNormar=True
while isNormar:
data=usr.skt.recv(1024)
time.sleep(1)
msg=data.split('|')#分析消息
if msg[0]=='login':
print 'user [%s] login' % msg[1]
usr.username=msg[1]
notice_other_usr(usr)
if msg[0]=='talk':
print 'user[%s]to[%s]:%s' % (usr.username,msg[1],msg[2])
send_msg(msg[1],msg[2])#发送消息给目标用户,参数1:目标用户,参数2:消息内容
if msg[0]=='exit':
print 'user [%s] exit' % msg[0]
isNormar=False
usr.close()
userlist.remove(usr)
except:
isNormar=False #通知其他用户以上的好友
def notice_other_usr(usr):
if(len(userlist)>1):
print 'The two users'
userlist[0].skt.send(("login|%s" % userlist[1].username))
userlist[1].skt.send(("login|%s" % userlist[0].username))
else:
print 'The one users' def send_msg(username,msg):
for usr in userlist:
if(usr.username==username):
usr.skt.send(msg) def main():
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind(('0.0.0.0',9999))
s.listen(5)
print u'waiting for connection...'
while True:
sock,addr=s.accept()#等待用户连接
user=User.User(sock)
userlist.append(user)
t=threading.Thread(target=hand_user_con,args=(user,));
t.start()
s.close() if(__name__=="__main__"):
main()
import sys
import socket
import threading,time #global variable
isNormar=True
other_usr='' def recieve_msg(username,s):
global isNormar,other_usr
print 'Please waiting other user login...'
s.send('login|%s' %username)
while(isNormar):
data= s.recv(1024)#阻塞线程,接受消息
msg=data.split('|')
if msg[0]=='login':
print u'%s user has already logged in, start to chat' % msg[1]
other_usr=msg[1]
else:
print msg[0] #程序入口
def main():
global isNormar,other_usr
try:
print 'Please input your name:'
usrname=raw_input()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("127.0.0.1",9999))
t=threading.Thread(target=recieve_msg,args=(usrname,s))
t.start()
except:
print 'connection exception'
isNormar=False
finally:
pass
while isNormar:
msg=raw_input()#接受用户输入
if msg=="exit":
isNormar=False
else:
if(other_usr!=''):
s.send("talk|%s|%s" % (other_usr,msg))#编码消息并发送
s.close() if __name__=="__main__":
main()

c.socketserver模块,实现socket并发

  socketserver说明如下图所示:

# SockteServer例子说明
# #### server ####################################
import SocketServer #导入SocketServer,多线程并发由此类实现 class MySockServer(SocketServer.BaseRequestHandler): #定义一个类 def handle(self): #handle(self)方法是必须要定义的,可以看上面的说明
print 'Got a new connection from', self.client_address
while True:
data = self.request.recv(1024) #需要通过self的方法调用数据接收函数
if not data:break
print 'recv:', data self.request.send(data.upper()) #需要通过self的方法调用数据接收函数 if __name__ == '__main__': #并非一定要用这样的方式,只是建议这样使用
HOST = '' #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有
PORT = 50007 #Server端开放的服务端口
s = SocketServer.ThreadingTCPServer((HOST, PORT), MySockServer)
#调用SocketServer模块的多线程并发函数
s.serve_forever() #持续接受客户端的连接 # #### client #####################################
import socket HOST = '192.168.1.13'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT)) while True:
user_input = raw_input('msg to send:').strip()
s.sendall(user_input)
data = s.recv(1024)
print 'Received', repr(data) s.close()

d.基于SocketServer多线程的简化SSH程序

# #### server ############################################
import SocketServer
import commands #使用其中的getstatusoutput()函数,让Server端可以识别Client端发送过来的命令并执行
import time #主要使用其中的time.sleep()函数,用来解决Server端发送数据的“连块”问题 class MySockServer(SocketServer.BaseRequestHandler): def handle(self):
print 'Got a new connection from', self.client_address
while True:
cmd = self.request.recv(1024)
if not cmd:
print 'Last connection with:',self.client_address
break cmd_result = commands.getstatusoutput(cmd) #获取Client端的指令并执行,返回结果是一个存储两个元素的元组,第一个元素为0表示成功执行,第二个元素则是命令的执行结果 self.request.send(str(len(cmd_result[1]))) #发送命令执行结果的大小长度,Client端要想接收任意大小的执行结果,就需要根据命令执行结果的大小来选择策略,这里需要注意的是,发送的数据是字符串,所以需要作类型转换 time.sleep(0.2) #睡眠0.2s,是为了解决“连块”的问题 self.request.sendall(cmd_result[1]) #发送命令执行结果 if __name__ == '__main__':
HOST = ''
PORT = 50007
s = SocketServer.ThreadingTCPServer((HOST, PORT), MySockServer) s.serve_forever() # #### client #######################################
import socket HOST = '192.168.1.13'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT)) def data_all(obj, lenth_size):
data = '' #用来存储每一次循环时socket接收的数据,解决socket大概两万多字节的缓冲瓶颈限制
while lenth_size != 0: #如果接收的数据长度不为0,开始执行接收数据处理策略
if lenth_size <= 4096: #这里以4096为单位块,作为每次的数据处理量大小
data_recv = obj.recv(lenth_size) #通过recv()接收数据
lenth_size = 0 #通过这一步的处理,数据全部接收完毕,置lenth_size为0,结束循环,完成数据的接收处理工作
else:
data_recv = obj.recv(4096) #以4096为单位块,一次接收4096的数据量大小
lenth_size -= 4096 #处理完一次4096字节的数据后,将lenth_size减去4096
data += data_recv #判断外层,用本地的data来存储接收到的数据,因为本地的data大小没有限制,所以不存在data饱和无法继续存储数据的问题,但前面socket的recv()函数一次最多只能接收的数据量大小是有限制的,这取决于socket的缓冲区大小,因此data_all函数的作用就是通过使用多次recv()函数,并且每次接收一定量的数据后就进行本地存储,直到把所有的数据都接收完毕
return data while True:
user_input = raw_input('cmd to send:').strip()
if len(user_input) == 0:continue
s.sendall(user_input) data_size = int(s.recv(1024)) #得到命令执行结果的大小长度,因为发送过来的数据是字符串,所以这里要作类型转换
print '\033[32;1mdata size:\033[0m',data_size #打印命令执行结果的大小
result = data_all(s, data_size) #通过data_all函数来执行相应的数据接收处理策略
print result #打印命令执行结果 s.close() #关闭套接字

PS:

  1.Server端发送数据的“连块”问题,即发送两次数据时,如果发送间隔比较短,socket会把两次发送的数据放在一起来发送,这里通过time.sleep()函数来解决。

  2.socket的缓冲区大小问题,即当执行top -bn 3这样执行结果长度大的命令时,socket缓冲区一次是无法存储这么多数据的,所以只能分多次来接收数据,这样就会在Client端带来一定的问题,比如命令执行的不同步等,解决的方法是通过用循环接收的方法来进行本地存储Server端发送的数据。

  当然,如果要执行man等查询方面的命令,上面的程序也是无法做到的,所以这里说,这只是一个简版的SSH程序

python - socket通信笔记的更多相关文章

  1. Python Socket通信原理

    [Python之旅]第五篇(一):Python Socket通信原理   python Socket 通信理论 socket例子 摘要:  只要和网络服务涉及的,就离不开Socket以及Socket编 ...

  2. Python Socket API 笔记

    将上节中的C#该成Python版的容易程度大大超出了我的意料之外.从来没有发现,仅仅用灰尘简单的几句话就实现了该程序的主要功能,可见python的简易和强大之处.这里先对SocketAPI 做一下总结 ...

  3. python socket编程笔记

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

  4. Python socket 通信功能简介

    常用的地址家族AF_UNIX:基于文件,实现同一主机不同进程之间的通信AF_INET:基于网络,适用于IPv4AF_INET6:基于网络,使用于IPv6 常见的连接类型SOCK_STREAM:即TCP ...

  5. Python Socket通信黏包问题分析及解决方法

    参考:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注:只有在TCP协议通信的 ...

  6. Python Socket通信例子

    一.TCP 通信 服务端 #!/usr/bin/env python # -*- coding: utf-8 -*- # server_tcp.py import socket so = socket ...

  7. Python socket通信之FTP

    Python中利用socket进行server端和client端通信是网络编程的基础,是最简单的传输范例. (懂网络的请自动跳过这一部分) 首先,要想通信,必须建立连接,建立连接的过程,需要clien ...

  8. 树莓派小车By 树莓派爱好者ITJoker(通过python socket通信实现树莓派视频小车)(一)

    本文由树莓派爱好者ITJoker 编辑,转载请注明出处.本人也有新浪博客同样是树莓派爱好者ITJoker 所需材料:树莓派2B或者2B以上,L2985n驱动板,若干排线,电池及电池盒,usb无线网卡( ...

  9. Python Socket 通信

    参考: http://www.cnblogs.com/alex3714/articles/5830365.html Socket A network socket is an endpoint of ...

随机推荐

  1. 吴恩达机器学习103:SVM之大间隔分类器的数学原理

    1.向量内积: (1)假设有u和v这两个二维向量:,接下来看一下u的转置乘以v的结果,u的转置乘以v也叫做向量u和向量v的内积,u是一个二维向量,可以将其在图上画出来,如下图所示向量u: 在横轴上它的 ...

  2. cherrypy

    十多年来,Web 程序设计人员一直使用 CGI 将应用程序连接到 Web 服务器和另一端的 Web 浏览器.有很多理由建议使用 CGI:它可以与任何编程语言一起使用,并且它在 Web 服务器和宿主服务 ...

  3. centos--软件源--本地软件源---离线安装

    一.软件源配置文件 1./etc/yum.conf 配置文件 [main] cachedir=/var/cache/yum #yum下载的RPM包的缓存目录 keepcache= #缓存是否保存,1保 ...

  4. 2019-11-29-dotnet-判断特定进程存在方法

    title author date CreateTime categories dotnet 判断特定进程存在方法 lindexi 2019-11-29 08:34:18 +0800 2019-09- ...

  5. [ERROR] Failed to execute goal org.apache.maven.plugins:maven-install-plugin:2.4:install (default-cli) on project kircp-js-plan-resource: The packaging for this project did not assign a file to the bu

    结合网上的相关资料,要使用Lifecycle下的install 原因好像是Lifecycle下才会走Maven完整的phase.

  6. SpringBootMVC01——A simple SpringBootMVC Sample

    不带数据库的SpringBootMVC案例 1.创建一个SpringBoot项目,添加thymeleaf,webstarter 2.目录层级 3.启动器代码 package com.littlepag ...

  7. JavaScript调试技巧之断点调试

    首先,在各个浏览器中,断点调试支持的最好的当然是Firefox,Firefox不仅可以使用Firebug调试页面js脚本,还可以用高级调试工具例如JavaScript Debugger (Venkma ...

  8. Vue基础第二章

    1.数据绑定与数据声明 Vue中的数据绑定就是让与Vue实例绑定的DOM节点或script标签内的变量之间数据更新互相影响,即数据绑定后Vue实例的数据修改会使DOM节点的数据或者script标签内的 ...

  9. 深入理解JAVA虚拟机 虚拟机执行子系统

    class类文件的结构 java的class类文件中存在两种结构:无符号数和表.最小的存储单元是8个字节. 无符号数是基本的数据类型,用来描述数字,UTF-8编码的字符串,索引引用. 表示多个无符号数 ...

  10. Linux基本命令+Makefile

    1.linux下查看进程占用cpu的情况(top): 格式 top [-] [d delay] [q] [c] [S] [s] [i] [n] 主要参数 d:指定更新的间隔,以秒计算. q:没有任何延 ...