套接字的详细介绍会在另一篇博文指出,此片博文主要是python套接字的简单客户端编写。

两种套接字介绍:

  • 面向连接的套接字:面向连接的套接字提供序列化,可靠的和不重复的数据交付。面向连接是可靠的传输,数据能够完成无误的传输到对方。传输数据时需要先建立连接(TCP的三次握手),然后传输数据。在编写套接字时使用参数 socket.SOCK_STREAM 来指定建立的TCP套接字对象
  • 无连接的套接字:通信开始前不需要建立连接。数据传输过程中无法保持它的顺序性,可靠性和重复性。数据传输前,不用先建立连接。在编写套接字时使用参数 socket.SOCK_DGRAM 来指定建立UDP套接字对象。

TCP  C/S编写:

  服务端编写:(伪代码)

    •   导入socket模块
    • 建立对应的TCP 套接字对象
    • 绑定要监听的主机和端口
    • 开启监听,调用listen函数
    • 开启和客户端的交互

客户端编写: (伪代码)

    •   导入socket模块
    • 建立对应的TCP 套接字对象
    • 调用connect方法建立到server端的连接
    • 进行和服务端的交互

通过实际例子来编写c/s端程序:

# *-* coding:utf-8 *-*
# Auth: wangxz import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("localhost", 5969))
s.listen(5) while True: # 这个循环保证客户端退出后,可以有另一个客户端接入
tcp_server, addr = s.accept()
# accept函数返回的是一个元组,一个是与客户端建立的新的tcp连接,另一个客户端连接的ip和端口
print("The client is %s at %s port " % addr ) #
while True: # 这个循环保证每个客户端与服务持续交互
print("The communication system".center(50, "-"))
data = tcp_server.recv(1024)
if not data: break # 判断如果接收的数据为空,则跳出循环,
# print(data.decode(encoding='ascii'))
print(data)
s.close()

server

# *-* coding:utf-8 *-*
# Auth: wangxz import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 5969))
while True: # 循环是客户端可以持续性输入
msg = raw_input("Please input your msg >>>: ")
# msg = msg.encode()
s.send(msg) #调用send方法发送数据

客户端数据

执行这段代码,可以多开启几个客户单程序,会发现只有第一个客户端可以与服务器通信,其余的客户端都是阻塞状态。当第一个断开连接之后,其余的客户端程序中的一个才会与服务端交互。

模仿写一个ssh命令的请求。

第1.0版本:

# *-* coding:utf-8 *-*
# Auth: wangxz
import socket
import os
import time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("localhost", 12345))
s.listen(5)
while True:
tcp_server, addr = s.accept()
print("The connectios is from %s at %s port" % addr)
while True:
data = tcp_server.recv(1024) # 接收客户端的命令代码,然后执行,如果是空则退出循环
if not data: break
tmp = os.popen(data, "r").read() # 调用popen()函数,执行shell命令,得到返回结果。
if not tmp:
tcp_server.send("\033[32;1m The command has esec succeddfully, but no return!\033[0m")
continue
else:
size = str(len(tmp))
tcp_server.send(size) # 首先发给客户端的是,此次执行cmd命令,返回结果数据的大小
tcp_server.sendall(tmp) # 注意这里
s.close()
# *-* coding:utf-8 *-*
# Auth: wangxz
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost",12345))
while True:
cmd = raw_input("Please input your command >>>: ")
s.send(cmd)
get_size = s.recv(1024) # 得到返回的数据大小,注意这里只接收返回的数据大小,并没有接收命令执行的结果
print("The msg is %s byte" % get_size)

执行结果如下:

Please input your command >>>: cd /etc # 没有返回结果
The msg is The command has esec succeddfully, but no return! byte
Please input your command >>>: w # 本应只得到234字节大小的,但是却返回了命令执行的结果
The msg is 234 22:49:03 up 5:15, 2 users, load average: 0.00, 0.01, 0.05
USER TTY LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 17:34 7.00s 0.10s 0.01s w
root pts/1 22:30 7.00s 0.04s 0.02s python ssh_client.py
byte
Please input your command >>>: df -h
The msg is 382Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 18G 1.3G 17G 8% /
devtmpfs 488M 0 488M 0% /dev
tmpfs 494M 0 494M 0% /dev/shm
tmpfs 494M 6.7M 487M 2% /run
tmpfs 494M 0 494M 0% /sys/fs/cgroup
/dev/sda1 497M 96M 401M 20% /boot
byte
Please input your command >>>:

下面这段解释为引用:

