博客核心内容:


1.Socket是什么
2.基于TCP协议的网络编程模型(进阶代码)
3.基于TCP协议的网络编程带来的两个问题以及相应的解决措施(通信循环和链接循环)
4.socket.error: [Errno 98] Address already in use(IP地址和端口号已经被占用的解决措施)
5.基于socket实现远程执行命令
6.基于TCP网络编程出现的粘包以及相应的解决方案
7.基于Socket网络编程之客户端并发实现(利用了多进程的技术)
8.进程池中同步与异步的解决方案


(一)Socket是什么

1、C/S架构与socket的关系:我们学习socket就是为了完成C/S架构的开发
2、C/S架构的软件(软件属于应用层)是基于网络进行通信的,网络的核心即一堆协议,协议即标准,你想开发一款基于网络通信的软件,就必须遵循这些标准
3、互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层:如下图所示

4、Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议,所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

5、也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序而程序的pid是同一台机器上不同进程或者线程的标识。

TCP服务端的编程模型:

(二)基于TCP协议的网络编程
ss = socket() #创建服务器套接字
ss.bind()      #把地址绑定到套接字
ss.listen()      #监听链接
inf_loop:      #服务器无限循环cs = ss.accept() #接受客户端链接comm_loop:         #通讯循环cs.recv()/cs.send() #对话(接收与发送)cs.close()    #关闭客户端套接字
ss.close()        #关闭服务器套接字(可选)

TCP客户端的编程模型:

cs = socket()    # 创建客户套接字
cs.connect()    # 尝试连接服务器
comm_loop:        # 通讯循环cs.send()/cs.recv()    # 对话(发送/接收)
cs.close()            # 关闭客户套接字

相关注意事项:

套接字家族的名字:AF_INET,指定我们的套接字是基于网络进行通信的。socket.SOCK_STREAM指的是TCP协议,即流式协议
socket.SOCK_DGRAM指的是UDP协议,即数据报协议phone.listen(5)的含义:TCP协议当中含有一个半链接池,5指的是半链接池的数量,而不是
指的并发的数量,相当于可以同时将5个链接给挂起来。我觉得使用连接池最大的一个好处就是减少连接的创建和关闭,增加系统负载能力.我认为在这里面可以画一个编程模型图(略)conn,addr = phone.accept()conn指的是TCP协议3次握手之后建立的那个链接,
addr指的是客户端的IP地址和端口号conn.read(1024)基于TCP协议接收到的消息是:1024表示的是字节,即1kbphone.send("hello") 在发消息的过程中,不能直接发送字符串,而应该发送二进制串,因为数据的传输
都应该是二进制形式

版本一:简易版本
服务端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(("127.0.0.1",8081))phone.listen(5)
print("starting")conn,addr = phone.accept()
print(conn)
print(addr)client_msg = conn.recv(1024)
print("客户端发送过来的消息是:%s"%client_msg)
conn.send(client_msg.upper())conn.close()
phone.close()

客户端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
#指定socket套接字通信所用的协议:TCP协议
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#随后客户端的套接字发送链接请求
phone.connect(('127.0.0.1',8081))#随后客户端向服务端发送消息phone.send("hello".encode("utf-8"))server_msg = phone.recv(1024)
print("服务端发送过来的消息是:%s"%server_msg)

服务端的运行结果:

starting
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8081), raddr=('127.0.0.1', 52596)>
('127.0.0.1', 52596)
客户端发送过来的消息是:b'hello'Process finished with exit code 0

客户端的运行结果:

服务端发送过来的消息是:b'HELLO'Process finished with exit code 0

我自己总结的编程模型:
服务端:

服务器端编程模型:
1.创建socket套接字对象,并指定通信所用的协议
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)2.将套接字对象绑定服务器端的IP地址和相应端口号
socket_obj.bind(("127.0.0.1",8080))3.套接字对象监听这个端口,并指定半链接池的数量
socket_obj.listen(5)4.服务器端接收客户端的链接请求
conn,addr = socket_obj.accept()5.双方发送消息6.关闭连接
close()

客户端:

客户端编程模型:
1.创建socket套接字对象,并指定通信所用的协议
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)2.随后客户端的套接字对象发送链接请求
socket_obj.connect(("127.0.0.1",8080))3.双方发送消息4.关闭连接
close()

图示:

