版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a2011480169/article/details/73602708

博客核心内容:


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协议的网络编程
  1. ss = socket() #创建服务器套接字
  2. ss.bind() #把地址绑定到套接字
  3. ss.listen() #监听链接
  4. inf_loop: #服务器无限循环
  5. cs = ss.accept() #接受客户端链接
  6. comm_loop: #通讯循环
  7. cs.recv()/cs.send() #对话(接收与发送)
  8. cs.close() #关闭客户端套接字
  9. ss.close() #关闭服务器套接字(可选)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

TCP客户端的编程模型:

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

相关注意事项:

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

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

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  5. phone.bind(("127.0.0.1",8081))
  6. phone.listen(5)
  7. print("starting")
  8. conn,addr = phone.accept()
  9. print(conn)
  10. print(addr)
  11. client_msg = conn.recv(1024)
  12. print("客户端发送过来的消息是:%s"%client_msg)
  13. conn.send(client_msg.upper())
  14. conn.close()
  15. phone.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

客户端:

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

服务端的运行结果:

  1. starting
  2. <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)>
  3. ('127.0.0.1', 52596)
  4. 客户端发送过来的消息是:b'hello'
  5. Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

客户端的运行结果:

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

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

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

客户端:

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

图示:

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

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

客户端:

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

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

  1. 等待建立连接中........
  2. 建立连接
  3. 客户端的地址是: ('127.0.0.1', 55745)
  4. 服务端在等待客户端发送消息..........
  5. 客户端发送过来的数据是:spark
  6. 服务端在等待客户端发送消息..........
  7. 客户端发送过来的数据是:fjdsflkd
  8. 服务端在等待客户端发送消息..........
  9. 客户端发送过来的数据是:fds
  10. 服务端在等待客户端发送消息..........
  11. 客户端发送过来的数据是:fdsfdsfdsfdsfsfdsfds
  12. 服务端在等待客户端发送消息..........
  13. 客户端发送过来的数据是:fds
  14. 服务端在等待客户端发送消息..........
  15. 客户端发送过来的数据是:zhangmingyang
  16. 服务端在等待客户端发送消息..........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

客户端的结果:

  1. >>:spark
  2. 服务端发送过来的数据是:我已经收到消息了!
  3. >>:fjdsflkd
  4. 服务端发送过来的数据是:我已经收到消息了!
  5. >>:fds
  6. 服务端发送过来的数据是:我已经收到消息了!
  7. >>:fdsfdsfdsfdsfsfdsfds
  8. 服务端发送过来的数据是:我已经收到消息了!
  9. >>:fds
  10. 服务端发送过来的数据是:我已经收到消息了!
  11. >>:zhangmingyang
  12. 服务端发送过来的数据是:我已经收到消息了!
  13. >>:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

综上:我们可以总结出一个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当中,如果客户端单方面的终止,服务端并不会终止跑出异常,而是在服务端里面一直死循环.
改正之后的客户端代码:

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

改正之后的服务端:

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

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

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

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

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

ssh的原理:

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

代码示例:
服务端:

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. import subprocess
  5. #创建socket套接字,并指定通信所用的协议
  6. socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  7. #重新使用IP地址和端口号
  8. socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  9. socket_server.bind(("127.0.0.1",8081))
  10. socket_server.listen(5)
  11. while True:
  12. print("等待客户端的连接.........")
  13. conn,address = socket_server.accept()
  14. print("链接已经生成")
  15. print("客户端的信息是:",address)
  16. """
  17. 双方建立了连接后,就开始进行相同通信
  18. """
  19. while True:
  20. try:
  21. client_msg = conn.recv(1024).decode("utf-8")
  22. print("客户端发送的消息是:%s" %client_msg)
  23. res = subprocess.Popen(client_msg,shell=True,
  24. stdout=subprocess.PIPE,
  25. stderr=subprocess.PIPE)
  26. res_err = res.stderr.read()
  27. if res_err:
  28. cmd_res = res_err
  29. else:
  30. cmd_res = res.stdout.read()
  31. conn.send(cmd_res)
  32. except Exception:
  33. break
  34. conn.close()
  35. socket_server.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

