需求
在这里插入代码片建立基于UDP协议的Socket编程方法来制作网络五子棋程序。
根据需求,该系统所应实现的功能有以下一些:
游戏时服务器端首先启动,当客户端连接后,服务器端可以走棋。
用户根据提示信息,轮到自己下棋才可以在棋盘上落子,同时下方标签会显对方的走棋信息,服务端和客户端用户均可以通过“退出游戏”按钮结束游戏。
代码
客户端
# 客户端启动程序
import sys
import tkinter as tk
from tkinter import messagebox as msgbox
import socket
import threading
import os
from lib import common
from conf import setting
# 发送消息
def send_message(pos):
global s
s.sendto(pos.encode(), (host, port))
print('客户端发送信息', pos)
# 走棋函数
def callback(event):
global turn
global Myturn
if Myturn == -1: # 第一次确定自己的角色
Myturn = turn
else:
if (Myturn != turn):
msgbox.showinfo(title='提示', message='还没轮到自己走棋')
return
# print('clicked at ', event.x, event.y, turn)
x = (event.x) // 40 # 610x610, 15x15
y = (event.y) // 40 # 换算棋盘坐标
print('clicked at ', x, y, turn)
if map[x][y] != ' ':
msgbox.showinfo(title='提示', message='已有棋子')
else:
img1 = imgs[turn]
cv.create_image((x * 40 + 20, y * 40 + 20), image=img1) # 画自己棋子
cv.pack()
map[x][y] = str(turn)
pos = str(x) + ',' + str(y)
send_message('move|' + pos)
print('客户端走棋的位置', pos)
label1['text'] = '客户端走棋的位置' + pos
# 输赢信息
if common.win_lose(turn, map) == True:
if turn == 0:
msgbox.showinfo(title='提示', message='黑方赢了')
send_message('over|黑方赢了')
else:
msgbox.showinfo(title='提示', message='白方赢了')
send_message('over|白方赢了')
# 换对手走棋
if turn == 0:
turn = 1
else:
turn = 0
return None
# 退出游戏
def callexit(event):
pos = 'exit|'
send_message(pos)
os._exit(0)
# 显示当前棋子信息
def print_map():
for j in range(0, 15):
for i in range(0, 15):
print(map[i][j], end=' ')
print('w')
# 画对方棋子
def drawOtherChess(x, y):
global turn
img1 = imgs[turn]
cv.create_image((x * 40 + 20, y * 40 + 20), image=img1)
cv.pack()
map[x][y] = str(turn)
# 换对方走棋
if turn == 0:
turn = 1
else:
turn = 0
# 接受服务器消息
def receiveMessage():
global s
while True:
global addr
data, addr = s.recvfrom(1024)
data = data.decode('utf-8')
a = data.split('|')
print('................客户端正在接收消息...')
if not data:
print('server has exited!')
break
# elif a[0] == 'join':
# print('client 连接服务器!')
# label1['text'] = 'client 连接服务器成功,请你走棋!'
elif a[0] == 'exit':
print('client 对方退出!')
label1['text'] = 'server 对方退出,游戏结束!'
elif a[0] == 'over':
print_map()
label1['text'] = data.split('|')[0]
msgbox.showinfo(title='提示', message=data.split('|')[1])
elif a[0] == 'move':
print('received: ', data, 'from ', addr)
p = a[1].split(',')
x = int(p[0])
y = int(p[1])
print(p[0], p[1])
label1['text'] = '服务器走的位置' + p[0] + p[1]
drawOtherChess(x, y)
s.close()
# 开启新的线程,用于接受信息
def startNewThread():
thread = threading.Thread(target=receiveMessage, args=())
thread.setDaemon(True)
thread.start()
print('thread start')
if __name__ == '__main__':
sys.path.append(setting.BASE_PATH)
# print(sys.path)
root = tk.Tk() # 创建win窗口对象
root.title('网络五子棋--客户端(By王训志)')
imgs = [
tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_black.PNG')),
tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_white.PNG')),
]
# 这里用0表示黑子、1表示白子
turn = 0
# 用于保存自己的角色 -1代表还未确定角色
Myturn = -1
# 记录游戏地图
map = [[' ' for i in range(15)] for y in range(15)]
cv = tk.Canvas(root, bg='green', width=610, height=610) # 绑定画布
common.draw(cv)
cv.bind('<Button-1>', callback) # 绑定点击按钮
cv.pack() # 将cv打包进主窗口
label1 = tk.Label(root, text="客户端....") # 绑定标签
label1.pack() # 同理打包
button1 = tk.Button(root, text='退出游戏')
button1.bind('<Button-1>', callexit)
button1.pack()
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print('................客户端正在尝试通信...')
port = 8080
host = '127.0.0.1' # 回环地址
pos = 'join|' # 连接服务器
send_message(pos)
startNewThread()
root.mainloop() # 显示窗口
服务端
# 服务端启动程序
import sys
import tkinter as tk
from tkinter import messagebox as msgbox
import socket
import threading
import os
from conf import setting
from lib import common
# 不用扫描全部棋盘,以中心点判断
def checkWin(x, y):
flag = False
color = map[x][y]
# 横向判断
i = 1
count = 1
while color == map[x+i][y]:
count += 1
i += 1
i = 1
while color == map[x-i][y]:
count += 1
i += 1
if count >= 5:
flag = True
# 纵向判断
i2 = 1
count2 = 1
while color == map[x][y-i2]:
count2 += 1
i2 += 1
i2 = 1
while color == map[x][y+i2]:
count2 += 1
i2 += 1
if count2 >= 5:
flag = True
# 斜方向 异向
i3 = 1
count3 = 1
while color == map[x+i3][y-i3]:
count3 += 1
i3 += 1
i3 = 1
while color == map[x-i3][y+i3]:
count3 += 1
i3 += 1
if count3 >= 5:
flag = True
# 斜方向 同向
i4 = 1
count4 = 1
while color == map[x+i3][y+i3]:
count4 += 1
i4 += 1
i4 = 1
while color == map[x-i3][y-i3]:
count4 += 1
i4 += 1
if count4 >= 5:
flag = True
return flag
def sendMessage(pos):
global s
global addr
s.sendto(pos.encode(), addr)
print('服务器发送信息', pos)
def callpos(event):
global turn
global Myturn
if Myturn == -1:
Myturn = turn
else:
if (Myturn != turn):
msgbox.showinfo(title='提示', message='还没轮到自己走棋')
return
# print('clicked at ', event.x, event.y, turn)
x = (event.x) // 40 # 610x610, 15x15
y = (event.y) // 40
print('clicked at ', x, y, turn)
if map[x][y] != ' ':
msgbox.showinfo(title='提示', message='已有棋子')
else:
img1 = imgs[turn]
cv.create_image(
(x*40+20, y*40+20),
image=img1,
)
cv.pack()
map[x][y] = str(turn)
pos = str(x) + ',' + str(y)
sendMessage('move|' + pos)
print('服务器走棋的位置', pos)
label1['text'] = '服务器走棋的位置' + pos
# 输赢信息
if common.win_lose(turn, map) == True:
if turn == 0:
msgbox.showinfo(title='提示', message='黑方赢了')
sendMessage('over|黑方赢了')
else:
msgbox.showinfo(title='提示', message='白方赢了')
sendMessage('over|白方赢了')
# 换对手走棋
if turn == 0:
turn = 1
else:
turn = 0
return None
def callexit(event):
pos = 'exit|'
sendMessage(pos)
os._exit(0)
def startNewThread():
thread = threading.Thread(target=receiveMessage, args=()) # 线程要的是函数名,而非函数返回值
thread.setDaemon(True)
thread.start()
print('thread start')
def print_map():
for j in range(0, 15):
for i in range(0, 15):
print(map[i][j], end=' ')
print('w')
def drawOtherChess(x, y):
global turn
img1 = imgs[turn]
cv.create_image((x*40+20, y*40+20), image=img1)
cv.pack()
map[x][y] = str(turn)
if turn == 0:
turn = 1
else:
turn = 0
def receiveMessage():
global s
while True:
global addr
data, addr = s.recvfrom(1024)
data = data.decode('utf-8')
a = data.split('|') # 根据自己定义的协议,划分数据
print('................服务器正在接收消息...')
if not data:
print('client has exited!')
break
elif a[0] == 'join':
print('client 连接服务器!')
label1['text'] = 'client 连接服务器成功,请你走棋!'
elif a[0] == 'exit':
print('client 对方退出!')
label1['text'] = 'client 对方退出,游戏结束!'
elif a[0] == 'over':
print_map()
label1['text'] = data.split('|')[0]
msgbox.showinfo(title='提示', message=data.split('|')[1])
elif a[0] == 'move':
print('received: ', data, 'from ', addr)
p = a[1].split(',')
x = int(p[0])
y = int(p[1])
print(p[0], p[1])
label1['text'] = '客户端走的位置' + p[0] + p[1]
drawOtherChess(x, y)
s.close()
if __name__ == '__main__':
sys.path.append(setting.BASE_PATH)
# print(sys.path)
root = tk.Tk()
root.title('网络五子棋--服务器端(By王训志)')
imgs = [
tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_black.PNG')),
tk.PhotoImage(file=os.path.join(setting.BASE_PATH, 'pictures', 'stone_white.PNG')),
]
turn = 0
# 保存自己的角色,-1 表示还没确定下来
Myturn = -1
map = [[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] for y in range(15)]
cv = tk.Canvas(root, bg='green', width=610, height=610)
common.draw(cv)
cv.bind('<Button-1>', callpos)
cv.pack()
label1 = tk.Label(root, text="服务器端....")
label1.pack()
button1 = tk.Button(root, text='退出游戏')
button1.bind('<Button-1>', callexit)
button1.pack()
# 创建UDP socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print('................服务器正在尝试通信...')
s.bind(('127.0.0.1', 8080))
addr = ('127.0.0.1', 8080)
startNewThread()
root.mainloop()
注意:仅展示部分代码,需要私