python Socket网络编程实现C/S模式和P2P

C/S模式

由于网络课需要实现Socket网络编程,所以简单实现了一下,C/S模式分别用TCP/IP协议与UDP协议实现,下面将分别讲解。

TCP/IP协议

TCP/IP协议是面向连接的,即客户端与服务器需要先建立连接后才能传输数据,以下是服务器端的代码实现。

服务端:

import socket
from threading import Thread

def deal(sock,addr):
 print('Accept new connection from {}:{}'.format(addr[0],addr[1]))
 sock.send('与服务器连接成功!'.encode('utf-8'))
 while True:
  data = sock.recv(1024).decode('utf-8') #1024为接收数据的最大大小
  print('receive from {}:{} :{}'.format(addr[0],addr[1],data))
  sock.send('信息已成功收到'.encode('utf-8'))

##创建tcp/IPV4协议的socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#为socket绑定端口
s.bind(('127.0.0.1',10240))
#监听端口,参数5为等待的最大连接量
s.listen(5)
print("Waiting for connection...")

while True:
 sock,addr = s.accept()
 t1 = Thread(target=deal,args=(sock,addr))
 t1.start()

#断开与该客户端的连接
sock.close()
s.close()

需要注意的是,服务器在等待客户端连接时,即accept()函数这里是阻塞的,如下代码每次只能接受一个客户端的连接。

while True:
  #接受一个新连接,accept等待并返回一个客户端连接
  sock,addr = s.accept()
  print('Accept new connection from {}:{}'.format(addr[0],addr[1]))
  #给客户端发送消息
  sock.send('连接成功!'.encode('utf-8'))
  while True:
    data = sock.recv(1024).decode('utf-8')  #1024为接收数据的最大大小
    print('receive from {}:{} :{}'.format(addr[0],addr[1],data))
    sock.send('信息已成功收到'.encode('utf-8'))
  #断开与该客户端的连接
  sock.close()

也就是说如果采用以上方式,一个客户端与服务器建立连接后,服务器就会进入一个死循环去收发该客户端的信息,因此需要引入多线程,每与一个客户端建立连接,就为其创建一个线程用于控制信息的收发,这样便可以接受多个客户端的连接了。

客户端:

import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

##建立连接
s.connect(('127.0.0.1',10240))

#接收客户端连接成功服务器发来的消息
print(s.recv(1024).decode('utf-8'))

while True:
  data = input('发送给服务器:')
  if len(data)>0:

    s.send(data.encode('utf-8'))
    print('form sever:{}'.format(s.recv(1024).decode('utf-8')))
s.close()

客户端是比较简单的,需要与服务器建立连接后,再进行收发信息,这里不再赘述了。

UDP协议

UDP协议是面向无连接的,即服务器与客户端不需要提前建立连接,只需要向指定的端口直接发送数据即可。

服务端

import socket

#为服务器创建socket并绑定端口  SOCK_DGRAM指定了socket的类型为udp
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

s.bind(('127.0.0.1',7890))

print('Waiting for data...')
#upd无需监听
while True:
  data,addr = s.recvfrom(1024)
  print('Recevie from {}:{} :{}'.format(addr[0],addr[1],data.decode('utf-8')))
  #sendto的另一个参数为客户端socket地址
  s.sendto('信息已成功收到!'.encode('utf-8'),addr)

客户端

import socket

#为服务器创建socket并绑定端口  SOCK_DGRAM指定了socket的类型为udp
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
  data = input('发送给服务器:')
  s.sendto(data.encode('utf-8'),('127.0.0.1',7890))

  print('Receive from sever:{}'.format(s.recv(1024).decode('utf-8')))

可以看到UDP协议是非常简单的,由于不需要建立连接,所以也不需要创建线程来管理数据的收发。

C/S模式的应用程序

使用PyQt5对以上的程序进行封装,这是基于TCP/IP协议实现的。

服务端

from PyQt5.QtWidgets import (QApplication,QPushButton,
             QWidget,QLineEdit,QTextEdit)
import sys
import socket
from threading import Thread

import datetime

