python网络编程

@[toc]

网络结构

  • ISP
    • Internet Service Provider
    • 一个或多个分组交换机在多段通信链路组成的网络
    • 提供不同类型的网络接入
    • 独立运营
  • ISP分层
    • 第一层:因特网主干
      Sprint,AT&T,Level3
    • 第二层:具有区域性或国家性覆盖规模,引导流量通过,也可以跟其他第二层ISP交换流量
      Provider-custoner
      Peer-peer
    • 较低层:通过一个或多个第二层ISP与更大的因特网连接

网络结构

  • 接入点POP
    ISP与ISP的连接点,由一个或多个路由器组成
  • 网络接入点NAP
    由第三方通信公司或因特网主干提供
  • 接入方式


  • Socket
    应用层与TCP/IP协议族通信的中间软件抽象层
  • TCP socket编程
函数 作用
AF_INET 使用IPv4协议
AF_INET6 使用IPv6协议
SOCK_STREAM 指定使用面向流的TCP协议
bind() 绑定端口,端口号不小于1024
listen() 监听窗口,并指定等待连接的最大数量
accept() 等待并返回一个客户端连接
connect() 主动连接服务端
recv() 接收TCP数据
send() 发送TCP数据

tcpserver.py文件,服务器端启动,监听用户端发起的连接
tcpclient.py文件,用户端启动,发起对服务器的连接
连接之后相互之间可以通话,但同一时间只能保证一条连接
当还有其他用户连接时只能等待,当前一个用户断开连接之后,下一个用户才能继续连接。

#tcpserver.py
import sys
import os
from socket import *
import time
import random

HOST='127.0.0.1'

RSIZE=1024

class TCPServer:#非多线程,只能响应一个client
    
    def __init__(self,port,maxconnections=5):
        self._port=port
        self._maxconnections=maxconnections
        self._server=socket(AF_INET, SOCK_STREAM)
    
    def start(self):
        self._server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#端口释放后马上可以被重新使用
        self._server.bind((HOST,self._port))
        self._server.listen(self._maxconnections)
        print("SERVER is listening on %s" % self._port)
        while True:#如果有多个client同是发起连接,只响应一个,其他会等待
            conn,addr=self._server.accept()#block
            print(f"client's connection: {conn}, its address:{addr}")
            while True:
                try:
                    data=conn.recv(RSIZE)
                    if not data:
                        break
                    print("CLIENT: %s" % data.decode('utf-8'))
                    if data.decode('utf-8')=='bye':
                        conn.send("再见!".encode('utf-8'))
                        break
                    else:
                        conn.send('收到!'.encode('utf-8'))
                except Exception as e:
                    print("SERVER ERROR: %s" % e)
                    break
            conn.close()
        self._server.close()

def main():
    ser=TCPServer(int(sys.argv[1]))
    #ser = TCPServer(2021)
    ser.start()

if __name__=='__main__':
    main()
#tcpclient.py
import os
import sys
import time
import random
from socket import *

from tcpserver import TCPServer

class TCPClient:
    def __init__(self,server_ip,server_port):
        self._server_ip=server_ip
        self._server_port=server_port
        self._client=socket(AF_INET,SOCK_STREAM)

    def start(self):
        self._client.connect((self._server_ip,self._server_port))
        while True:
            msg=input("CLIENT:")
            self._client.send(msg.encode('utf-8'))
            data=self._client.recv(1024)
            if not data:
                continue
            if(data.decode('utf-8')=='再见'):
                print("结束连接")
                break
            else:
                print("SERVER: %s" % data.decode('utf-8'))
        self._client.close()

def main():
    client=TCPClient(sys.argv[1],int(sys.argv[2]))
    #client = TCPClient('127.0.0.1',2021)
    client.start()

if __name__=='__main__':
    main()



使用多线程模拟多人聊天

#mtserver.py
import sys
import os
from threading import Thread
from socket import *

BUFFERS=1024
MAXC=64