在上面的基础上我们将代码进行改进:(2次改进)
服务端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket#1、创建一个socket套接字对象,同时需要指定网络通信所用的协议,在这里用的是TCP协议
socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#2、socket套接字绑定IP地址和端口号
socket_server.bind(("127.0.0.1",8083))#3、通过socket这个监听器对8080端口号进行监听状态
socket_server.listen(5)#4、服务器端接收套接字发送过来的请求
print("等待建立连接中........")
conn ,address = socket_server.accept()
print("建立连接")
print("客户端的地址是:",address)
"""
从下面开始双方将会基于这个链接进行相互通信,下面应该建立一个循环
"""
while True:print("服务端在等待客户端发送消息..........")cli_msg = conn.recv(1024).decode("utf-8")print("客户端发送过来的数据是:%s"%cli_msg)#要想获得最终的结果:需要将二进制串解码成字符串conn.send("我已经收到消息了!".encode("utf-8"))conn.close()
socket_server.close()

客户端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket#1、创建一个socket套接字对象,同时需要指定网络通信所用的协议,在这里用的是TCP协议
socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#2、通过socket这个套接字向服务端发送链接请求
socket_client.connect(("127.0.0.1",8083))"""
从下面开始双方将会基于这个链接进行相互通信
"""
while True:msg = input(">>:").strip()socket_client.send(msg.encode("utf-8"))server_msg = socket_client.recv(1024).decode("utf-8")print("服务端发送过来的数据是:%s"%server_msg)socket_client.close()

最终运行结果:
服务端的结果:

等待建立连接中........
建立连接
客户端的地址是: ('127.0.0.1', 55745)
服务端在等待客户端发送消息..........
客户端发送过来的数据是:spark
服务端在等待客户端发送消息..........
客户端发送过来的数据是:fjdsflkd
服务端在等待客户端发送消息..........
客户端发送过来的数据是:fds
服务端在等待客户端发送消息..........
客户端发送过来的数据是:fdsfdsfdsfdsfsfdsfds
服务端在等待客户端发送消息..........
客户端发送过来的数据是:fds
服务端在等待客户端发送消息..........
客户端发送过来的数据是:zhangmingyang
服务端在等待客户端发送消息..........

客户端的结果:

>>:spark
服务端发送过来的数据是:我已经收到消息了!
>>:fjdsflkd
服务端发送过来的数据是:我已经收到消息了!
>>:fds
服务端发送过来的数据是:我已经收到消息了!
>>:fdsfdsfdsfdsfsfdsfds
服务端发送过来的数据是:我已经收到消息了!
>>:fds
服务端发送过来的数据是:我已经收到消息了!
>>:zhangmingyang
服务端发送过来的数据是:我已经收到消息了!
>>:

综上:我们可以总结出一个result:
服务端在两个地方会发生卡的情况:
1、客户端没有发送链接请求,或者说等待客户端发送链接请求的时候会卡
conn ,address = socket_server.accept()
2、服务端等待客户端发送消息的时候会卡:
cli_msg = conn.recv(1024).decode(“utf-8”)

基于TCP协议的问题:
1、客户端不能发送空的数据,否则在服务端就会卡
cli_msg = conn.recv(1024).decode(“utf-8”)
解决方法:
在客户端通过语句级别进行控制:
msg = input(“>>:”).strip()
#如果msg消息为空,则不进行消息的发送
if not msg:continue

(三)基于TCP协议的网络编程带来的两个问题以及相应的解决措施

2、**TCP协议是双向的连接:在客户端强制终止的时候,服务端会报错:远程主机强迫关闭了一个现有的连接
服务端在等待客户端发送消息……….**
Traceback (most recent call last):
File “D:/Python Work Location/Python 0507/day08/directory2/服务端.py”, line 25, in
cli_msg = conn.recv(1024).decode(“utf-8”)
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
呵呵,客户异常的终止了自己的客户端,服务端就down了,你感觉合适吗?

解决方法:异常处理机制(将可能出现的代码块进行捕获)

while True:
try:
print(“服务端在等待客户端发送消息……….”)
cli_msg = conn.recv(1024).decode(“utf-8”)
print(“客户端发送过来的数据是:%s”%cli_msg)
#要想获得最终的结果:需要将二进制串解码成字符串
conn.send(“我已经收到消息了!”.encode(“utf-8”))
except ConnectionResetError:
break

又一个问题:此时服务端不会异常终止掉,但是会终止掉。
解决方法:while True:链接循环不能终止, 仅仅终止通信循环[呵呵,其实我感觉下面那个应该叫做
链接循环]