哈哈,这里就引入了一个重要的概念,“粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送到客户端了,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。 但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送了。就是我们上面看到的情况 。

我们在这里必须要想办法把粘包分开, 因为不分开,你就没办法取出来服务器端返回的命令执行结果的大小呀。so ,那怎么分开呢?首先你是没办法让缓冲区强制刷新把数据发给客户端的。 你能做的,只有一个。就是,让缓冲区超时,超时了,系统就不会等缓冲区满了,会直接把数据发走,因为不能一个劲的等后面的数据呀,等太久,会造成数据延迟了,那可是极不好的。so如果让缓冲区超时呢?

解决这个问题,改进之后的服务端代码如下:

  • 第一种:使用time.sleep延迟处理(强烈不推荐)
  • 第二种:服务端调用一个recv方法。由于recv在接收不到数据时是阻塞的,这样就会造成,服务器端接收不到客户端的响应,就不会执行后面的conn.sendall(命令结果)的指令,收到客户端响应后,再发送命令结果时,缓冲区就已经被清空了,因为上一次的数据已经被强制发到客户端了
# *-* coding:utf-8 *-*
# Auth: wangxz
import socket
import os
import time s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("localhost", 12345))
s.listen(5)
while True:
tcp_server, addr = s.accept()
print("The connectios is from %s at %s port" % addr)
while True:
data = tcp_server.recv(1024) # 接收客户端的命令代码,然后执行,如果是空则退出循环
if not data: break
tmp = os.popen(data, "r").read() # 调用popen()函数,执行shell命令,得到返回结果。
if not tmp:
tcp_server.send("\033[32;1m The command has esec succeddfully, but no return!\033[0m")
continue
else:
size = str(len(tmp))
tcp_server.send(size) # 首先发给客户端的是,此次执行cmd命令,返回结果数据的大小
# time.sleep(0.5) # 不推荐使用
tcp_server.recv(1024) # 接收客户端要求传送开始的请求
tcp_server.sendall(tmp) # 注意这里 s.close()

客户端代码如下:

# *-* coding:utf-8 *-*
# Auth: wangxz
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost",12345))
while True:
cmd = raw_input("Please input your command >>>: ")
s.send(cmd)
get_size = s.recv(1024)
print("The msg is %s byte" % get_size)
s.send("Please start transfer the data!") # 请求开始传送数据
data = s.recv(1024)
print(data)

执行结果如下:

[root@mgto7 cs]# python ssh_client.py
Please input your command >>>: w
The msg is 234 byte
23:02:20 up 5:29, 2 users, load average: 0.00, 0.01, 0.05
USER TTY LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 17:34 4.00s 0.09s 0.00s w
root pts/1 22:30 4.00s 0.04s 0.02s python ssh_client.py Please input your command >>>: df -h
The msg is 382 byte
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 18G 1.3G 17G 8% /
devtmpfs 488M 0 488M 0% /dev
tmpfs 494M 0 494M 0% /dev/shm
tmpfs 494M 6.7M 487M 2% /run
tmpfs 494M 0 494M 0% /sys/fs/cgroup
/dev/sda1 497M 96M 401M 20% /boot Please input your command >>>: top -bn 1

一切完美,但是执行top -bn 1命令时,因为传送的数据过于大,因此返回的结果不会一次全部传送完毕,而是会暂存在socket缓冲中,下次执行w(或其余的命令)时,会发现仍然返回的是top -bn 1 命令的结果,一直到缓冲区发送完毕,然后才会有新的结果赶回。

上面的客户端执行没有返回结果的命令是,会返回一个句话,可以自己执行查看!

上面的脚步命令不能执行类似于top这种实时刷新的命令。

先改进数据全部传输的问题:

思路:客户端接受数据时,通过判断接收数据的大小与原来传送的数据大小进行比较,若是两者相等,则表示已经接收完毕,否则继续接收。

服务端代码不变,客户端代码如下:

# *-* coding:utf-8 *-*
# Auth: wangxz
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost",12345))
while True:
cmd = raw_input("Please input your command >>>: ")
s.send(cmd)
get_size = s.recv(1024)
print("The msg is %s byte" % get_size)
s.send("Please start transfer the data!")
received_size = 0
cmd_msg = " "
while received_size < int(get_size): #加入了一个循环
data = s.recv(1024)
received_size += int(len(data))
cmd_msg += data
else:
print(cmd_msg)

ssh_client

这样至少可以处理类似于top -bn 1这样的有很多返回结果的数据。

需要注意:

    在发送命令返回的数据时,我们使用了sendall而不是send,因为发送到也是有数据限制的,这样就相当于多次调用了send。可以先这样理解。