def speak(name,conn):
    print("欢迎{}进入聊天室...".format(name))
    while True:
        try:
            msg=conn.recv(BUFFERS)
            if not msg:
                break
            print("{}:{}".format(name,msg.decode('utf-8')))
            if msg.decode('utf-8')=='byebye':
                print("{}离开了聊天室...".format(name))
                break
        except Exception as e:
            print("server error %s" % e)
            break
    conn.close()

if __name__=='__main__':
    ip=sys.argv[1]
    port=int(sys.argv[2])
    server=socket(AF_INET,SOCK_STREAM)
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#端口释放后马上可以被重新使用
    server.bind((ip,port))
    server.listen(MAXC)
    print("MT server is started...")
    while True:
        conn,addr=server.accept()#block
        ci,cp=addr
        #启动一个线程处理该连接,主线程继续处理其他连接
        t=Thread(target=speak,args=("client-"+ci+"-"+str(cp),conn))
        t.start()
    server.close()
#mtclient.py
import sys
from socket import *
from threading import Thread
import random
import time

CHARS='abcdefghijklmnopqrstuvwxyz'
ROUND=8

class ChatBot(Thread):
    
    def __init__(self,ip,port,mlength):
        super().__init__()
        self._ip=ip
        self._port=port
        self._max_msg_length=mlength

    def _random_message(self):
        message=[]
        for i in range(self._max_msg_length):
            word=[]
            for j in range(random.randint(1,5)):
                word.append(random.choice(CHARS))
            message.append(''.join(word))
        return ' '.join(message)
    
    def run(self):
        _client=socket(AF_INET,SOCK_STREAM)
        _client.connect((self._ip,self._port))
        _sent_count=0
        while True:
            msg=self._random_message()
            _client.send(msg.encode('utf-8'))
            _sent_count+=1
            time.sleep(random.randint(1,5))
            if _sent_count>ROUND:
                _client.send('byebye'.encode('utf-8'))
                break
        _client.close()

def main():
    ip=sys.argv[1]
    port=int(sys.argv[2])
    tlist=[]
    for i in range(int(sys.argv[3])):
        tlist.append(ChatBot(ip,port,random.randint(4,8)))
    for t in tlist:
        t.start()
    for t in tlist:
        t.join()
    print("All bots exit...")

if __name__=='__main__':
    main()

在同一个程序里面同时实现server和client的功能,需要多加一个参数指定是server还是client。

#schat.py
from socket import *
from threading import Thread
import sys

BS=2048

def recv(name,conn):
    while True:
        data=conn.recv(BS)
        if not data:
            break
        if data.decode('utf-8')=='BYEBYE':
            print("对方提议停止聊天,输入BYEBYE可终止...")
            break
        else:
            print("%s: %s" % (name,data.decode('utf-8')))

def send(conn):
    while True:
        msg=input("")
        if not msg:
            continue
        conn.send(msg.encode('utf-8'))
        if msg=='BYEBYE':
            break

if __name__=='__main__':
    if len(sys.argv)!=4:
        print('usage: python schat.py ip port server|client')
    else:
        ip=sys.argv[1]
        port=int(sys.argv[2])
        name=sys.argv[3]#server, client
        server=socket(AF_INET,SOCK_STREAM)
        if name=='server':
            server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
            server.bind((ip,port))
            server.listen(5)
            print("server started...")
            conn,addr=server.accept()
        if name=='client':
            server.connect((ip,port))
            print('client connected...')
            addr=(ip,port)
            conn=server
        tr=Thread(target=recv,args=(str(addr),conn))
        tr.start()
        ts=Thread(target=send,args=(conn,))
        ts.start()
        tr.join()
        ts.join()
        conn.close()
        server.close()

HTTP

http.server

– Defines classes for implementing HTTP servers (Web
servers)
HttpServer/ThreadingHttpServer: creates and listens at the HTTP socket, dispatching the requests to a handler
• Handler: BaseHTTPRequestHandler,
SimpleHTTPRequestHandler

http.client

– Defines classes which implement the client side of the HTTP and HTTPS protocols
– It is normally not used directly
urllib.request uses it to handle URLs that use HTTP and HTTPS