客户端:

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. #创建socket套接字,并指定通信所用的协议
  5. socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  6. #客户端向服务端发送链接请求
  7. socket_client.connect(("127.0.0.1",8081))
  8. """
  9. 双方建立了连接后,就开始进行相同通信
  10. """
  11. while True:
  12. str_msg = input(">>")
  13. if not str_msg: continue
  14. socket_client.send(str_msg.encode('utf-8'))
  15. print("消息已经发送出去!")
  16. ser_msg = socket_client.recv(1024).decode("gbk")
  17. print("服务端发送过来的消息是:%s"%ser_msg)
  18. socket_client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

服务端的执行结果:

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

客户端的执行结果:

  1. D:\Python34\python.exe "D:/Python Work Location/Python 0507/day08/directory4/客户端.py"
  2. >>ls
  3. 消息已经发送出去!
  4. 服务端发送过来的消息是:'ls' 不是内部或外部命令,也不是可运行的程序
  5. 或批处理文件。
  6. >>dir
  7. 消息已经发送出去!
  8. 服务端发送过来的消息是: 驱动器 D 中的卷是 NewDisk
  9. 卷的序列号是 B823-EC5F
  10. D:\Python Work Location\Python 0507\day08\directory4 的目录
  11. 2017/06/23 02:41 <DIR> .
  12. 2017/06/23 02:41 <DIR> ..
  13. 2017/06/23 02:41 637 客户端.py
  14. 2017/06/23 02:39 1,252 服务端.py
  15. 2 个文件 1,889 字节
  16. 2 个目录 387,609,653,248 可用字节
  17. >>ipconfig
  18. 消息已经发送出去!
  19. 服务端发送过来的消息是:
  20. Windows IP 配置
  21. 无线局域网适配器 无线网络连接 4:
  22. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  23. 连接特定的 DNS 后缀 . . . . . . . :
  24. 无线局域网适配器 无线网络连接 3:
  25. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  26. 连接特定的 DNS 后缀 . . . . . . . :
  27. 以太网适配器 本地连接 2:
  28. 连接特定的 DNS 后缀 . . . . . . . :
  29. 本地链接 IPv6 地址. . . . . . . . : fe80::bcc1:4f1e:cf40:b7e8%24
  30. IPv4 地址 . . . . . . . . . . . . : 10.255.48.51
  31. 子网掩码 . . . . . . . . . . . . : 255.255.255.0
  32. 默认网关. . . . . . . . . . . . . :
  33. 以太网适配器 Bluetooth 网络连接 2:
  34. 媒体状态 . . . . . . . . . . . . : 媒体已断开
  35. 连接特定的 DNS 后缀 . . . . . . . :
  36. 无线局域网适配器 无线网络连接:
  37. 连接特定的 DNS 后缀 . . . . . . . :
  38. 本地链接 IPv6 地址. . . . . . . . : fe80::f8fc:fab4:f423:7ad3%15
  39. IPv4 地址 . . . . . . . . . . . . : 192.168.1.13
  40. 子网掩码 . . . . . . . . . . . . : 255.255.255.0
  41. 默认网关. . . . . . . . . . . . . : fe80::1%15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

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

(七)粘包以及解决措施

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

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

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

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

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

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

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

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

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

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. import time
  5. import struct
  6. socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  7. #客户端的套接字发送链接请求
  8. socket_client.connect(("127.0.0.1",8083))
  9. """
  10. 双方建立好链接请求之后,进行相同通信
  11. """
  12. msg = "zhang贸发大水范德萨范德萨zhangdir"
  13. print(len(msg))
  14. print(msg)
  15. len_msg = struct.pack('i',len(msg.encode("utf-8")))
  16. socket_client.send(len_msg)
  17. socket_client.send(msg.encode("utf-8"))
  18. socket_client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

服务端:

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. import time
  5. import struct
  6. socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  7. socket_server.bind(("127.0.0.1",8083))
  8. socket_server.listen(5)
  9. #等待客户端的socket发送链接请求,并建立连接
  10. print("等待链接中......")
  11. conn,addresss = socket_server.accept()
  12. print("链接已生成......")
  13. print("客户端的地址是:",addresss)
  14. """
  15. 双方建立好链接请求之后,进行相同通信
  16. """
  17. msg_len = struct.unpack('i',conn.recv(4))
  18. print(msg_len[0])
  19. msg = conn.recv(msg_len[0])
  20. print(msg.decode('utf-8'))
  21. conn.close()
  22. socket_server.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

