一、套接字
套接字(socket)是一个抽象层,应用程序可以通过它发送或接受数据,可对其进行像文件一样的打开、读写和关闭等操作。网络套接字是IP地址与端口的组合。
套接字是网络编程中的一种通信机制,是支持TCP/IP得其网络的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单地说就是通信两方的一种约定,用套接字中的相关函数来完成通信过程。
二、两种协议
TCP协议
称为流式协议,可靠协议,是一种提供可靠数据传输的通用协议。
UDP协议
数据报协议(自带报头),一个面向无连接的协议。采用该协议不需要两个应用程序先建立连接。不能够提供数据互传,因此还协议传输数据安全新差。
三、TCP通信
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
1.socket通信简易版本
服务端
import socket server = socket.socket() server.bind(('127.0.0.1',8080)) # 插电话卡 绑定IP地址和端口(把地址绑定到套接字) server.listen() # 开机 半连接池 conn,addr = server.accept() # 接听 (接收客户端链接) data = conn.recv(1024) # 听别人说话(接收客户端信息) print(data) # 打印客户端信息 conn.send(b'hello girl') # 给别人回话(向客户端发送信息) conn.close() # 挂电话(关闭客户端套接字) server.close() # 关机(关闭服务器套接字)
客户端
import socket client = socket.socket() # 拿电话(创建套接字) client.connect(('127.0.0.1',8080)) # 拨号 写对方的ip和port(尝试连接服务器) client.send(b'hello boy') # 对别人说话(发送信息) data = client.recv(1024) # 听别人说话(接受信息) print(data) # 打印数据,也就是所得信息 client.close() # 挂电话(关闭客户端套接字)
2.通信循环
服务端
import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) conn,addr = server.accept() while True: data = conn.recv(1024) print(data) conn.send(data.upper()) # 将客户端传过来的数据转换成大写给客户端
客户端
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: # 利用While 循环来实现 msg = input('>>>:').encode('utf-8') # 输入内容 client.send(msg) # 将这个内容发送给服务端 data = client.recv(1024) print(data) # 打印出这个数据
3.连接循环
服务端
import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) # 5表示允许客户端最大等待数为5 while True: # 连接循环上一层,下面一层结束了,返回继续执行 conn,addr = server.accept() # 等待别人来 print(addr) # 打印结果为客户端的ip地址 while True: try: # 能够有效地解决服务端报错问题 data = conn.recv(1024) print(data) if len(data) == 0: break conn.send(data.upper()) except ConnectionResetError as e: print(e) break conn.close()
客户端
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) data = client.recv(1024) print(data)
4.代码健壮性补充
问题1
服务端代码
import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) conn,addr = server.accept() while True: data = conn.recv(1024) print(data) conn.send(data.upper())
客户端代码
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: # 利用While 循环来实现 msg = input('>>>:').encode('utf-8') # 输入内容 client.send(msg) # 将这个内容发送给服务端 data = client.recv(1024) print(data) # 打印出这个数据
1.先启动服务端,在启动客户端正常运行
2.结束客户端后,客户端能够正常退出,服务端会报错
针对于这个问题,就利用异常处理的方式来解决
客户端代码不变,服务端需要添加 Try 的方式来解决,这样服务端也能够正常退出,不会报错
服务端更改后的代码
import socket server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) conn,addr = server.accept() print(addr) while True: try: # 能够有效地解决服务端报错问题 data = conn.recv(1024) print(data) if len(data) == 0: break conn.send(data.upper()) except ConnectionResetError as e: print(e) break conn.close()
问题2
1.先启动服务端,在启动客户端正常运行
产生的问题:
会发现点击Enter键的时候,客户端不能够输入内容,服务端也一直在等待,这样就导致了程序一直卡在这了
2.解决方法:
在服务端等待的原基础上,对客户端代码进行修改,需要对msg进行一个判断
更改后的代码
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) data = client.recv(1024) print(data)
进行判断之后就可以输入了
5.TCP粘包问题
import struct import subprocess import socket import json server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() while True: try: cmd = conn.recv(1024) if len(cmd) == 0: break cmd = cmd.decode('utf-8') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res = obj.stdout.read() + obj.stderr.read() d = {'name':'jason','file_size':len(res),'info':'dasdjfhjncksc'} json_d = json.dumps(d) #1.先制作一个字典的报头 header = struct.pack('i',len(json_d)) #2.发送字典报头 conn.send(header) #3.发送字典 conn.send(json_d.encode('utf-8')) #4.发送真实的数据 conn.send(res) except ConnectionRefusedError: break conn.close()
客户端
import socket import struct import json client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0: continue client.send(msg) #1.先接受字典报头 header_dict = client.recv(4) #2.解析报头,获取字典长度 dict_size = struct.unpack('i',header_dict)[0] #3.接受字典数据 dict_bytes = client.recv(dict_size) dict_json = json.loads(dict_bytes.decode('utf-8')) #4.从字典中获取信息 print(dict_json) recv_size = 0 real_data = b'' while recv_size < dict_json.get('file_size'): data = client.recv(1024) real_data += data recv_size += len(data) print(real_data.decode('gbk'))