【学习笔记】Socket

Socket

udp收发

import socket

# family:
# socket.AF_UNIX == 只能够用于单一的Unix系统进程间通信
# socket.AF_INET == 服务器之间网络通信
# socket.AF_INET6 == IPv6

# type:
# socket.SOCK_STREAM == 流式socket , for TCP
# socket.SOCK_DGRAM == 数据报式socket , for UDP
# socket.SOCK_RAW == 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文

# 创建socket
family = socket.AF_INET
type_ = socket.SOCK_DGRAM
s = socket.socket(family, type_)

# 发送数据到指定ip地址的指定端口
data = 'hello'.encode('utf8')
ip_address = '0.0.0.0'
port = 9099
address = (ip_address, port)
s.sendto(data, address)

# 关闭socket
s.close()

import socket

# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 在指定端口接收数据,ip地址为本机地址
ip_address = '0.0.0.0'
port = 9099
address = (ip_address, port)
s.bind(address)

# 接收数据
data, (from_ip, from_port) = s.recvfrom(1024)
print('从{}地址{}端口号接受到消息,内容是{}'.format(from_ip, from_port, data.decode('utf8')))

# 关闭socket
s.close()

tcp收发

import socket

# 创建socket
family = socket.AF_INET
type_ = socket.SOCK_STREAM
s = socket.socket(family, type_)

# 绑定端口
ip_address = '0.0.0.0'
port = 9099
address = (ip_address, port)
s.bind(address)

# 把socket变成一个被动监听的socket
s.listen(128)

# 接收数据
client_socket, (client_ip, client_port) = s.accept()
data = client_socket.recv(1024)
print(data.decode('utf8'))

# 关闭socket
s.close()

import socket

# 创建socket
family = socket.AF_INET
type_ = socket.SOCK_STREAM
s = socket.socket(family, type_)

# 在发送数据之前,先与服务器进行连接
ip_address = '0.0.0.0'
port = 9099
address = (ip_address, port)
s.connect(address)

# 发送数据
data = 'hello'.encode('utf8')
s.send(data)

# 关闭socket
s.close()

文件收发

import socket
import os

# 创建socket
family = socket.AF_INET
type_ = socket.SOCK_STREAM
s = socket.socket(family, type_)

# 绑定端口
ip_address = '0.0.0.0'
port = 9099
address = (ip_address, port)
s.bind(address)

# 把socket变成一个被动监听的socket
s.listen(128)

# 接收文件名
client_socket, (client_ip, client_port) = s.accept()
file_name = client_socket.recv(3000).decode('utf8')

if os.path.isfile(file_name):
    with open(file_name, 'rb') as f:
        content = f.read()
        client_socket.send(content)
else:
    print('文件不存在!')

# 关闭socket
s.close()

import socket

# 创建socket
family = socket.AF_INET
type_ = socket.SOCK_STREAM
s = socket.socket(family, type_)

# 在发送数据之前,先与服务器进行连接
ip_address = '0.0.0.0'
port = 9099
address = (ip_address, port)
s.connect(address)

# 发送请求的文件名
file_name = input('Please input file name: ')
s.send(file_name.encode('utf8'))

# 接收文件
save_name = 'receive.' + file_name.split('.')[1]
with open(save_name, 'wb') as f:
    while True:
        content = s.recv(1024)
        if not content:
            break
        f.write(content)

# 关闭socket
s.close()

多线程收发

import socket
import time
import threading

# 创建socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
my_port = 8099
address = ('0.0.0.0', my_port)
s.bind(address)


def send_msg(send_port=9099):
    while True:
        msg = input()
        print('{}: {}'.format(my_port, msg))
        s.sendto(msg.encode('utf8'), ('0.0.0.0', send_port))
        time.sleep(1)


def recv_msg():
    while True:
        data, (_, port) = s.recvfrom(1024)
        print('{}: {}'.format(port, data.decode('utf8')))
        time.sleep(1)


thread1 = threading.Thread(target=send_msg)
thread2 = threading.Thread(target=recv_msg)

thread1.start()
thread2.start()


线程锁

线程锁能防止多个线程同时对一个全局变量进行操作

import threading
import time

ticket = 20
lock = threading.Lock()


def sell_ticket():
    global ticket
    while True:
        lock.acquire()
        if ticket > 0:
            ticket -= 1
            print('{}卖出一张票, 还剩{}张票'.format(threading.current_thread().name, ticket))
            lock.release()
            time.sleep(0.3)
        else:
            lock.release()
            print('票卖完了')
            break


t1 = threading.Thread(target=sell_ticket, name='线程1')
t2 = threading.Thread(target=sell_ticket, name='线程2')

t1.start()
t2.start()

服务器搭建

import socket