运行结果:
客户端:

  1. 23
  2. zhang贸发大水范德萨范德萨zhangdir
  • 1
  • 2

服务端:

  1. 等待链接中......
  2. 链接已生成......
  3. 客户端的地址是: ('127.0.0.1', 50107)
  4. 43
  5. zhang贸发大水范德萨范德萨zhangdir
  6. Process finished with exit code 0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. import struct
  5. import json
  6. #创建socket套接字,并指定通信所用的协议
  7. socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  8. #客户端向服务端发送链接请求
  9. socket_client.connect(("127.0.0.1",8081))
  10. """
  11. 双方建立了连接后,就开始进行相同通信
  12. """
  13. while True:
  14. str_msg = input(">>")
  15. if not str_msg: continue
  16. socket_client.send(str_msg.encode('utf-8'))
  17. print("消息已经发送出去!")
  18. #我们在客户端就先收取4个字节的长度,并进行解包,看看服务端发送过来的数据到底多长
  19. total_size = struct.unpack('i',socket_client.recv(4))[0]
  20. #下面这样写是不合适的,我们应该一点一点的拿,通过for循环
  21. #ser_msg = socket_client.recv(ser_len[0])
  22. recv_size = 0
  23. data = b''
  24. while recv_size < total_size:
  25. #socket_client.recv(1024)虽然写的是收取1024个字节,但是收取的是实际的字节
  26. recv_data = socket_client.recv(1024)
  27. data += recv_data
  28. #不能写1024,写真实接收的数据
  29. recv_size += len(recv_data)
  30. print("服务端发送过来的消息是:%s"%data.decode('gbk'))
  31. socket_client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

服务端:

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. import subprocess
  5. import struct
  6. import json
  7. #创建socket套接字,并指定通信所用的协议
  8. socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  9. #重新使用IP地址和端口号
  10. socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  11. socket_server.bind(("127.0.0.1",8081))
  12. socket_server.listen(5)
  13. while True:
  14. print("等待客户端的连接.........")
  15. conn,address = socket_server.accept()
  16. print("链接已经生成")
  17. print("客户端的信息是:",address)
  18. """
  19. 双方建立了连接后,就开始进行相同通信
  20. """
  21. while True:
  22. try:
  23. client_msg = conn.recv(1024).decode("utf-8")
  24. if not client_msg:break
  25. print("客户端发送的消息是:%s" %client_msg)
  26. res = subprocess.Popen(client_msg,shell=True,
  27. stdout=subprocess.PIPE,
  28. stderr=subprocess.PIPE)
  29. #获取的结果是平台格式的二进制串
  30. res_err = res.stderr.read()
  31. if res_err:
  32. cmd_res = res_err
  33. else:
  34. cmd_res = res.stdout.read()
  35. #服务端在真正发送数据的时候,应该先发送一下数据的长度
  36. aa = struct.pack('i', len(cmd_res))
  37. #这两条数据肯定会黏在一起的
  38. #先发送报头的长度
  39. conn.send(aa)
  40. #在发送真实的数据
  41. conn.send(cmd_res)
  42. except Exception:
  43. break
  44. conn.close()
  45. socket_server.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

服务端程序:

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

客户端程序:

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. socket_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  5. socket_client.connect(("127.0.0.1",8081))
  6. """
  7. 双方建立连接后进行通信
  8. """
  9. while True:
  10. msg = input(">>:").strip()
  11. if not msg: continue
  12. socket_client.send(msg.encode("utf-8"))
  13. server_msg = socket_client.recv(1024).decode("utf-8")
  14. print("服务端发送过来的数据是:%s"%server_msg)
  15. socket_client.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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

  1. 等待建立连接中........
  2. 建立连接
  3. 客户端的地址是: ('127.0.0.1', 61406)
  4. 进程的名称是:('127.0.0.1', 61406)
  5. 服务端在等待客户端发送消息..........
  6. 等待建立连接中........
  7. 建立连接
  8. 客户端的地址是: ('127.0.0.1', 61410)
  9. 进程的名称是:('127.0.0.1', 61410)
  10. 服务端在等待客户端发送消息..........
  11. 等待建立连接中........
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