UDP的优势

  • 应用层能更好地控制要发送的数据和发送时间
  • 无需连接建立
  • 不需要任何准备即可进行数据传输
  • 不引入建立连接的时延
  • 无连接状态
  • 无需维护一些状态参数
  • 分组头部开销小,TCP头至少占20字节,UDP头占8个字节
#udpserver.py
import sys
import os
from socket import *
import time
import random

HOST='127.0.0.1'

RSIZE=1024

class TCPServer:#非多线程,只能响应一个client
    
    def __init__(self,port,maxconnections=5):
        self._port=port
        self._maxconnections=maxconnections
        self._server=socket(AF_INET, SOCK_STREAM)
    
    def start(self):
        self._server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)#端口释放后马上可以被重新使用
        self._server.bind((HOST,self._port))
        self._server.listen(self._maxconnections)
        print("SERVER is listening on %s" % self._port)
        while True:#如果有多个client同是发起连接,只响应一个,其他会等待
            conn,addr=self._server.accept()#block
            print(f"client's connection: {conn}, its address:{addr}")
            while True:
                try:
                    data=conn.recv(RSIZE)
                    if not data:
                        break
                    print("CLIENT: %s" % data.decode('utf-8'))
                    if data.decode('utf-8')=='bye':
                        conn.send("再见!".encode('utf-8'))
                        break
                    else:
                        conn.send('收到!'.encode('utf-8'))
                except Exception as e:
                    print("SERVER ERROR: %s" % e)
                    break
            conn.close()
        self._server.close()

def main():
    ser=TCPServer(int(sys.argv[1]))
    #ser = TCPServer(2021)
    ser.start()

if __name__=='__main__':
    main()
#udpclient.py
import os
import sys
import time
import random
from socket import *

from tcpserver import TCPServer

class TCPClient:
    def __init__(self,server_ip,server_port):
        self._server_ip=server_ip
        self._server_port=server_port
        self._client=socket(AF_INET,SOCK_STREAM)

    def start(self):
        self._client.connect((self._server_ip,self._server_port))
        while True:
            msg=input("CLIENT:")
            self._client.send(msg.encode('utf-8'))
            data=self._client.recv(1024)
            if not data:
                continue
            if(data.decode('utf-8')=='再见'):
                print("结束连接")
                break
            else:
                print("SERVER: %s" % data.decode('utf-8'))
        self._client.close()

def main():
    client=TCPClient(sys.argv[1],int(sys.argv[2]))
    #client = TCPClient('127.0.0.1',2021)
    client.start()

if __name__=='__main__':
    main()

SMTP

  • Simple Mail Transfer Protocol
  • 从发送方的邮件服务器向接收方的邮件服务器发送邮件
  • 运行在发送方邮件服务器的客户机端和接收方邮件服务器的服务器端
  • MIME:多用途互联网邮件扩展

POP3

  • 与服务器建立连接(默认是110端口)
  • 认证——事务处理——更新
  • 未给用户提供任何创建远程文件夹以及为报文指派文件夹的方法

IMAP

  • 互联网邮件访问协议
  • 创建文件夹并在文件夹之间移动邮件
  • 允许只读取报文头部
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 网络 理论模型,分为七层物理层数据链路层传输层会话层表示层应用层 实际应用,分为四层链路层网络层传输层应用层 IP...
    FlyingLittlePG阅读 4,272评论 0 0
  • TCP/IP简介 计算机为了联网,就必须规定通信协议,早起的计算机网络。就是由各厂商自己规定一套协议,IBM、Ap...
    蓓蓓的万能男友阅读 5,464评论 0 2
  • Python是很强大的网络编程工具。Python有很多针对常见网络协议的库,这些库可以使我们集中精力在程序的逻辑处...
    泷汰泱阅读 1,488评论 0 0
  • 网络中的术语解释 TCP和UDP的区别 是否连接: TCP面向连接(发送数据之前需要建立连接,三次握手).UDP面...
    莫辜负自己的一世韶光阅读 4,055评论 0 2
  • 一、基础知识 1、名词缩写 TCP(Transmission Control Protocol)传输控制协议 IP...
    ququququ阅读 6,017评论 0 3