Python从入门到进阶(十八)——网络编程

一 网络编程

  1. 网络编程基础:
  • Sockets(套接字)可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器。
  • Socket主要是使用IP地址,协议,端口号来标识一个进程。端口号的范围为0~65535(用户端口号一般大于1024),协议有很多种,经常用到的就是TCP,IP,UDP等;
  1. python中的内置库socket
  • python中的socket库提供了处理公共传输的特定类,以及一个用于处理其余部分的通用接口。
  • socket是内置库,无需安装;
  1. 导入方式:
import socket
  1. 使用socket
创建服务端 客户端
import socket
    # 先启动服务端,再启动客户端,实现简单通信
class SocketSv(object):
    def ServerStart(self,ip="127.0.0.l",port=18080):
        print('服务端启动')
        # 创建Tcp/Ip套接字对象
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 流式Socket
        # 创建Udp/Ip套接字对象
        # s=socket.socket(socket.AF_NET,socket.SOCK_DGRAM) # 数据报式Scoket
        s.bind(('127.0.0.1',18080)) # 绑定地址
        s.listen(3) # 建立最多三个连接
        while True:
            conn,addr=s.accept()    # 等待客户端连接
            print('欢迎{}'.format(addr))  # 打印访问的用户信息
            while True:
                data=conn.recv(1024)
                dt=data.decode('utf-8') # 接受一个1024字节的数据
                print('服务端收到:',dt)
                aa=input('服务端发送:')
                if(aa=='quit'):
                    conn.close() # 关闭来自客户端的连接
                    s.close() # 关闭服务器端连接
                else:
                    conn.send(aa.encode('utf-8')) # 发送数据
    
    def ClientStart(self,Ip='127.0.0.1',port=18080):
        import sys
        print('客户端启动')
        c=socket.socket() # 创建socket对象
        c.connect((Ip,port)) # 跟服务端建立连接
        print(c.getpeername()) # 获取远程的地址
        while True:
            ab=input('客户端发送:')
            if ab=='quit':
                c.close() # 关闭客户端连接
                sys.exit(0)
            else:
                c.send(ab.encode('utf-8')) # 发送数据
                data=c.recv(1024) # 接受一个1024字节的数据
                print('客户端收到:',data.decode('utf-8')) # 输出接受的信息
        
    pass
