socket网络编程

使用socket可以实现网络通信,告别单机脚本

1.socket的简单认识

服务端

import socket

#创建socket对象
server = socket.socket()

#绑定ip和端口
server.bind(("128.0.0.1",8000))

#表示可以接受客户端的个数
server.listen(5)  #最多可以接受5个客户端

#等待客户端的连接,如果没人连就阻塞
#如果客户端连接,获取到客户端的连接进行通信
#conn是客户端与服务端连接的中间体对象,address是客户端的地址信息
conn,address = server.accept()

#通过连接对象去获取信息
data = conn.recv(1024)
print(data)

#给客户端发送信息
conn.send(b"stop")

#与客户端断开连接
conn.close()

#关闭服务端
server.close()

客户端

#client中的阻塞:
#client.connect()
#conn.recv()

import socket
client = socket.socket()

#客户端发起连接请求
#阻塞,一直等待连接成功,连接成功后才会继续往下走
client.connect(("128.0.0.1",8000))

#向服务端发信息
client.send(b'hello')

#接受服务端返回的信息
data = client.recv(1024)
print(data)

#完成服务,关闭客户端
client.close()

2.实现登录判断

服务端

#根据本地的csv文件中保存的账号和密码对客户端输入的用户名和密码进行比对,然后判断是否登录成功
import csv
import socket

def login(content_lst):
    with open("uid&upd.csv","r",encoding="UTF-8") as f:
        reader = csv.reader(f)
        for content in reader:
            if content_lst == content:
                status = "200"
                return status
            else:
                 status = "400"
        return status

server = socket.socket()
server.bind(("192.168.0.108",8001))
server.listen(5)
while 1:
    conn,address = server.accept()
    while 1:
        response = conn.recv(1024).decode("UTF-8")
        if response == "exit":
            break
        content_lst = response.strip().split(",")  #对客户端发送来的信息先去空格,再转化为list
        status = login(content_lst)   #调用登录函数,判断状态码
        conn.send(status.encode("UTF-8"))  #把状态码发送给客户端
    conn.close()

客户端

#让用户输入账号和密码,传给服务端进行登录
import socket
chance = 3  #用户可以输入的次数
sk = socket.socket()
sk.connect(("128.0.0.1",8001))
while chance > 0:
    uname = input("请输入用户名:")
    upassword = input("请输入密码:")
    msg = uname + "," + upassword
    sk.send(msg.encode("UTF-8"))   #将用户输入信息发送给服务端
    response = sk.recv(1024)   #拿到服务端给的状态码
    if response.decode("UTF-8") == "200":   #根据状态码判断登录状态
        print("登陆成功")
        break
    else:
        print("用户名或密码错误")
    chance -= 1
    print("你还有{}次机会".format(chance))
sk.send("exit".encode("UTF-8"))
sk.close()

3.使用struct解决黏包问题

黏包问题的本质:接收方发送方发送数据的分界线
解决黏包问题:
发送端:1.构建报头(数据分界线) 2.发送报头 3.发送数据
接收端:1.接收报头 2.根据报头接受数据
黏包的类型:1.数据连续发送,接收方无法分别 2.数据过大,接受方接受不完,服务方又继续发送

服务端

#根据客户端输入的内容返回特定的内容
import subprocess
import socket
import struct
server = socket.socket()
server.bind(("192.168.0.108",8001))
server.listen(5)
while 1:
    conn,address = server.accept()
    print("server is working")
    while 1:
        try :
            response = conn.recv(1024).decode("UTF-8")
            print(response)
            if response == "exit":
                break
            res = subprocess.Popen(response,
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE)
            out = res.stdout.read()
            err = res.stderr.read()
            if err:
                header = struct.pack("i",len(err))  #构建报头
                conn.send(header)  #发送报头
                conn.send(err)      #发送数据
            else:
                header = struct.pack("i",len(out))  #构建报头
                conn.send(header)      #发送报头
                conn.send(out)       #发送数据
        except Exception as e:
            print(e)
            break
    conn.close()

客户端