class UI(QWidget):
  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):

    #控件
    self.clear_btn = QPushButton('清空内容',self)
    self.text = QTextEdit(self)

    #布局
    self.clear_btn.setGeometry(150,400,100,40)
    self.text.setGeometry(20,20,360,370)

    self.text.setReadOnly(True)

    #信号连接
    self.clear_btn.clicked.connect(self.commit)
    #初始化socket
    self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    ##建立连接
    self.s.bind(('127.0.0.1',10240))

    self.s.listen(5)
    self.text.setText("Waiting for connection...")
    self.t = Thread(target = self.recv,args = ())
    self.t.start()
    #主窗口布局
    self.setGeometry(300, 300, 400, 450)
    self.setWindowTitle('Server')
    self.show()

  def commit(self):
    self.text.clear()

  def recv(self):
    while True:
      sock,addr = self.s.accept()
      t1 = Thread(target=self.deal,args=(sock,addr))
      t1.start()
    sock.close()

  def deal(self,sock,addr):
    #sock,addr = s.accept()
    self.text.append('Accept new connection from {}:{}'.format(addr[0],addr[1]))
    sock.send('与服务器连接成功!'.encode('utf-8'))
    while True:
      data = sock.recv(1024).decode('utf-8')  #1024为接收数据的最大大小
      self.text.append('[{}] receive from {}:{} :{}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),addr[0],addr[1],data))
      sock.send('信息已成功收到'.encode('utf-8'))
    sock.close()

  def closeEvent(self,event):
    self.s.close()
    event.accept()

if __name__ == '__main__':
  app = QApplication(sys.argv)
  ex = UI()
  sys.exit(app.exec_())

这里需要注意的是,由于Qt的主程序本身一直处于循环,如果直接阻塞等待客户端连接会导致程序崩溃,因此需要在Qt初始化时创建一个线程用于等待客户端的连接,要想同时多个客户端访问服务器,还需要在连接成功后再创建一个线程单独用于接收该客户端的数据。

客户端

from PyQt5.QtWidgets import (QApplication,QPushButton,
             QWidget,QLineEdit,QTextEdit)
import sys
import socket
from threading import Thread

import datetime

class UI(QWidget):
  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):

    #控件
    self.edit = QLineEdit(self)
    self.commit_btn = QPushButton('发送',self)
    self.text = QTextEdit(self)

    #布局
    self.edit.setGeometry(20, 410, 280, 30)
    self.commit_btn.setGeometry(310,410,70,30)
    self.text.setGeometry(20,20,360,380)

    self.text.setReadOnly(True)

    #信号连接
    self.commit_btn.clicked.connect(self.commit)
    #初始化socket
    self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    ##建立连接
    self.s.connect(('127.0.0.1',10240))
    self.text.setText('服务器 [{}]:{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),self.s.recv(1024).decode('utf-8')))
    #主窗口布局
    self.setGeometry(300, 300, 400, 450)
    self.setWindowTitle('Client')
    self.show()

  def commit(self):
    if len(self.edit.text()):
      text = self.edit.text()
      self.s.send(text.encode('utf-8'))
      self.text.append('本机 [{}]:{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),text))
      self.text.append('服务器 [{}]:{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),self.s.recv(1024).decode('utf-8')))
      self.edit.clear()

  def closeEvent(self,event):
    self.s.close()
    event.accept()

  def recv(self):
    while True:
      pass

if __name__ == '__main__':
  app = QApplication(sys.argv)
  ex = UI()
  sys.exit(app.exec_())

客户端还是比较简单,不需要创建线程,在发送按纽点击时触发事件,向服务器发送数据,并将发送的数据与服务器返回的数据显示在textEdit上。

P2P模式

老师说P2P模式就是用两个服务器相互连接通信(我以为是要客户端发送给服务器,服务器再转发给另一个客户端),为了实现方便,直接采用UDP协议,也不用创建那么多线程了。代码如下:

from PyQt5.QtWidgets import (QApplication,QPushButton,
             QWidget,QLineEdit,QTextEdit,QLabel)
import sys
import socket
from threading import Thread

import datetime

class UI(QWidget):
  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):

    #控件
    self.edit = QLineEdit(self)
    self.commit_btn = QPushButton('发送',self)
    self.text = QTextEdit(self)
    self.host_label = QLabel('ip地址:',self)
    self.host = QLineEdit(self)
    self.dst_port_label = QLabel('目标端口:',self)
    self.dst_port_edit = QLineEdit(self)
    self.src_port_label = QLabel('本机端口:',self)
    self.src_port_edit = QLineEdit(self)
    self.que_ren_btn = QPushButton('确认',self)

    #self.host_label.setStyleSheet("QLabel{font-size:25px}")
    #self.dst_port_label.setStyleSheet("QLabel{font-size:25px}")
    #self.src_port_label.setStyleSheet("QLabel{font-size:25px}")
    #布局
    self.edit.setGeometry(20, 480, 280, 30)
    self.commit_btn.setGeometry(310,480,70,30)
    self.text.setGeometry(20,90,360,380)
    self.host_label.setGeometry(20,20,65,25)
    self.host.setGeometry(90,20,110,25)
    self.dst_port_label.setGeometry(205,20,65,25)
    self.dst_port_edit.setGeometry(275,20,110,25)
    self.src_port_label.setGeometry(20,55,65,25)
    self.src_port_edit.setGeometry(90,55,110,25)
    self.que_ren_btn.setGeometry(205,55,70,25)

    self.text.setReadOnly(True)

    #信号连接
    self.commit_btn.clicked.connect(self.commit)
    self.que_ren_btn.clicked.connect(self.que_ren)
    #初始化socket
    self.s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    #主窗口布局
    self.setGeometry(300, 300, 400, 520)
    self.setWindowTitle('Client')
    self.show()

  def commit(self):
    if len(self.edit.text()):
      text = self.edit.text()
      self.s.sendto(text.encode('utf-8'),('127.0.0.1',self.dst_port))
      self.text.append('本机 [{}]:\n{}\n'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),text))
      self.edit.clear()

  def closeEvent(self,event):
    self.s.close()
    event.accept()

  def recv(self):
    while True:
      data,addr = self.s.recvfrom(1024)
      self.text.append('{}:{}[{}]:\n{}\n'.format(addr[0],addr[1],datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),data.decode('utf-8')))

  def que_ren(self):
    self.src_port = int(self.src_port_edit.text())
    self.dst_port = int(self.dst_port_edit.text())
    #绑定ip地址与端口
    self.s.bind(('127.0.0.1',self.src_port))
    #开启接收消息的线程
    self.t = Thread(target=self.recv,args=())
    self.t.start()