注意:在linux当中,如果客户端单方面的终止,服务端并不会终止跑出异常,而是在服务端里面一直死循环.
改正之后的客户端代码:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket#1、创建一个socket套接字对象,同时需要指定网络通信所用的协议,在这里用的是TCP协议
socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#2、通过socket这个套接字向服务端发送链接请求
socket_client.connect(("172.22.178.52",8083))"""
从下面开始双方将会基于这个链接进行相互通信
"""
while True:msg = input(">>:").strip()#如果msg消息为空,则不进行消息的发送if not msg:continuesocket_client.send(msg.encode("utf-8"))print("消息已经发送!")server_msg = socket_client.recv(1024).decode("utf-8")print("服务端发送过来的数据是:%s"%server_msg)socket_client.close()

改正之后的服务端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket#1、创建一个socket套接字对象,同时需要指定网络通信所用的协议,在这里用的是TCP协议
socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#2、socket套接字绑定IP地址和端口号
socket_server.bind(("172.22.178.52",8083))#3、通过socket这个监听器对8080端口号进行监听状态
socket_server.listen(5)#4、服务器端接收套接字发送过来的请求
while True:print("等待建立连接中........")conn ,address = socket_server.accept()print("建立连接")print("客户端的地址是:",address)"""从下面开始双方将会基于这个链接进行相互通信,下面应该建立一个循环"""while True:try:#针对windows系统客户端断开链接print("服务端在等待客户端发送消息..........")cli_msg = conn.recv(1024).decode("utf-8")#考虑Linux的情况系统客户端断开链接if not cli_msg: breakprint("客户端发送过来的数据是:%s"%cli_msg)#要想获得最终的结果:需要将二进制串解码成字符串conn.send("我已经收到消息了!".encode("utf-8"))except ConnectionResetError:break#若客户端终止链接,此时服务端也应该终止链接,但是不应该终止监听状态conn.close()socket_server.close()

有的时候当我们在重启服务端时可能会遇到:

(四)socket.error: [Errno 98] Address already in use
Traceback (most recent call last):File "服务端.py", line 9, in <module>socket_server.bind(("172.22.178.52",6819))
socket.error: [Errno 98] Address already in use

这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
解决方法:
加入一条socket配置,重用ip和端口

socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
socket_server.bind(("172.22.178.52",6819))

ssh的原理:

(五)基于socket实现远程执行命令
ssh:可以远程操作对方的主机.(ssh本质上是基于socket编写的一个软件)
xshell,是ssh的一个客户端;服务器上会安装一个ssh的服务端.
原理:在客户端执行一条命令之后,这条命令并不会在客户端本地被执行,而是会被当做一条字符串发送给
服务端,在服务端被执行,执行完之后在将结果通过socket套接字发送回来,随后客户端在自己的终端在显示
出来,当然给人的一种错觉是这条命令是在客户端本地被执行的。即在服务端执行命令,并将结果返回给客户端。

代码示例:
服务端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
import subprocess#创建socket套接字,并指定通信所用的协议
socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#重新使用IP地址和端口号
socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
socket_server.bind(("127.0.0.1",8081))
socket_server.listen(5)while True:print("等待客户端的连接.........")conn,address = socket_server.accept()print("链接已经生成")print("客户端的信息是:",address)"""双方建立了连接后,就开始进行相同通信"""while True:try:client_msg = conn.recv(1024).decode("utf-8")print("客户端发送的消息是:%s" %client_msg)res = subprocess.Popen(client_msg,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)res_err = res.stderr.read()if res_err:cmd_res = res_errelse:cmd_res = res.stdout.read()conn.send(cmd_res)except Exception:breakconn.close()socket_server.close()

客户端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket#创建socket套接字,并指定通信所用的协议
socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#客户端向服务端发送链接请求
socket_client.connect(("127.0.0.1",8081))"""
双方建立了连接后,就开始进行相同通信
"""
while True:str_msg = input(">>")if not str_msg: continuesocket_client.send(str_msg.encode('utf-8'))print("消息已经发送出去!")ser_msg = socket_client.recv(1024).decode("gbk")print("服务端发送过来的消息是:%s"%ser_msg)socket_client.close()

服务端的执行结果:

等待客户端的连接.........
链接已经生成
客户端的信息是: ('127.0.0.1', 51680)
客户端发送的消息是:ls
客户端发送的消息是:dir
客户端发送的消息是:ipconfig

客户端的执行结果:

D:\Python34\python.exe "D:/Python Work Location/Python 0507/day08/directory4/客户端.py"
>>ls
消息已经发送出去!
服务端发送过来的消息是:'ls' 不是内部或外部命令,也不是可运行的程序
或批处理文件。>>dir
消息已经发送出去!
服务端发送过来的消息是: 驱动器 D 中的卷是 NewDisk卷的序列号是 B823-EC5FD:\Python Work Location\Python 0507\day08\directory4 的目录2017/06/23  02:41    <DIR>          .
2017/06/23  02:41    <DIR>          ..
2017/06/23  02:41               637 客户端.py
2017/06/23  02:39             1,252 服务端.py2 个文件          1,889 字节2 个目录 387,609,653,248 可用字节>>ipconfig
消息已经发送出去!
服务端发送过来的消息是:
Windows IP 配置无线局域网适配器 无线网络连接 4:媒体状态  . . . . . . . . . . . . : 媒体已断开连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 无线网络连接 3:媒体状态  . . . . . . . . . . . . : 媒体已断开连接特定的 DNS 后缀 . . . . . . . : 以太网适配器 本地连接 2:连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::bcc1:4f1e:cf40:b7e8%24IPv4 地址 . . . . . . . . . . . . : 10.255.48.51子网掩码  . . . . . . . . . . . . : 255.255.255.0默认网关. . . . . . . . . . . . . : 以太网适配器 Bluetooth 网络连接 2:媒体状态  . . . . . . . . . . . . : 媒体已断开连接特定的 DNS 后缀 . . . . . . . : 无线局域网适配器 无线网络连接:连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::f8fc:fab4:f423:7ad3%15IPv4 地址 . . . . . . . . . . . . : 192.168.1.13子网掩码  . . . . . . . . . . . . : 255.255.255.0默认网关. . . . . . . . . . . . . : fe80::1%15

自定义报头解决粘包问题:

(七)粘包以及解决措施

1、如果命令的执行结果小于1024个字节,就会接着收结果,为上一次遗留的结果。
2、我们基于socket这种机制写的是应用程序
3、内核态与用户态的概念:CPU的两种工作状态:
如果是内核态:操作系统操纵硬件
如果是用户态:操作应用程序,用户态不能操作硬件,但是此时应用程序会发起一个系统调用,
去操作我们的网卡
4、客户端的应用程序不断将结果丢到操作系统的缓存当中
5、服务端与客户端的状态是一发一收?这种说法是错误的,客户端一发,难道服务端就一定要
一收吗?
正确答案:客户端发和服务端收,两者之间没有一点关系,双方操作的都是各自的缓存。

代码示例:非一收一发的情况:
总结:在socket编程当中,无论是收发,只与自己的缓存有关系,跟对方没有关系.

粘包的两种情况:
1、在客户端发送的时候产生粘包[客户端发送的数据包数据量比较小的时候,并且相互之间时间间隔
比较短的时候===>此时操作系统还没有来得及将缓存中的数据给发送出去==>导致这几条数据基本上同时到达对方的缓存当中,由于对方收的是1kb的数据,所以将数据一下子都获取出来]
问题:为什么没有来得及?

2、问题:程序运行的速度快还是网络运行(网络延迟)的速度快?
基于网络通信的软件:最大的瓶颈在于网络IO

总结:无论是在客户端还是服务端,粘包现象都和TCP/IP工作的方式有关(流失协议)

UDP不会粘包的原因:数据报协议,没发一条消息都是单独的一条数据报。

原因:
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。(即不知道数据流的开始
与结束)

解决方法:
如果我们知道每个数据报的长度的话,那我们就可以针对性的收了

也就是无论我们是在客户端粘包还是在服务端粘包,本质的原因就是因为我们不知道从哪里收,即不知道数据的
长度是多少。
示例程序1:客户端

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
import time
import structsocket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#客户端的套接字发送链接请求
socket_client.connect(("127.0.0.1",8083))"""
双方建立好链接请求之后,进行相同通信
"""
msg = "zhang贸发大水范德萨范德萨zhangdir"
print(len(msg))
print(msg)
len_msg = struct.pack('i',len(msg.encode("utf-8")))
socket_client.send(len_msg)
socket_client.send(msg.encode("utf-8"))socket_client.close()