import socket
import struct
sk = socket.socket()
sk.connect(("192.168.0.108",8001))
while 1:
    cmd = input("请输入命令:")
    if cmd == "":
        continue
    if cmd == "exit":
        break
    sk.send(cmd.encode("UTF-8"))
    header = sk.recv(4)  #获取报头
    length = struct.unpack("i",header)[0]  #将报头解析出来
    data_length = 0
    response = b''
    while data_length < length:   #接受数据
        data = sk.recv(1024)
        response += data
        data_length += len(data)
    print(response.decode("GBK"))
sk.close()

4.基于socket实现简单的文件上传和下载

服务端

#借助struct来解决黏包

import socket
import json
import struct
import hashlib
import os
from 网络编程.FTP import verify
server = socket.socket()
server.bind(("127.0.0.1",8080))
server.listen(5)

while 1:
    conn,address = server.accept()
    print("server is working...")

    #前四个字节是报头的长度
    #获取报头长度之后就可以获取报头
    #获取报头之后就能获取报头中的文件大小信息
    while 1:
        try :
            info_lst = conn.recv(1024).decode("UTF-8").split(",")
            type = info_lst[0]
            if type == "put": #上传文件,服务端接受
                md5 = hashlib.md5()
                headers_len_byte = conn.recv(4)
                print(headers_len_byte)
                headers_len = struct.unpack("i",headers_len_byte)[0]   #将请求头长度解包
                print(headers_len)
                headers = json.loads(conn.recv(headers_len).decode("UTF-8"))  #将请求头反格式化
                print(headers)
                type = headers.get("type")
                file_name = headers.get("file_name")
                file_size = headers.get("file_size")

                recv_data_size = 0
                with open(file_name,"wb") as f:  #接受数据
                    while recv_data_size < file_size:
                        data = conn.recv(1024)
                        f.write(data)
                        recv_data_size += len(data)
                        print("文件大小:{},已接受:{}".format(file_size,recv_data_size))
                print("接收成功")
                hex = conn.recv(1024).decode("UTF-8")
                if hex == verify.jiaoyan(file_name):
                    print("数据完整")
                else:
                    print("数据有丢失")

            else:  #下载文件,服务端发送

                file_name = info_lst[1]
                file_size = os.path.getsize(file_name)
                file_info = {"type": type, "file_name": file_name, "file_size": file_size}  # 文件信息字典
                file_info_json = json.dumps(file_info)  # 将请求头格式化

                # 也可以使用struct模块来解决年黏包
                print(len(file_info_json))
                headers_len_byte = struct.pack("i", len(file_info_json))
                conn.send(headers_len_byte)  # 1
                print(len(headers_len_byte))
                conn.send(file_info_json.encode("UTF-8"))  # 2
                with open(file_name, "rb") as f:  # 发送文件
                    for line in f:
                        conn.send(line)  # 3
                    f.close()
                print(conn.recv(1024).decode("UTF-8"))
                md5_str = verify.jiaoyan(file_name)
                conn.send(md5_str.encode("UTF-8"))

        except Exception as e:
            print(e)
            break
    conn.close()

客户端

from 网络编程.FTP import verify
import socket
import os
import json
import struct
import hashlib

sk = socket.socket()
sk.connect(("127.0.0.1",8080))