服务端同步的执行代码:

(八)进程池中同步与异步的执行方案
  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. from multiprocessing import Process,Pool
  5. import time
  6. socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  7. socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  8. socket_server.bind(("127.0.0.1",8081))
  9. socket_server.listen(5)
  10. def talk(conn,addr):
  11. while True:
  12. try:
  13. msg = conn.recv(1024)
  14. if not msg:break
  15. print("客户端发送过来的数据是:%s" % msg)
  16. conn.send(msg.upper())
  17. except Exception:
  18. break
  19. if __name__ == "__main__":
  20. pool = Pool(processes=4)
  21. while True:
  22. # 链接循环
  23. print("等待建立连接中........")
  24. conn, address = socket_server.accept()
  25. print("建立连接")
  26. print("客户端的地址是:", address)
  27. #建立完连接之后向进程池中提交任务
  28. pool.apply(func=talk,args=(conn,address,))
  29. #问题:只有当第一个提交的任务执行完毕之后,进程池中才会建立连接,接受第二个任务,并进行执行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

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

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

  1. #!/usr/bin/python
  2. # -*- coding:utf-8 -*-
  3. import socket
  4. from multiprocessing import Process,Pool
  5. import time
  6. import os
  7. socket_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  8. socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  9. socket_server.bind(("127.0.0.1",8081))
  10. socket_server.listen(5)
  11. def talk(conn,addr):
  12. print("当前执行的进程PID是\033[41;1m%s\033[0m"%os.getpid())
  13. while True:
  14. try:
  15. msg = conn.recv(1024)
  16. if not msg:break
  17. print("客户端发送过来的数据是:%s" % msg.decode("utf-8"))
  18. conn.send(msg.upper())
  19. except Exception:
  20. break
  21. if __name__ == "__main__":
  22. pool = Pool(processes=2)
  23. res_l = []
  24. while True:
  25. # 链接循环
  26. print("等待建立连接中........")
  27. conn, address = socket_server.accept()
  28. print("建立连接")
  29. print("客户端的地址是:", address)
  30. #建立完连接之后向进程池中提交任务
  31. res = pool.apply_async(func=talk,args=(conn,address))
  32. #通过程序可以知道:res_l中仅仅添加了AsyncResult对象,但是并没有得到执行
  33. res_l.append(res)
  34. print(res_l)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

运行结果:

  1. D:\Python34\python.exe "D:/Python Work Location/Python 0507/day10/服务端进程池.py"
  2. 等待建立连接中........
  3. 建立连接
  4. 客户端的地址是: ('127.0.0.1', 57599)
  5. [<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>]
  6. 等待建立连接中........
  7. 当前执行的进程PID7564
  8. 建立连接
  9. 客户端的地址是: ('127.0.0.1', 57603)
  10. [<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>]
  11. 等待建立连接中........
  12. 当前执行的进程PID13832
  13. 建立连接
  14. 客户端的地址是: ('127.0.0.1', 57604)
  15. [<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>, <multiprocessing.pool.ApplyResult object at 0x0000000003077550>]
  16. 等待建立连接中........
  17. 建立连接
  18. 客户端的地址是: ('127.0.0.1', 57608)
  19. [<multiprocessing.pool.ApplyResult object at 0x00000000030774E0>, <multiprocessing.pool.ApplyResult object at 0x0000000003077978>, <multiprocessing.pool.ApplyResult object at 0x0000000003077550>, <multiprocessing.pool.ApplyResult object at 0x00000000030874A8>]
  20. 等待建立连接中........
  21. 建立连接
  22. 客户端的地址是: ('127.0.0.1', 57612)
  23. [<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>]
  24. 等待建立连接中........
  25. 客户端发送过来的数据是:dfdsf
  26. 客户端发送过来的数据是:gdsgs
  27. 当前执行的进程PID7564
  28. 客户端发送过来的数据是:gdsgdgg
  29. 当前执行的进程PID13832
  30. 当前执行的进程PID7564
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

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