服务端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
import time
import structsocket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_server.bind(("127.0.0.1",8083))
socket_server.listen(5)#等待客户端的socket发送链接请求,并建立连接
print("等待链接中......")
conn,addresss = socket_server.accept()
print("链接已生成......")
print("客户端的地址是:",addresss)"""
双方建立好链接请求之后,进行相同通信
"""msg_len = struct.unpack('i',conn.recv(4))
print(msg_len[0])msg = conn.recv(msg_len[0])
print(msg.decode('utf-8'))conn.close()
socket_server.close()

运行结果:
客户端:

23
zhang贸发大水范德萨范德萨zhangdir

服务端:

等待链接中......
链接已生成......
客户端的地址是: ('127.0.0.1', 50107)
43
zhang贸发大水范德萨范德萨zhangdirProcess finished with exit code 0

示例程序:远程执行命令的解决方案
客户端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
import struct
import json#创建socket套接字,并指定通信所用的协议
socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#客户端向服务端发送链接请求
socket_client.connect(("127.0.0.1",8081))"""
双方建立了连接后,就开始进行相同通信
"""
while True:str_msg = input(">>")if not str_msg: continuesocket_client.send(str_msg.encode('utf-8'))print("消息已经发送出去!")#我们在客户端就先收取4个字节的长度,并进行解包,看看服务端发送过来的数据到底多长total_size = struct.unpack('i',socket_client.recv(4))[0]#下面这样写是不合适的,我们应该一点一点的拿,通过for循环#ser_msg = socket_client.recv(ser_len[0])recv_size = 0data = b''while recv_size < total_size:#socket_client.recv(1024)虽然写的是收取1024个字节,但是收取的是实际的字节recv_data = socket_client.recv(1024)data += recv_data#不能写1024,写真实接收的数据recv_size += len(recv_data)print("服务端发送过来的消息是:%s"%data.decode('gbk'))socket_client.close()

服务端:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
import subprocess
import struct
import json#创建socket套接字,并指定通信所用的协议
socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#重新使用IP地址和端口号
socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
socket_server.bind(("127.0.0.1",8081))
socket_server.listen(5)while True:print("等待客户端的连接.........")conn,address = socket_server.accept()print("链接已经生成")print("客户端的信息是:",address)"""双方建立了连接后,就开始进行相同通信"""while True:try:client_msg = conn.recv(1024).decode("utf-8")if not client_msg:breakprint("客户端发送的消息是:%s" %client_msg)res = subprocess.Popen(client_msg,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)#获取的结果是平台格式的二进制串res_err = res.stderr.read()if res_err:cmd_res = res_errelse:cmd_res = res.stdout.read()#服务端在真正发送数据的时候,应该先发送一下数据的长度aa = struct.pack('i', len(cmd_res))#这两条数据肯定会黏在一起的#先发送报头的长度conn.send(aa)#在发送真实的数据conn.send(cmd_res)except Exception:breakconn.close()socket_server.close()

服务端程序:

(七)基于Socket网络编程之客户端并发实现(多进程)
#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
from multiprocessing import Process
import time#1、创建一个socket套接字对象,同时需要指定网络通信所用的协议,在这里用的是TCP协议
socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#2、socket套接字绑定IP地址和端口号
socket_server.bind(("127.0.0.1",8081))#3、通过socket这个监听器对8080端口号进行监听状态
socket_server.listen(5)#4、服务器端接收套接字发送过来的请求
def talk(conn):#通信循环while True:try:#针对windows系统客户端断开链接print("服务端在等待客户端发送消息..........")cli_msg = conn.recv(1024).decode("utf-8")#考虑Linux的情况系统客户端断开链接if not cli_msg: breakprint("客户端发送过来的数据是:%s"%cli_msg)#要想获得最终的结果:需要将二进制串解码成字符串conn.send("我已经收到消息了!".encode("utf-8"))except ConnectionResetError:break# 若客户端终止链接,此时服务端也应该终止链接,但是不应该终止监听状态conn.close()if __name__ == '__main__':while True:#链接循环print("等待建立连接中........")conn ,address = socket_server.accept()print("建立连接")print("客户端的地址是:",address)"""从下面开始双方将会基于这个链接进行相互通信,下面应该建立一个循环"""p = Process(target=talk,args=(conn,),name=str(address))print("进程的名称是:%s"%p.name)p.start()time.sleep(15)"""在这里我有一个疑问,为什么不是写多个?"""socket_server.close()

