网络编程|套接字(socket模块)

  网络编程是计算机之间的数据交互。数据传输的大致过程是,计算机A与计算机B通信。计算机A从本机的应用层,传输层,网络层,数据链路层和物理链路层发出信息,通过网络,传输到计算机B,通过计算机B的物理链路层,数据链路层,网络层,传输层到达应用层的应用程序。这也说明,通信本质上应用程序之间的数据交互。

  数据从应用层到物理链路层有很多协议,想要发出信息,就必须遵守这些协议的规范。套接字socket模块就帮助开发者解决这中问题。

  套接字有两种其中,一种是文件类型的套接字:AF_UNIX,一种是网络类型的套接字:AF_INET。文件类型的套接字可以应用在Linux系统,Linux系统一切皆文件。使用python经行网络编程,使用更多的是AF_INET网络类型的套接字。

TCP通信

socket通信

  通信代码如下图。整个通信先开启服务端,服务端运行到接受客服端链接时暂停,等待客户端发来的信号开始执行之后的程序。这时启动客户端,客户端发送信息,服务端接受到后从对象中取出数据,最大1024个字节,然后打印hello回复信息。关闭通信。客服端也一样,收到信息。进行打印,关闭通信。其中服务端有一个连接池的概念,这个链接池限制链接客户端的个数。

# 服务端
import socket     # 调用socket模块
server = socket.socket()   # 创建一个套接字对象
server.bind(('192.168.12.183', 8080))  # 服务端自己的IP和端口 IPv4即可,端口自己配置,在8000以上最好
server.listen(5)               # 监听链接, 称便连接池
conn, addr = server.accept()    # 接受客服端链接,也可视为使双向连接通道建立完成。返回的conn是对象客服端套接字对象,addr是地址

data = conn.recv(1024)           # 数据接受
print(data)                     # 打印接收数据
conn.send(b'word!')             # 回复信息
conn.close()                     # 断开通道
server.close()                   # 关闭通信
>>>b'hello'

#客户端
import socket
client = socket.socket()
client.connect(('192.168.12.183', 8080))           # 链接服务端,服务端IP与端口

client.send(b'hello')                            # 发送数据
data = client.recv(1024)                         # 数据接受
print(data)                                       # 打应数据

client.close()                                    # 关闭通道
>>>b'word!'

  在通信的时候一般都是可以经行循环通信。在上面的基础上可以改进此通信。使服务端可以随时经行通信。

# 服务端
import socket
server = socket.socket()
server.bind(('192.168.12.183', 8080))  # 服务端自己的IP和端口
server.listen(5)

conn, addr = server.accept()    # 建立通道 在此基础上可以经行数据传输
while True:

    data = conn.recv(1024)
    print(data)

    conn.send(b'word!')

# 客户端
import socket
client = socket.socket()

client.connect(('192.168.12.183', 8080))
while True:
    msg = input('>>>').strip()
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data)

  连接循环。对输入和接受的数据进行逻辑处理是程序更加健壮。

# 服务端
import socket
server = socket.socket()
server.bind(('192.168.12.183', 8080))  # 服务端自己的IP和端口
server.listen(5)
while True:
    conn, addr = server.accept()    # 建立通道 在此基础上可以经行数据传输
    while True:
        try:
            data = conn.recv(1024)
            print(data)
            if len(data) == 0: break   # 客服端异常退出后会循环打印b'',这个时候需要做个判断
            conn.send(b'word!')
        except ConnectionResetError:
            break

    conn.close()
# 客户端
import socket
client = socket.socket()

client.connect(('192.168.12.183', 8080))
while True:
    try:
        msg = input('>>>').strip()
        if not msg: continue
        client.send(msg.encode('utf-8'))
        data = client.recv(1024)
        print(data)
    except ConnectionResetError as e:
        print(e)
        break

   在使用TCP通信时,但多个数据包连续(时间间隔比较短时会出现黏包)发送时,接受方会将多个包同时接受以先后顺序,队列的方式放在一起。接收方会无法将多个文件经行分离。这是一种黏包现象。

# 服务端
import socket

server = socket.socket()
server.bind(('127.0.0.1', 8081))
server.listen(5)

while True:
    conn, addr = server.accept()
    data = conn.recv(1024)
    print(data.decode('utf-8'))
    conn.send(b'hello')
    conn.send(b'hello')
    conn.send(b'hello')
    conn.send(b'hello')
    conn.send(b'hello')