if __name__ == '__main__':
  app = QApplication(sys.argv)
  ex = UI()
  sys.exit(app.exec_())

首先需要输入要传送信息的IP地址,以及端口号,以及设置自己的端口号(IP地址没有用到,我设置了是127.0.0.1),点击确定按钮时触发事件,会为socket绑定端口号,并且创建一个用于接收消息的线程,在点击发送按钮时会触发另一个事件用于发送消息,发送与接收的消息最后会显示在TextEdit上。

注意

这里要统一说明一下,在使用Qt封装后程序会一直循环运行,导致关闭程序时socket也没有关闭(因为我也刚学,不清楚不关闭的后果,可能会占用这个端口一段时间吧),因此需要重写Qt的closeEvent函数,在该函数中进行关闭。

总结

到此这篇关于python Socket网络编程实现C/S模式和P2P的文章就介绍到这了,更多相关python Socket C/S模式和P2P内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python socket C/S结构的聊天室应用实现

    Python socket C/S结构的聊天室应用 服务端: #!/usr/bin/env python #coding:utf8 import socket,select def broadcast_data (sock,message): for socket in conn_list: if socket != server_socket and socket != sock : try : socket.send(message) except : socket.close() conn

  • Python socket套接字实现C/S模式远程命令执行功能案例

    本文实例讲述了Python socket套接字实现C/S模式远程命令执行功能.分享给大家供大家参考,具体如下: 一. 前言 要求: 使用python的socket套接字编写服务器/客户机模式的远程命令执行脚本. serverCmd.py 远程机器上用来执行客户端发送命令的脚本 clientCmd.py 本地机器上,向远程服务器发送命令的脚本 servers.txt  本地机器上,存放所有的远程服务器IP地址文件(仅支持第一个IP) 发送:cmd [command]形式消息,让远程主机执行命令(本

  • python Socket网络编程实现C/S模式和P2P

    C/S模式 由于网络课需要实现Socket网络编程,所以简单实现了一下,C/S模式分别用TCP/IP协议与UDP协议实现,下面将分别讲解. TCP/IP协议 TCP/IP协议是面向连接的,即客户端与服务器需要先建立连接后才能传输数据,以下是服务器端的代码实现. 服务端: import socket from threading import Thread def deal(sock,addr): print('Accept new connection from {}:{}'.format(ad

  • Python socket网络编程TCP/IP服务器与客户端通信

    Python socket网络编程 初学 python,前段时间买了两本书<python 编程从入门到实践><Python 核心编程第三版>,第一本书主要讲的是一些基本语法和一些基本的使用方法,而第二本则深入很多,自己看来也是一知半解,刚好看到了这部分网络编程,依然有好多不太理解的地方,不过想来通过自己不断的摸索,不断地搜寻资料学习,早晚应该会变得通透吧....... 这部分主要使用的模块就是 socket 模块,在这个模块中可以找到 socket()函数,该函数用于创建套接字对象

  • 详解Python Socket网络编程

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ 聊天.收发 email 等等.要解决网络上两台主机之间的进程通信问题,首先要唯一标识该进程,在 TCP/IP 网络协议中,就是通过 (IP地址,协议,端口号) 三元组来标识进程的,解决了进程标识问题,就有了通信的基础了. 本文主要介绍使用Python 进行TCP Socket 网络编程,假设你已经具

  • python socket网络编程之粘包问题详解

    一,粘包问题详情 1,只有TCP有粘包现象,UDP永远不会粘包 你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,其实是先把数据从用户态copy到内核态,这样的操作是耗资源和时间的,频繁的在内核态和用户态之前交换数据势必会导致发送效率降低, 因此socket 为提高传输效率,发送方往往要收集到足够多的数据后才发送一次数据给对方.若连续几次需要send的数据都很少,通常TCP socket 会根据优化算法把这些数据合成一个TCP段

  • python socket网络编程步骤详解(socket套接字使用)

    一.套接字套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象.它们允许程序接受并进行连接,如发送和接受数据.为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要.套接字为BSD UNIX系统核心的一部分,而且他们也被许多其他类似UNIX的操作系统包括Linux所采纳.许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供对套接字的支持.三种最流行的套接

  • python之Socket网络编程详解

    什么是网络? 网络是由节点和连线构成,表示诸多对象及其相互联系.在数学上,网络是一种图,一般认为专指加权图.网络除了数学定义外,还有具体的物理含义,即网络是从某种相同类型的实际问题中抽象出来的模型.在计算机领域中,网络是信息传输.接收.共享的虚拟平台,通过它把各个点.面.体的信息联系到一起,从而实现这些资源的共享.网络是人类发展史来最重要的发明,提高了科技和人类社会的发展. 网络通信的三要素 IP地址 用来表示一台独立的主机 特殊的IP地址 127.0.0.1或称localhost(表示本地回环

  • Pythony运维入门之Socket网络编程详解

    Socket是什么? Socket 是电脑网络中进程间数据流的端点Socket 是操作系统的通信机制应用程序通过Socket进行网络数据的传输 首先,简单了解一下TCP通信过程: TCP三次握手(面试常考): 第一次握手:客户端 发送SYN报文,设置随机数序号X,服务器由SYN=1知道,客户端要求建立联机 第二次握手:服务器端接收到客户端的报文之后,经过处理,返回给客户端SYN+ACK报文,同时设置随机序号Y,此时返回的报文确认ACK=X+1 第三次握手:接收到报文的客户端,会在处理确认之后,再

  • Python简单网络编程示例【客户端与服务端】

    本文实例讲述了Python简单网络编程.分享给大家供大家参考,具体如下: 内容目录 1. 客户端(client.py) 2. 服务端(server.py) 一.客户端(client.py) import socket import sys port = 70 host = sys.argv[1] filename = sys.argv[2] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port))

  • C++中Socket网络编程实例详解

    C++中Socket网络编程实例详解 现在几乎所有C/C++的后台程序都需要进行网络通讯,其实现方法无非有两种:使用系统底层socket或者使用已有的封装好的网络库.本文对两种方式进行总结,并介绍一个轻量级的网络通讯库ZeroMQ.  1.基本的Scoket编程 关于基本的scoket编程网络上已有很多资料,作者在这里引用一篇文章中的内容进行简要说明. 基于socket编程,基本上就是以下6个步骤: 1.socket()函数 2.bind()函数 3.listen().connect()函数 4

  • iOS socket网络编程实例详解

    代码下载 服务端代码下载地址 客户端代码下载地址 相关概念 socket是一个针对TCP和UDP编程的接口,你可以借助它建立TCP连接等.socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议.Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口. socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦

随机推荐