客户端程序:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socketsocket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_client.connect(("127.0.0.1",8081))"""
双方建立连接后进行通信
"""while True:msg = input(">>:").strip()if not msg: continuesocket_client.send(msg.encode("utf-8"))server_msg = socket_client.recv(1024).decode("utf-8")print("服务端发送过来的数据是:%s"%server_msg)socket_client.close()

另外一个客户端代码是相同的。
运行结果:

等待建立连接中........
建立连接
客户端的地址是: ('127.0.0.1', 61406)
进程的名称是:('127.0.0.1', 61406)
服务端在等待客户端发送消息..........
等待建立连接中........
建立连接
客户端的地址是: ('127.0.0.1', 61410)
进程的名称是:('127.0.0.1', 61410)
服务端在等待客户端发送消息..........
等待建立连接中........

注意:在这个程序当中利用了主进程和子进程两个进程进而实现并发的效果!

服务端同步的执行代码:

(八)进程池中同步与异步的执行方案
#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
from multiprocessing import Process,Pool
import timesocket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
socket_server.bind(("127.0.0.1",8081))
socket_server.listen(5)def talk(conn,addr):while True:try:msg = conn.recv(1024)if not msg:breakprint("客户端发送过来的数据是:%s" % msg)conn.send(msg.upper())except Exception:breakif __name__ == "__main__":pool = Pool(processes=4)while True:# 链接循环print("等待建立连接中........")conn, address = socket_server.accept()print("建立连接")print("客户端的地址是:", address)#建立完连接之后向进程池中提交任务pool.apply(func=talk,args=(conn,address,))#问题:只有当第一个提交的任务执行完毕之后,进程池中才会建立连接,接受第二个任务,并进行执行

问题:
在进程池中同步(打电话)的提交任务,若提交的前一个任务没有执行完,后一个任务则不能执行.
此时进程池中的任务将变为串行的效果,当然这种结果并不是我们想要的。

异步的解决方案(服务端):
代码示例:

#!/usr/bin/python
# -*- coding:utf-8 -*-import socket
from multiprocessing import Process,Pool
import time
import ossocket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
socket_server.bind(("127.0.0.1",8081))
socket_server.listen(5)def talk(conn,addr):print("当前执行的进程PID是\033[41;1m%s\033[0m"%os.getpid())while True:try:msg = conn.recv(1024)if not msg:breakprint("客户端发送过来的数据是:%s" % msg.decode("utf-8"))conn.send(msg.upper())except Exception:breakif __name__ == "__main__":pool = Pool(processes=2)res_l = []while True:# 链接循环print("等待建立连接中........")conn, address = socket_server.accept()print("建立连接")print("客户端的地址是:", address)#建立完连接之后向进程池中提交任务res = pool.apply_async(func=talk,args=(conn,address))#通过程序可以知道:res_l中仅仅添加了AsyncResult对象,但是并没有得到执行res_l.append(res)print(res_l)

运行结果:

D:\Python34\python.exe "D:/Python Work Location/Python 0507/day10/服务端进程池.py"
等待建立连接中........
建立连接
客户端的地址是: ('127.0.0.1', 57599)
[<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>]
等待建立连接中........
当前执行的进程PID是7564
建立连接
客户端的地址是: ('127.0.0.1', 57603)
[<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>]
等待建立连接中........
当前执行的进程PID是13832
建立连接
客户端的地址是: ('127.0.0.1', 57604)
[<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>, <multiprocessing.pool.ApplyResult object at 0x0000000003077550>]
等待建立连接中........
建立连接
客户端的地址是: ('127.0.0.1', 57608)
[<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>, <multiprocessing.pool.ApplyResult object at 0x0000000003077550>, <multiprocessing.pool.ApplyResult object at 0x00000000030874A8>]
等待建立连接中........
建立连接
客户端的地址是: ('127.0.0.1', 57612)
[<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>, <multiprocessing.pool.ApplyResult object at 0x0000000003077550>, <multiprocessing.pool.ApplyResult object at 0x00000000030874A8>, <multiprocessing.pool.ApplyResult object at 0x0000000003087550>]
等待建立连接中........
客户端发送过来的数据是:dfdsf
客户端发送过来的数据是:gdsgs
当前执行的进程PID是7564
客户端发送过来的数据是:gdsgdgg
当前执行的进程PID是13832
当前执行的进程PID是7564