# 服务端
PS G:\BaiduNetdiskDownload\VS2019Space\PythonApplication1> python
Python 3.11.4 (tags/v3.11.4:d2340ef, Jun  7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from Jbjn import SocketSv
>>> a=SocketSv.SocketSv()
>>> a.ServerStart()
服务端启动
欢迎('127.0.0.1', 51885)
服务端收到: This is a good time.
服务端发送:Yes,Welcome to Chat.
服务端收到: Oh,It's so cool.

# 客户端
>>> from Jbjn import SocketSv
>>> b=SocketSv.SocketSv()
>>> b.ClientStart()
客户端启动
('127.0.0.1', 18080)
客户端发送:This is a good time.
客户端收到: Yes,Welcome to Chat.
客户端发送:Oh,It's so cool.
  1. 以上只能实现单一通信,发送后必须等待接收,实现群聊的需要使用到多线程
使用UDP通信
创建群聊服务端
 # ServerMany服务器
    def serverMany(self,ip='127.0.0.1',port=9999):
        try:
            s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 创建socket对象 数据报式Socket
            addr=(ip,port)
            s.bind(addr) # 绑定地址和端口
            print('服务端启动,地址:',addr)
            user={} # 存放字典{addr:name}
            while True:
                try:
                    data,addr=s.recvfrom(1024) # 等待接受客户端消息存放在2个变量data和addr里
                    if not addr in user: # 如果addr不在user字典里则执行以下代码
                        for address in user: # 从user遍历数据出来address
                            s.sendto(data+'Enter ChattingRoom...'.encode('utf-8'),address) # 发送user字典的data和address到客户端
                        user[addr]=data.decode('utf-8') # 接受的消息解码成utf-8并存在字典user里,键名定义为addr
                        continue # 如果addr在user字典里,跳过本次循环

                    if 'EXIT'.lower() in data.decode('utf-8'): # 如果EXIT在发送的data里
                        name=user[addr] # user字典addr键对应的值赋值给变量name
                        user.pop(addr) # 删除user里的addr
                        for address in user: # 从user取出address
                            s.sendto((name +'离开了ChattingRoom...').encode(),address) # 发送name和address到客户端
                    else:
                        print(f'{data.decode("utf-8")} from {addr[0]}:{addr[1]}')
                        for address in user: # 从user遍历出address
                            if address!=addr: # address不等于addr时间执行下面的代码
                                s.sendto(data,address) # 发送data和address到客户端
                except ConnectionResetError:
                    print('服务端异常')
        except Exception as e:
            s.close()
            print(str(e))
    # 创建客户端;
    name='username'

    def recv(self,sock,addr):
        '''
        一个UDP连接在接收消息之前必须要让系统知道所占端口
        也就是需要send一次,否则win下会报错
        '''
        sock.sendto(name.encode('utf-8'),addr)
        while True:
            data=sock.recv(1024)
            if data:
                print(data.decode('utf-8'))
        
    def send(self,sock,addr):
        '''
            发送数据的方法
            参数:
                sock:定义一个实例化socket对象
                server:传递的服务器IP和端口
        '''
        while True:
            string=input('正在输入:')
            message=name+':'+string
            if message:
                data=message.encode('utf-8')
                sock.sendto(data,addr)
                if string.lower()=='EXIT'.lower():
                    break
    
    def main(self,ip='127.0.0.1',port=9999):
        import threading
        global name
        name=input('请输入你的名称:')
        '''
            主函数执行方法,通过多线程来实现多个客户端之间的通信
        '''
        s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        server=(ip,port)
       
        tr=threading.Thread(target=self.recv,args=(s,server),daemon=True)
        ts=threading.Thread(target=self.send,args=(s,server))
        
        tr.start()
        ts.start()
        tr.join()
        ts.join()
        s.close()
        pass
    pass

main()
G:\BaiduNetdiskDownload\VS2019Space\PythonApplication1>python
Python 3.11.4 (tags/v3.11.4:d2340ef, Jun  7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from Jbjn import SocketSv
>>> a=SocketSv.SocketSv()
>>> a.serverMany()
服务端启动,地址: ('127.0.0.1', 9999)
Mr.Bai:xiawuhao! from 127.0.0.1:60931
Mr.Li:Hello! from 127.0.0.1:50487
Mr.Bai:Who are you? from 127.0.0.1:60931
Mr.Li:I'm Mr.Li,And you? from 127.0.0.1:50487
Mr.Bai:I'm Mr.Bai.Nice to meet you. from 127.0.0.1:60931
Miss.Yun:Can I see history messages? from 127.0.0.1:56873
Mr.Bai:Yes,I think you can. from 127.0.0.1:60931
Mr.Li:It's cool. from 127.0.0.1:50487
Miss.Yun:It's time to go home. from 127.0.0.1:56873
Mr.Li:Let's go. from 127.0.0.1:50487
Mr.Bai:Qiu dou ma dai. from 127.0.0.1:60931
  1. 编写一个简易的 ECHO 服务机器人。满足:
  • 客户端发送文本,服务端回复同样的文本,两边都打印各自收到的文本。
  • 客户端支持用户输入’q’ 退出。
  • 客户端支持每次不大于 140 的文本输入,如果超出则提示重新输入。
  • 客户端必须正确打印字符串。
编写一个简易的 ECHO 服务机器人。满足:

客户端发送文本,服务端回复同样的文本,两边都打印各自收到的文本。
客户端支持用户输入'q' 退出。
客户端支持每次不大于 140 的文本输入,如果超出则提示重新输入。
客户端必须正确打印字符串。
# -*- coding: UTF-8 -*-
import socket
import sys

def echo_server(host, port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((host, port))
        s.listen()
        conn, addr = s.accept()
        with conn:
            print('收到客户端请求:', addr)
            while True:
                data = conn.recv(1024)
                print('收到客户端数据:', str(data, 'utf8'))
                if not data:
                    break
                conn.sendall(data)

def echo_client(host, port, input_message):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((host, port))

        while True:
    		msg = input_message("请输入信息[按q退出]:")
    	if msg == 'q':
        	break

    	if len(msg) > 140:
        	print("您的输入太长了~~,请重新输入!")
        continue

    	msg_buffer = bytes(msg, 'utf-8')
    	s.sendall(msg_buffer)

    	data = s.recv(1024)
    	print('收到服务端回包:', str(data, 'utf8'))
        
        while True:
            msg = input_message("请输入信息[按q退出]:")
            data = ...
            print('收到服务端回包:', str(data, 'utf8'))

if __name__ == '__main__':
    host = '127.0.0.1'
    port = 9999

    # 如果命令行指定了 'server' 参数,就启动服务端程序
    # 否则,启动客户端程序
    if len(sys.argv) > 1 and sys.argv[1] == 'server':
        echo_server(host, port)
    else:
        echo_client(host, port, lambda tip: input(tip))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值