前言:
在学校两个月都没有时间更博客,现在放假回家把原来准备好的给补上。
客户端服务器(Client Server, C/S) 结构网络是一种主从结构网络。服务器一般处于等待状态,如果有客户端请求,服务器响应请求,建立连接提供服务。服务器是被动的,客户端是主动的。
一、知识点总结
1. 创建TCP Socket
socket 模块提供了一个socket() 函数可以创建多种形式的socket 对象。
语法如下:socket(family, type[,protocol])
# 定义套接字 s
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
各参数的意义如下:family :
地址系列。默认为AF_INET (2 ,socket 模块中的常量),对应于IPV4 ;AF_UNIX ,对应于Unix 的进程间通信;AF_INET6 ,对应于IPV6 。type :
socket 类型。默认为SOCK_STREAM ,对应于TCP 流套接字;SOCK_DGRAM ,对应于UDP 数据报套接字;SOCK_RAW ,对应于raw 套接字。protocol
2. TCP Socket 服务器编程方法
sock.bind(address) :
绑定地址和端口,address 是包含主机名(或IP 地址)和端口的二元组对象;sock.listen(backlog) :
监听端口,backlog 是最大连接数,backlog 默认值是1 。sock.accept() :
s.bind(("127.0.0.1", 8888)) # 绑定IP和端口(参数为二元组),就是寻址
s.listen(5) # 因为是TCP,所有要监听端口
self.conn,self.addess=s.accept() # 等待客户端连接(参数为最大连接数),返回一个二元组(新的socket对象+客户端地址)
data =self.conn.recv(1024) # 接受1024字节序列数据(这个函数阻塞,直到接受到数据)
3 .客户端编程socket 方法
socket.connect(address)
:连接服务器
s.connect(("127.0.0.1", 8888))
socket ,address 是包含主机名(或IP 地址)和端口的二元组对象。
4 .服务器和客户端编程socket 共用方法
socket.recv(buffsize) :
接收TCP Socket 数据, 该方法返回字节序列对象。 参数buffsize 指定一次接收的最大字节数,因此如果要接收的数据量大于buffsize ,则需要多次调用该方法进行接收。socket.send(bytes) :
发送TCP Socket 数据,将bytes 数据 发送到远程Socket ,返回成功发送的字节数。如果要发送的数据量很大,则需要多次调用该方法发送数据。socket.sendall(bytes) :
发送TCP Socket 数据,将bytes 数据 发送到远程Socket ,如果发送成功返回None ,如果失败则抛出异常。与socket.send(bytes) 不同的是,该方法连续发送数据,直到发送完所有数据或发生异常。socket.settimeout(timeout) :
设置Socket 超时时间,timeout 是一个浮点数,单位是秒,值为None 则表示永远不会超时。一般超时时间应在刚创建Socket 时设置。socket.close() :
二、实现QQ聊天 (未使用多线程)
未使用多线程会出现阻塞,只能一发一收的进行,不能多发多收进行!
服务器端:
# 案列使用TCP连接
# 这是服务器端
import socket
import wx
# 定义套接字 s
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 定义窗口类
class MyFrame(wx.Frame):
# 初始化这里就是生成界面,然后绑定了按钮事件,其他没了
def __init__(self):
super().__init__(parent=None,size=(350,250),title="QQ聊天:服务器")
self.Center()
panel=wx.Panel(self)
self.text = wx.TextCtrl(parent=panel, id=-1, style=wx.TE_MULTILINE,size=(100,150))
startbutton = wx.Button(parent=panel, id=1, label="开启服务器")
self.inputtext = wx.TextCtrl(parent=panel, id=-1)
button=wx.Button(parent=panel,id=2,label="发送")
# 绑定按钮事件
self.Bind(wx.EVT_BUTTON, self.Talk, id=1)
self.Bind(wx.EVT_BUTTON,self.on_button,id=2)
box1=wx.BoxSizer()
box1.Add(startbutton, 1, flag=wx.ALL | wx.EXPAND, border=10)
box1.Add(self.inputtext,1,flag=wx.ALL|wx.EXPAND,border=10)
box1.Add(button,1,flag=wx.ALL|wx.EXPAND,border=10)
mainbox=wx.BoxSizer(wx.VERTICAL)
mainbox.Add(self.text,flag=wx.EXPAND)
mainbox.Add(box1, flag=wx.EXPAND)
panel.SetSizer(mainbox)
# 开启服务器端函数
def Talk(self,event):
s.bind(("127.0.0.1", 8888)) # 绑定IP和端口(参数为二元组),就是寻址
s.listen(5) # 因为是TCP,所有要监听端口
print("服务器启动·····")
self.conn,self.addess=s.accept() # 等待客户端连接(参数为最大连接数),返回一个二元组(新的socket对象+客户端地址)
data =self.conn.recv(1024) # 接受1024字节序列数据(这个函数阻塞,直到接受到数据)
if len(data) != 0: # 将接受的数据写入文本纪录框中
self.text.LabelText += "客户端:" + data.decode() + "\r\n"
# 发送信息给客户端函数
def on_button(self,event):
msg=self.inputtext.GetValue() # 获取要发送的文本
self.conn.send(msg.encode()) # 给客户端发送信息
self.text.LabelText+="服务器:"+msg+"\r\n" # 将发送信息写入纪录文本框
data = self.conn.recv(1024) # 等待接收客户端信息
if len(data) != 0:
self.text.LabelText +="客户端:"+data.decode()+"\r\n" # 将发送信息写入纪录文本框
# 应用程序
class App(wx.App):
def OnInit(self):
frame=MyFrame()
frame.Show()
return True
def OnExit(self):
s.close() # 关闭socket对象
return 0
# 进入main函数运行:循环
if __name__=="__main__":
app=App()
app.MainLoop()
客户端:
# 案列使用TCP连接
# 这是客户端
import socket
import wx
# 定义套接字 s
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 定义窗口类
class MyFrame(wx.Frame):
# 初始化这里就是生成界面,然后绑定了按钮事件,其他没了
def __init__(self):
super().__init__(parent=None, size=(350, 250), title="QQ聊天:客户端")
self.Center()
panel = wx.Panel(self)
self.text = wx.TextCtrl(parent=panel, id=-1, style=wx.TE_MULTILINE, size=(100, 150))
startbutton = wx.Button(parent=panel, id=1, label="连接服务器")
self.inputtext = wx.TextCtrl(parent=panel, id=-1)
button = wx.Button(parent=panel, id=2, label="发送")
self.Bind(wx.EVT_BUTTON, self.Talk, id=1)
self.Bind(wx.EVT_BUTTON, self.on_button, id=2)
box1 = wx.BoxSizer()
box1.Add(startbutton, 1, flag=wx.ALL | wx.EXPAND, border=10)
box1.Add(self.inputtext, 1, flag=wx.ALL | wx.EXPAND, border=10)
box1.Add(button, 1, flag=wx.ALL | wx.EXPAND, border=10)
mainbox = wx.BoxSizer(wx.VERTICAL)
mainbox.Add(self.text, flag=wx.EXPAND)
mainbox.Add(box1, flag=wx.EXPAND)
panel.SetSizer(mainbox)
# 连接服务器函数
def Talk(self, event):
s.connect(("127.0.0.1", 8888))
return
# 发送信息给服务器端
def on_button(self, event):
msg = self.inputtext.GetValue()# 获取要发送的文本
s.send(msg.encode())# 给服务器端发送信息
self.text.LabelText += "客户端:" + msg + "\r\n"# 将发送信息写入纪录文本框
data = s.recv(1024)# 等待接收服务器端信息
if len(data) != 0:
self.text.LabelText += "服务器:" + data.decode() + "\r\n"# 将发送信息写入纪录文本框
class App(wx.App):
def OnInit(self):
frame = MyFrame()
frame.Show()
return True
if __name__=="__main__":
app=App()
app.MainLoop()
三、实现QQ聊天 (使用多线程)
服务器端:
# 这个聊天案例使用了 多线程
# 案列使用TCP连接
# 这是服务器端
import socket
import wx
import threading
import time
# 定义套接字 s
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 定义窗口类
class MyFrame(wx.Frame):
# 初始化这里就是生成界面,然后绑定了按钮事件,其他没了
def __init__(self):
super().__init__(parent=None,size=(350,250),title="QQ聊天:服务器")
self.Center()
panel=wx.Panel(self)
self.text = wx.TextCtrl(parent=panel, id=-1, style=wx.TE_MULTILINE,size=(100,150))
startbutton = wx.Button(parent=panel, id=1, label="开启服务器")
self.inputtext = wx.TextCtrl(parent=panel, id=-1)
button=wx.Button(parent=panel,id=2,label="发送")
# 绑定按钮事件
self.Bind(wx.EVT_BUTTON, self.Talk, id=1)
self.Bind(wx.EVT_BUTTON,self.on_button,id=2)
box1=wx.BoxSizer()
box1.Add(startbutton, 1, flag=wx.ALL | wx.EXPAND, border=10)
box1.Add(self.inputtext,1,flag=wx.ALL|wx.EXPAND,border=10)
box1.Add(button,1,flag=wx.ALL|wx.EXPAND,border=10)
mainbox=wx.BoxSizer(wx.VERTICAL)
mainbox.Add(self.text,flag=wx.EXPAND)
mainbox.Add(box1, flag=wx.EXPAND)
panel.SetSizer(mainbox)
# 开启服务器端函数
def Talk(self,event):
s.bind(("127.0.0.1", 8888)) # 绑定IP和端口(参数为二元组),就是寻址
s.listen(5) # 因为是TCP,所有要监听端口
print("服务器启动·····")
self.conn, self.addess = s.accept() # 等待客户端连接(参数为最大连接数),返回一个二元组(新的socket对象+客户端地址)
threadQQ=threading.Thread(target=self.thread_body,name="Srever")
threadQQ.start()
def thread_body(self):
while True:
data = self.conn.recv(1024) # 接受1024字节序列数据(这个函数阻塞,直到接受到数据)
if len(data) != 0: # 将接受的数据写入文本纪录框中
self.text.LabelText += "客户端:" + data.decode() + "\r\n"
time.sleep(1)
# 发送信息给客户端函数
def on_button(self,event):
msg=self.inputtext.GetValue() # 获取要发送的文本
self.conn.send(msg.encode()) # 给客户端发送信息
self.text.LabelText+="服务器:"+msg+"\r\n" # 将发送信息写入纪录文本框
# 应用程序
class App(wx.App):
def OnInit(self):
frame=MyFrame()
frame.Show()
return True
def OnExit(self):
s.close() # 关闭socket对象
return 0
# 进入main函数运行:循环
if __name__=="__main__":
app=App()
app.MainLoop()
客户端:
# 这个聊天案例使用了 多线程
# 案列使用TCP连接
# 这是客户端
import socket
import wx
import threading
import time
# 定义套接字 s
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 定义窗口类
class MyFrame(wx.Frame):
# 初始化这里就是生成界面,然后绑定了按钮事件,其他没了
def __init__(self):
super().__init__(parent=None, size=(350, 250), title="QQ聊天:客户端")
self.Center()
panel = wx.Panel(self)
self.text = wx.TextCtrl(parent=panel, id=-1, style=wx.TE_MULTILINE, size=(100, 150))
startbutton = wx.Button(parent=panel, id=1, label="连接服务器")
self.inputtext = wx.TextCtrl(parent=panel, id=-1)
button = wx.Button(parent=panel, id=2, label="发送")
self.Bind(wx.EVT_BUTTON, self.Talk, id=1)
self.Bind(wx.EVT_BUTTON, self.on_button, id=2)
box1 = wx.BoxSizer()
box1.Add(startbutton, 1, flag=wx.ALL | wx.EXPAND, border=10)
box1.Add(self.inputtext, 1, flag=wx.ALL | wx.EXPAND, border=10)
box1.Add(button, 1, flag=wx.ALL | wx.EXPAND, border=10)
mainbox = wx.BoxSizer(wx.VERTICAL)
mainbox.Add(self.text, flag=wx.EXPAND)
mainbox.Add(box1, flag=wx.EXPAND)
panel.SetSizer(mainbox)
# 连接服务器函数
def Talk(self, event):
s.connect(("127.0.0.1", 8888))
threadQQ=threading.Thread(target=self.thread_dody,name="Client")
threadQQ.start()
return
def thread_dody(self):
while True:
data = s.recv(1024) # 等待接收服务器端信息
if len(data) != 0:
self.text.LabelText += "服务器:" + data.decode() + "\r\n" # 将发送信息写入纪录文本框
time.sleep(1)
# 发送信息给服务器端
def on_button(self, event):
msg = self.inputtext.GetValue()# 获取要发送的文本
s.send(msg.encode())# 给服务器端发送信息
self.text.LabelText += "客户端:" + msg + "\r\n"# 将发送信息写入纪录文本框
class App(wx.App):
def OnInit(self):
frame = MyFrame()
frame.Show()
return True
if __name__=="__main__":
app=App()
app.MainLoop()
学习进步!