从上面的程序可以看出:
进程池当中始终共享着开始创建的那num个进程,减小了创建进程的开销.

基于Socket网络编程相关推荐

  1. 基于socket网络编程技术实现TCP和UDP的流程详解及实例

    具体函数讲解太多,根据程序自行分析. 可以参考这篇文章: https://blog.csdn.net/qq_41687938/article/details/119102328?spm=1001.20 ...

  2. socket网络编程 java_Java Web 基础(一) 基于TCP的Socket网络编程

    一.Socket简单介绍 Socket通信作为Java网络通讯的基础内容,集中了异常.I/O流模式等众多知识点.学习Socket通信,既能够了解真正的网络通讯原理,也能够增强对I/O流模式的理解. 1 ...

  3. 基于Linux的socket网络编程项目——游侠手机商城

    基于Linux的socket网络编程项目--游侠手机商城 一.项目说明 二.项目使用的技术 三.客户端搭建 四.服务器端搭建 一.项目说明 本项目是一个仿真手机商城类系统,基本功能: 登录界面功能:用 ...

  4. Linux C++/Java/Web/OC Socket网络编程

    一,Linux C++ Socket网络编程 1.什么是TCP/IP.UDP? TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制 ...

  5. 【网络编程】之四、socket网络编程例解

    前面说了那么多,现在我们给出4个代码+详解,基于win32平台的socket编程.使用TCP 和 UDP 两种协议. OK,先来看一下TCP服务器和客户端: [cpp] view plaincopy ...

  6. Socket网络编程(TCP/IP/端口/类)和实例

    原文:C# Socket网络编程精华篇 转自:微冷的雨 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次模型 当然这里我们只讨论重要的四层 01,应用层(Ap ...

  7. Day09: socket网络编程-OSI七层协议,tcp/udp套接字,tcp粘包问题,socketserver

    今日内容:socket网络编程     1.OSI七层协议     2.基于tcp协议的套接字通信     3.模拟ssh远程执行命令     4.tcp的粘包问题及解决方案     5.基于udp协 ...

  8. Python面向对象进阶和socket网络编程

    写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese:def __init__(self, ...

  9. BIO,Socket网络编程入门代码示例,NIO网络编程入门代码示例,AIO 网络编程

    BIO,Socket网络编程入门代码示例 1.BIO服务器端程序 package cn.itcast.bio;import java.io.InputStream; import java.io.Ou ...

  10. python网络编程讲解_详解Python Socket网络编程

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...

最新文章

  1. Windows 11 首个预览版发布,最低配置要求或降低!
  2. java分布式api网管关,分布式04-Spring Cloud Zuul Api网关 一
  3. String类的使用 Part2
  4. 台式电脑怎么看计算机型号,怎么查看台式机的型号
  5. C#LeetCode刷题-几何
  6. 常用的、现成的!正则表达式及基础知识
  7. linux界面更改用户名,linux跳过用户名密码登陆界面方法
  8. 正则的简单学习与应用
  9. javaweb网上订餐系统源码_【有源码】毕设网上订餐系统
  10. Win32汇编基础1
  11. autowired注解_Spring系列之Spring常用注解总结
  12. java list加入listview_将卡添加到ListView
  13. 【图像去噪】基于马尔可夫随机场实现图像去噪附matlab代码
  14. 【Linux】输出当前工作路径
  15. ssh链接报错Server responded “Algorithm negotiation failed”
  16. 百度网盘怎么取消自动续费
  17. VBA基础函数:取数组最大下标——UBound函数
  18. 国内十大活跃报表 BI 产品深度点评
  19. 如何简单理解贝叶斯决策理论(Bayes Decision Theory)
  20. 细胞生物学-1-绪论

热门文章

  1. QTP10.0安装所遇问题-脚本调试器问题
  2. 《CSS权威指南》第3版
  3. Java集合框架和重要接口、类
  4. linux project 软件下载,项目计划管理画甘特图工具Ganttproject安装(for linux)
  5. 第四章 数学规划模型
  6. linux dump 分析工具,使用 Crash 工具分析 Linux dump 文件
  7. R语言maps包绘制世界地图并存为矢量图 超基础!
  8. Java 加密、解密PDF文档
  9. 超详细大学生申请软件著作权登记模板及教程
  10. 常用开发工具、网站、文章、博客、官网、资源、牛人等推荐(持续更新)