class MyServer(object):
    def __init__(self, ip, port):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind((ip, port))
        self.socket.listen(128)

    def run_forever(self):
        while True:
            client_socket, client_addr = self.socket.accept()

            data = client_socket.recv(1024).decode('utf8')
            print('接受到来自{}:{}的请求'.format(client_addr[0], client_addr[1]))
            path = data.splitlines()[0].split(' ')[1]
            print('请求的路径为{}'.format(path))

            response_header = 'HTTP/1.1 200 OK\n'
            if path == '/login':
                response_body = '<h1 style="color:green">欢迎来到登录界面</h1>'
            elif path == '/register':
                response_body = '<h1 style="color:blue">欢迎来到注册界面</h1>'
            elif path == '/':
                response_body = '<h1 style="color:black">欢迎来到首页</h1>'
            else:
                response_header = 'HTTP/1.1 404 Page Not Found\n'
                response_body = '<h1 style="color:red">您要查找的页面不存在!!!</h1>'

            response_header += 'Content-Type: text/html;charset=utf-8\n' + '\n'
            response = response_header + response_body

            client_socket.send(response.encode('utf8'))


server = MyServer('0.0.0.0', 9090)
server.run_forever()

"""
---- Request Header ----

HTTP/1.1(响应采用的协议和版本号) 200(状态码) OK(描述信息)
Location: http://www.baidu.com(服务端需要客户端访问的页面路径) 
Server:apache tomcat(服务端的Web服务端名)
Content-Encoding: gzip(服务端能够发送压缩编码类型) 
Content-Length: 80(服务端发送的压缩数据的长度) 
Content-Language: zh-cn(服务端发送的语言类型) 
Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
Refresh: 1;url=http://www.it315.org(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
Transfer-Encoding: chunked(分块传递数据到客户端)  
Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
Expires: -1//3种(服务端禁止客户端缓存页面数据)
Cache-Control: no-cache(服务端禁止客户端缓存页面数据)  
Pragma: no-cache(服务端禁止客户端缓存页面数据)   
Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)  
Date: Tue, 11 Jul 2000 18:23:51 GMT(服务端响应客户端的时间)

HTTP/1.1 200 OK
Bdpagetype: 2
Bdqid: 0xe8da5893001926cb
Cache-Control: private
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Sun, 14 Jun 2020 16:03:45 GMT
Expires: Sun, 14 Jun 2020 16:03:44 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=522; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=1457_31670_21085_31069_32046_31714_30823_26350; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1592150625018778983416778820750107289291
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
"""

"""
---- Response Header ----

GET(请求的方式) /newcoder/hello.html(请求的目标资源) HTTP/1.1(请求采用的协议和版本号)
Accept: */*(客户端能接收的资源类型) 
Accept-Language: en-us(客户端接收的语言类型) 
Connection: Keep-Alive(维护客户端和服务端的连接关系) 
Host: localhost:8080(连接的目标主机和端口号) 
Referer: http://localhost/links.asp(告诉服务器我来自于哪里) 
User-Agent: Mozilla/4.0(客户端版本号的名字) 
Accept-Encoding: gzip, deflate(客户端能接收的压缩数据的类型) 
If-Modified-Since: Tue, 11 Jul 2000 18:23:51 GMT(缓存时间)  
Cookie(客户端暂存服务端的信息) 
Date: Tue, 11 Jul 2000 18:23:51 GMT(客户端请求服务端的时间)

GET / HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763
Accept-Encoding: gzip, deflate
Host: 192.168.1.14:9090
Connection: Keep-Alive
"""

WSGI服务器

from wsgiref.simple_server import make_server


def load_file(path, mode='r', encode=None):
    try:
        with open('html' + path, mode=mode, encoding=encode) as f:
            file = f.read()
            return file
    except FileNotFoundError as e:
        raise e


def show_login():
    return '<h1 style="color:green">欢迎来到登录界面</h1>'


def show_register():
    return '<h1 style="color:blue">欢迎来到注册界面</h1>'


def show_html(path):
    if path.endswith('.jpg'):
        return load_file(path, mode='rb')
    else:
        return load_file(path, encode='utf8')


url = {
    '/login': show_login,
    '/register': show_register,
}


def demo_app(environment, start_response):
    """
    status code:
    1**	信息,服务器收到请求,需要请求者继续执行操作
    2**	成功,操作被成功接收并处理
    3**	重定向,需要进一步的操作以完成请求
    4**	客户端错误,请求包含语法错误或无法完成请求
    5**	服务器错误,服务器在处理请求的过程中发生了错误
    """
    """
    用来处理用户的请求
    :param environment: 一个字典,存储用户环境
    :param start_response: 是一个函数,用来返回response header
    :return: 返回给浏览器的数据
    """
    path = environment['PATH_INFO']  # 用户请求的路径
    path = '/index.html' if path == '/' else path

    status_code = '200 OK'
    method = url.get(path)
    if method:
        response_body = method()
    elif path.endswith('.jpg') or path.endswith('.html'):
        response_body = show_html(path)
    else:
        status_code = '404 Page Not Found'
        response_body = '<h1 style="color:red">您要查找的页面不存在!!!</h1>'

    start_response(status_code, [('Content-Type', 'text/html;charset=utf-8')])
    if not path.endswith('.jpg'):
        response_body = response_body.encode('utf8')
    return [response_body]


if __name__ == '__main__':
    with make_server('0.0.0.0', 9090, demo_app) as http:
        sa = http.socket.getsockname()
        print('Serving HTTP on {}, port {} ...'.format(sa[0], sa[1]))

        # 打开浏览器,输入访问地址
        # import webbrowser
        # webbrowser.open('http://localhost:9090/xyz?abc')

        # http.handle_request() # 处理一次请求
        http.serve_forever()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值