# 服务端
from socket import *
ip_port = ('127.0.0.1', 8000)
back_log = 10
buffer_size = 1024
tcp_server = socket(AF_INET, SOCK_STREAM)
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 当出现OSError: [Errno 48] Addres already in use异常时(服务端仍处于四次挥手的time_wait状态),加入这一条socket配置,重用ip和端口,得在bind前加
tcp_server.bind(ip_port)
tcp_server.listen(back_log)
while True:
'''服务端循环链接请求来收发消息'''
print('服务端开始运行了---')
conn, addr = tcp_server.accept() # 服务端阻塞,等待客户端链接
print('双向链接是', conn)
print('客户端地址是', addr)
while True:
'''循环收消息和发消息,加入if判断跳出循环断开链接'''
try:
data = conn.recv(buffer_size) # 1024为字节,不加try当有一方链接自动断开时,会报错ConnectionResetError,所以加上try;如果不加try有些系统不会报错,data一直为空,此时加入一个if判断然后跳出循环就好了;
print('客户端发送的消息是', data.decode('utf-8'))
conn.send(data.upper()) # 如果输入是中文,upper()不会报错,而是将原来信息全部返回
except Exception:
break
conn.close()
tcp_server.close()
# 客户端
from socket import *
ip_port = ('127.0.0.1', 8000)
buffer_size = 1024
tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)
while True:
'''循环发消息和收消息,加入if判断输入是否为空,是为空则再次输入'''
msg = input('请输入:').strip()
if not msg: continue # 判断用户输入为空的状态,如果为空进入下一次循环
tcp_client.send(msg.encode('utf-8'))
data = tcp_client.recv(buffer_size)
print('服务端发送的消息是', data.decode('utf-8'))
tcp_client.close()
# 客户端1,代码一样,测试多个链接请求
from socket import *
ip_port = ('127.0.0.1', 8000)
buffer_size = 1024
tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)
while True:
'''循环发消息和收消息,加入if判断输入是否为空,是为空则再次输入'''
msg = input('请输入:').strip()
if not msg: continue # 判断用户输入为空的状态,如果为空进入下一次循环
tcp_client.send(msg.encode('utf-8'))
data = tcp_client.recv(buffer_size)
print('服务端发送的消息是', data.decode('utf-8'))
tcp_client.close()
'''
socket收发消息原理刨析:
计算机分为应用软件和OS(操作系统)及硬件组成;内存有用户态内存(应用程序)和内核态内存(缓存,系统数据),客户端与服务端的交流可以这样形容:
socket应用程序把数据send()到内核态内存,然后经由网卡进行传输,另一端的内核态内存收到网卡传输的数据,然后由recv(1024)进行对数据读取;当send()数据为空时,根本没有消息传输
比如此程序用户端输入为空,就是直接按enter键,然后两端都被阻塞,recv()没有从内核态内存读取到消息
缓冲区(内核态内存)数据为队列列型,即先进先出;堆栈类型是先进后出
'''
![](https://img2020.cnblogs.com/blog/1995523/202105/1995523-20210519141906327-923798372.jpg)