UDP C/S编写:

因为udp是无连接的,因此编写相对简单:

server端:

    •   导入socket模块
    • 建立udp套接字对象
    • 绑定端口
    • 进行交互

client端:

    • 导入socket模块
    • 建立udp套接字对象
    • 进行交互

一个简单的udp 实例如下:

# *-* coding:utf-8 *-*
# Auth: wangxz import socket
import time s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("localhost", 12465)) while True:
print("Waiting for the connect...")
data, addr = s.recvfrom(1024) # 返回两个数据,一个是接收到的数据,另一个是客户端的ip和端口组成的元组
print("\033[35;1m The client is %s at %s port \033[0m" % addr)
s.sendto("[%s] \n ---- %s" % (time.strftime("%y-%m-%d %H:%M:%S", (time.localtime(time.time()))
) , data), addr) # udp使用seldto发送时候,需要指定发送到的地址

server端

# *-* coding:utf-8 *-*
# Auth: wangxz
import socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ADDR = ("localhost", 12465)
while True: # 客户端不用建立连接,直接使用sendto发送即可
data = raw_input("Please input your msg >>> : ")
s.sendto(data, ADDR)
msg, addr = s.recvfrom(1024)
print(msg)

udp client

代码执行:

python网络编程之一的更多相关文章

  1. Python 网络编程(二)

    Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单 ...

  2. Python 网络编程(一)

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

  3. Python学习(22)python网络编程

    Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...

  4. Day07 - Python 网络编程 Socket

    1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...

  5. python网络编程-01

    python网络编程 1.socket模块介绍 ①在网络编程中的一个基本组件就是套接字(socket),socket是两个程序之间的“信息通道”. ②套接字包括两个部分:服务器套接字.客户机套接字 ③ ...

  6. 《Python网络编程》学习笔记--使用谷歌地理编码API获取一个JSON文档

    Foundations of Python Network Programing,Third Edition <python网络编程>,本书中的代码可在Github上搜索fopnp下载 本 ...

  7. Python网络编程基础pdf

    Python网络编程基础(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1VGwGtMSZbE0bSZe-MBl6qA 提取码:mert 复制这段内容后打开百度网盘手 ...

  8. python 网络编程(Socket)

    # from wsgiref.simple_server import make_server## def RunServer(environ,start_response):# start_resp ...

  9. python 网络编程 IO多路复用之epoll

    python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...

  10. 自学Python之路-Python网络编程

    自学Python之路-Python网络编程 自学Python之路[第一回]:1.11.2 1.3

随机推荐

  1. 企业案例(一):由于mysql sleep线程过多小故障

    1.什么是长连接 长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态. 通常的短连接操作步骤是: 连接->数据传输->关闭连接: 而长连接通常就是: 连接-> ...

  2. jQuery生成一个DIV容器,ID是"rating".

    我们需要一些服务器端代码,这个例子中用到了一个PHP文件,读取rating参数然后返回rating总数和平均数.看一下rate.php代码.虽然这些例子也可以不使用AJAX来实现,但显示我们不会那么做 ...

  3. java.exe和javaw.exe有什么区别

  4. Java实现验证码的产生和验证

    大家都知道为了防止我们的网站被有些人和黑客恶意攻击,比如我们网站的注册页面,如果我们在用户注册的时候不加上一个验证码框的话,别人就可以写一个脚本对你的网站进行恶意的注册,比如每分钟对你的网站进行n次的 ...

  5. 51nod 1835 - 完全图 - [dp][组合数公式][快速幂]

    题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1835 基准时间限制:1 秒 空间限制:131072 KB   ...

  6. Linux:file命令显示自定义文件类型

    file 命令可以查看文件类型信息,原理见: 非常Linux-file命令与magic file 修改 /ect/magic 文件后,可用 file 命令显示自定义文件类型信息. man magic ...

  7. Python哈希表的例子:dict、set

    dict(字典) Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. 和list比较,dic ...

  8. iOS多线程编程之GCD的常见用法(转载)

    一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) withObject:nil aft ...

  9. flask中的url_for

    转自:https://blog.csdn.net/dengfan666/article/details/78320188 1.URL反转:根据视图函数名称得到当前所指向的url 2.url_for() ...

  10. WordPress跳过语言包加载提高效率

    WordPress 加载语言包是需要花费 0.1-0.5 秒不等的时间,所以如果 WordPress 前台可以不加载语言包,而主题中的一些文本直接写成中文,就可以加快网站的速度,并且又能保证后台的中文 ...