>>>aaa
# 客户端
import socket

client = socket.socket()
client.connect(('127.0.0.1', 8081))

while True:
    msg = input('>>>').encode('utf-8')
    client.send(msg)
    data = client.recv(1024)
    print(data)
    data = client.recv(1024)
    print(data)
    data = client.recv(1024)
    print(data)
    data = client.recv(1024)
    print(data)
    data = client.recv(1024)
    print(data)
b'hello'
b'hellohellohello'
b'hello'
黏包现象

  解决黏包现象可以用报头的方式解决这种问题。利用报头固定大小,携带数据大小的属性。告诉接收者发送到数据有多大,按照大小接受。

# 客户端
import socket
import struct
import json


client = socket.socket()
client.connect(('127.0.0.1', 8080))

while True:
    try:
        com = input('>>>').strip()
        if len(com) == 0: continue
        client.send(com.encode('utf-8'))

        header = client.recv(4)                # 先收报头
        dict_l = struct.unpack('i',header)[0]    # 解压报头获取携带字典大小数据

        j_dict = client.recv(dict_l)           # 按字典大小接收字典
        dict_m = json.loads(j_dict.decode('utf-8'))   # 还原字典

        a = 0
        msg = b''
        if a < dict_m['file_msg']:   # dict_m['file_msg']  获取字典中携带要发送数据包的大小
            data = client.recv(1024)
            msg += data
            a += len(data)
        print(msg)


    except BaseException:
        break
# 服务端
import socket
import json
import struct
import subprocess

server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

while True:
    conn, addr = server.accept()
    while True:
        try :
            com = conn.recv(1024)
            if len(com) == 0: break
            print(com)

            obj = subprocess.Popen(com.decode('utf-8'),shell =True,
                                  stdout = subprocess.PIPE,
                                   stderr= subprocess.PIPE)
            res = obj.stdout.read() + obj.stderr.read()           # 要发送信息

            dict_s = {'name':'命令信息', 'file_msg': len(res)}      # 将要发送信息放入字典中
            json_d = json.dumps(dict_s)      # 将字典序列化

            header = struct.pack('i',json_d)  # 将序列化的字典大小信息放入报头

            conn.send(header)                 # 先发报头
            conn.send(json_d.encode('utf-8'))  # 在发序列化后的字典
            conn.send(res)                     # 最后发要发送到信息

        except BaseException:
            break
    conn.close()
解决粘包问题

UDP通信

  UDP通信,自带报头,不存在粘包现象。与TCP协议下通信相比,UDP协议下通信不需要建立双向通道,直接向对方地址发送。支持一对一,和一对多,可进行并发通信。

UDP通信的有四个特点。客户端允许发送,不会粘包,支持并发,客户端不存在也不会出错。

  

# 客户端
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
client_addr = ('127.0.0.1', 8080)           # 服务端地址端口

while True:
    msg = input('>>>')
    server.sendto(msg.encode('utf-8'), client_addr)        # 发数据包

    data, addr = server.recvfrom(1024)               # 收数据包
    print(data)
# 服务端
import socket

server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8080))           # 服务端自己的地址和端口

while True:
    data, addr = server.recvfrom(1024)          # 收数据包,返回数据包和客户端地址
    print(data)

    msg = 'hello'
    server.sendto(msg.encode('utf-8'), addr)       # 发数据,按返回的客户端地址发送
UDP协议下通信

  socketserver模块。使TCP协议下通信和UDP协议下通信一样灵活。

# 客户端
    import socket
client = socket.socket()
client.connect(('127.0.0.1', 8080))

while True:
    client.send(b'hello')
    data = client.recv(1024)
    print(data.decode('utf-8'))

# 服务端
import socketserver

class MyServer(socketserver.BaseRequestHandler):      # 设计一个类,继承socketserver模块下BaseRequestHandler这个类
    def handle(self):
        while True:
            try:
                data = self.request.recv(1024)    # 接收数据
                print(data.decode('utf-8'))
                self.request.send(b'ok')          # 发送数据
            except ConnectionResetError as e:
                print(e)
                break
if __name__ == '__main__':
    server = socketserver.ThreadingTCPServer(('127.0.01', 8080), MyServer)     # 创建对象
    server.serve_forever()  # 启动对象
socketserve模块r

猜你喜欢

转载自www.cnblogs.com/huaiXin/p/11317246.html