while 1:
    user_input = input("请输入命令:")   #最后输入文件的绝对路径
    try:
        if user_input == "exit":
            sk.send(user_input.encode("UTF-8"))
            break
        if user_input == "":
            continue
        else:

            info_lst = user_input.strip().split(" ")
            print(info_lst)
            type = info_lst[0]
            file_name = info_lst[1]
            sk.send(",".join(info_lst).encode("UTF-8"))

            if type == "put":  #上传文件,客户端发送

                md5 = hashlib.md5()

                file_size = os.path.getsize(file_name)
                file_info = {"type":type,"file_name":file_name,"file_size":file_size}  #文件信息字典
                file_info_json = json.dumps(file_info)  #将请求头格式化

                #也可以使用struct模块来解决年黏包
                print(len(file_info_json))
                headers_len_byte = struct.pack("i",len(file_info_json))
                sk.send(headers_len_byte)  #1
                print(len(headers_len_byte))
                sk.send(file_info_json.encode("UTF-8"))  #2

                with open(file_name, "rb") as f:  #发送文件
                    for line in f:
                        sk.send(line)  #3
                    f.close()
                sk.send(verify.jiaoyan(file_name).encode("UTF-8"))


            else:  #下载文件,客户端接受
                md5 = hashlib.md5()

                headers_len_byte = sk.recv(4)
                print(headers_len_byte)
                headers_len = struct.unpack("i", headers_len_byte)[0]  # 将请求头长度解包
                print(headers_len)
                headers = json.loads(sk.recv(headers_len).decode("UTF-8"))  # 将请求头反格式化
                print(headers)
                type = headers.get("type")
                file_name = headers.get("file_name")
                file_size = headers.get("file_size")
                recv_data_size = 0
                with open(file_name, "wb") as f:  # 接受数据
                    while recv_data_size < file_size:
                        data = sk.recv(1024)
                        f.write(data)
                        recv_data_size += len(data)
                        print("文件大小:{},已接受:{}".format(file_size, recv_data_size))
                print("接收成功")
                sk.send("ok".encode("UTF-8"))
                hex = sk.recv(1024).decode("UTF-8")
                print(hex)
                md5_str = verify.jiaoyan(file_name)
                if hex == md5_str:
                    print("数据完整")
                else:
                    print("数据有丢失")

    #1,2,3出可能会发生黏包
    except Exception as e:
        print("命令错误")

5.socket解决并发

import socketserver
class Myserver(socket.BaseRequestHandler):
	def handle(self):
		pass
server = socketserver.ThreadingTCPServer(("127.0.0.1",8080),Myserver)
server.server_forever()  #		

6.网络编程零碎知识点

1.服务端的conn是连接的客户端的socket对象
2.ssh:客户端通过给服务端传递cmd命令,服务端执行命令后结果返回给客户端,从而到达客户端控制服务端的目的
3.命令行显示的数据编码格式为GBK
4.send()传送的数据为str,不为空,且为byte类型
5.管道里的数据只能读取一次
6.subprocess中的stdout的数据读取出来为byte类型
7.解决黏包:
	发送端:1.构建报头(采用struct模块)  2.发送报头  3.发送数据
	接收端:1.接收报头  2.接受数据
8.先启动服务端,再启动客户端,客户端与服务端的ip和端口号要一致,而且保证服务端的端口号没有被占用
9.黏包问题的本质就是接收方不知道数据的分界线,所以发送方只需要告诉接收方的分界线就可以解决黏包问题
10.应用程序不能直接调用硬件,要通过操作系统的帮助
11.在windows平台上,如果断开客户端的socket,服务端会报错,而且windows平台上不能发送空数据
   在linux和ios平台上,如果断开客户端的socket,会给服务端发送一个空数据
12.server.listen(a)  表示的是排列的客户端最多为a个,算上正在连接的客户端一共最多可以有a+1个客户端,类似于队列
13.在写网络编程的代码时注意事项:
    1.连接对象的使用
    2.客户端和服务端的ip和端口号一样,不同的服务端是否占用同一个端口号
    3.发送数据之前判断是否会黏包
    4.根据不同的情况选择不同的方式去处理黏包
14.为什么socket收发消息时要转化成字节类型?将数据转化为utf8或gbk的格式,是为了压缩数据,节省空间
15.socketserver不仅简化了服务端的写法,最重要的是解决了并发问题+
self.request == conn
self.client_address == address
16.MAC地址:每个网卡有唯一的MAC地址
17.dhcp服务:路由器或交换机中的dhcp服务会自动为pc分配ip地址
18.如果两个机器的ip地址冲突,都上不了网
19.子网掩码:eg 255.255.255.0  挡住的ip位数作为网段,未挡住的是可变的值,表示在局域网中ip地址的前三位固定
20.网关:局域网与局域网通信要借助网关,网关地址是网络地址(子网掩码与局域网中的任意ip进行与运算的结果)
21.arp协议:局域网中的发送数据格式要遵循arp协议
22.dns协议,我们访问一个网站时通过dns协议将域名解析为ip地址,然后连接对应ip地址就可以进行浏览
本地电脑上域名与ip的对应关系在:C:\Windows\System32\drivers\etc\hosts
全球一共有13台顶级dns服务器,来存放域名与ip的对应关系
23.建立一个网站①租一个服务器+公网ip②租域名
24.通过划分局域网来避免广播风暴 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值