基于Socket网络编程的更多相关文章

  1. Java Web 基础(一) 基于TCP的Socket网络编程

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

  2. socket 网络编程高速入门(一)教你编写基于UDP/TCP的服务(client)通信

    由于UNIX和Win的socket大同小异,为了方便和大众化,这里先介绍Winsock编程. socket 网络编程的难点在入门的时候就是对基本函数的了解和使用,由于这些函数的结构往往比較复杂,參数大 ...

  3. Python Socket 网络编程

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

  4. Python全栈【Socket网络编程】

    Python全栈[socket网络编程] 本章内容: Socket 基于TCP的套接字 基于UDP的套接字 TCP粘包 SocketServer 模块(ThreadingTCPServer源码剖析) ...

  5. Python之路【第七篇】python基础 之socket网络编程

    本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket  网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...

  6. 循序渐进Socket网络编程(多客户端、信息共享、文件传输)

    循序渐进Socket网络编程(多客户端.信息共享.文件传输) 前言:在最近一个即将结束的项目中使用到了Socket编程,用于调用另一系统进行处理并返回数据.故把Socket的基础知识总结梳理一遍. 1 ...

  7. windows socket 网络编程

    样例代码就在我的博客中,包含六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具project,和此小工具的全部执行时库.资源和执行程序.代码的压缩包位置是http://www.b ...

  8. Socket网络编程详解

    一,socket的起源 socket一词的起源 在组网领域的首次使用是在1970年2月12日发布的文献IETF RFC33中发现的, 撰写者为Stephen Carr.Steve Crocker和Vi ...

  9. Socket网络编程(案例)

    Socket:套接字 java.net包 1.流式套接字:基于TCP协议的Socket网络编程 工作方式: 1.客户端A连接到服务器: 2.服务器建立连接并把客户端A添加到列表: 3.客户端B.C.. ...

随机推荐

  1. LeetCode(50):Pow(x, n)

    Medium! 题目描述: 实现 pow(x, n) ,即计算 x 的 n 次幂函数. 示例 1: 输入: 2.00000, 10 输出: 1024.00000 示例 2: 输入: 2.10000, ...

  2. 【python】中文提取,判断,分词

    参考: http://www.cnblogs.com/kaituorensheng/p/3595879.html https://github.com/fxsjy/jieba 判断是否包含中文 def ...

  3. laravel使用when搜索遇到状态参数(有0的状态)的坑

    今天,使用when()方法写活动列表的接口,有两个状态搜索,都有0这个状态,当传参为0时when()就失效了... 反反复复的验证参数,传参确实是0和1啊...百思不得其解~~~后面仔细想想when( ...

  4. cf803c 数论

    细节很多的题 #include<bits/stdc++.h> using namespace std; #define ll long long int main(){ ll n,k,tm ...

  5. 查看Windows系统里的进程已运行的时间

    搜索 ProcessExplorer ,可以去微软下载它.右键点击项类,selcet conlumns...在 Process Performance 里 选择start time.有了进程的启动时间 ...

  6. Spring3.X jdk8 java.lang.IllegalArgumentException

    异常提示: javax.servlet.ServletException: Servlet.init() for servlet springMVC threw exception org.apach ...

  7. 存储过程,存储函数(Oracle)

    存储过程和存储函数 指存储在数据库中供所有用户程序调用的子程序叫存储过程.存储函数. 存储过程和存储函数的区别? 存储函数:可以通过return 语句返回函数值. 存储过程:不能 除此之外我们可以认为 ...

  8. hdu3047 Zjnu Stadium【带权并查集】

    <题目链接> <转载于 >>> > 题目大意: 有n个人坐在zjnu体育馆里面,然后给出m个他们之间的距离, A B X, 代表B的座位比A多X. 然后求出这 ...

  9. IdentityServer4-MVC+Hybrid实现Claims授权验证(四)

    上节以对话形式,大概说了几种客户端授权模式的原理,这节重点介绍Hybrid模式在MVC下的使用.且为实现IdentityServer4从数据库获取User进行验证,并对Claim进行权限设置打下基础( ...

  10. 【Ray Tracing The Next Week 超详解】 光线追踪2-8 Volume

     Preface 今天有两个东东,一个是体积烟雾,一个是封面图 下一篇我们总结项目代码 Chapter 8:Volumes 我们需要为我们的光线追踪器添加新的物体——烟.雾,也称为participat ...