网络编程(socket)

一、Socket模块

1、简介:封装了网络编程所需的代码,提供接口供用户使用

2、客户端编程流程:(1)、定义socket类 (2)、连接到服务器 (3)、接收数据 (4)、关闭客户端

3、服务端编程流程:(1)、定义socket类 (2)、绑定IP、端口 (3)、开始监听 (4)、接收客户端数据 (5)、接发数据 (6)、关闭服务端

4、无论是server还是client,最好接收数据不超过8192K

5、发送前数据会放到缓冲区,从缓冲区发送到client的条件是:(1)、缓冲区满(默认情况) (2)、超时

例子如下:

import socket


def main():
    client = socket.socket()  # 定义了一个socket类,并且提供了一个连接
    client.connect(('localhost', 6969))  # 连接地址,端口,用元组
    client.send(b'Hello World')  # 发送数据
    data = client.recv(1024)  # 接收数据
    print(data)


if __name__ == '__main__':
    main()
import socket


def main():
    server = socket.socket()
    server.bind(('localhost', 6969))  # 绑定端口
    server.listen()  # 监听端口
    print('我在等电话')
    conn, addr = server.accept()  # 接受请求,conn为客户端连接实例,addr为地址,相当于要等电话
    print('电话来了')
    data = conn.recv(1024)  # 接受conn发送请求,此处默认是阻塞
    print(data)
    conn.send(data.upper())  # 向conn发送数据
    server.close()

以下对上述例子进行改进,客户端可一直与服务器通话:

server.py

import socket


def main():
    server = socket.socket()
    server.bind(('localhost', 6969))  # 绑定端口
    server.listen()  # 监听端口
    print('我在等电话')
    conn, addr = server.accept()  # 接受请求,conn为客户端实例,addr为地址
    print('电话来了')
    while True:
        try:
            data = conn.recv(1024)  # 接受conn发送请求
            print('recv:', data)
            conn.send(data.upper())  # 向conn发送数据
        except ConnectionResetError:
            break
    server.close()


if __name__ == '__main__':
    main()

client.py

import socket


def main():
    client = socket.socket()  # 定义了一个socket类,并且提供了一个连接
    client.connect(('localhost', 6969))  # 连接地址,端口,用元组
    while True:
        send_str = input('输入发送的字符串:')
        client.send(send_str.encode('utf-8'))  # 发送数据
        data = client.recv(1024)  # 接收数据
        print('recv:', data.decode('utf-8'))
    client.close()


if __name__ == '__main__':
    main()

以下例子只需要改变服务端,但客户端1断开时客户端2再连上

import socket


def main():
    server = socket.socket()
    server.bind(('localhost', 6969))  # 绑定端口
    server.listen()  # 监听端口,里面的参数为监听的个数
    print('我在等电话')
    while True:
        conn, addr = server.accept()  # 接受请求,conn为客户端实例,addr为地址
        print('电话来了')
        while True:
            try:
                data = conn.recv(1024)  # 接受conn发送请求
                print('recv:', data)
                conn.send(data.upper())  # 向conn发送数据
            except ConnectionResetError:
                break
    server.close()


if __name__ == '__main__':
    main()

注意:客户端发送的空数据,服务器收不了

 

以下例子能实现客户端发送命令,服务器端返回命令结果

client.py

import socket


def main():
    client = socket.socket()  # 定义了一个socket类,并且提供了一个连接
    client.connect(('localhost', 6969))  # 连接地址,端口,用元组
    while True:
        send_str = input('输入发送的字符串:')
        client.send(send_str.encode('utf-8'))  # 发送数据
        data = client.recv(102400)  # 接收数据
        print(data.decode('utf-8'))
    client.close()


if __name__ == '__main__':
    main()

server.py

import socket
import os


def main():
    server = socket.socket()
    server.bind(('localhost', 6969))  # 绑定端口
    server.listen()  # 监听端口,里面的参数为监听的个数
    print('我在等电话')
    while True:
        conn, addr = server.accept()  # 接受请求,conn为客户端实例,addr为地址
        print('电话来了')
        while True:
            try:
                data = conn.recv(1024)  # 接受conn发送请求
                data = data.decode('utf-8')
                com_res = os.popen(data).read()
                conn.send(com_res.encode('utf-8'))  # 向conn发送数据
            except ConnectionResetError:
                break
    server.close()


if __name__ == '__main__':
    main()

以下程序能实现服务器发文件,客户端收文件,但由于客户端收文件大小有限,故不能一次发送完

server.py

import socket


def main():
    server = socket.socket()
    server.bind(('localhost', 6969))  # 绑定端口
    server.listen()  # 监听端口,里面的参数为监听的个数
    print('我在等电话')
    while True:
        conn, addr = server.accept()  # 接受请求,conn为客户端实例,addr为地址
        print('电话来了')
        with open('test.rar', 'rb') as f:
            conn.sendall(f.read())
    server.close()


if __name__ == '__main__':
    main()

client.py

import socket


def main():
    client = socket.socket()  # 定义了一个socket类,并且提供了一个连接
    client.connect(('localhost', 6969))  # 连接地址,端口,用元组
    f = open('test_client.rar', 'wb')
    while True:
        send_str = input('输入发送的字符串:')
        client.send(send_str.encode('utf-8'))  # 发送数据
        data = client.recv(1024000)  # 接收数据
        f.write(data)
    client.close()


if __name__ == '__main__':
    main()

 粘包:网络中,程序里有两个send语句放在一起,两个send的数据合并一起一并发送。

如下:

server.py

 1 def main():
 2     server = socket.socket()
 3     server.bind(('localhost', 6969))
 4     server.listen()
 5     while True:
 6         conn, addr = server.accept()
 7         print('新客户端:', addr)
 8         while True:
 9             rec_data_byte = conn.recv(1024)
10             if len(rec_data_byte) == 0:
11                 print('客户端已断开!')
12                 break
13             else:
14                 rec_data_str = rec_data_byte.decode('utf-8')
15                 cmd_res_str = os.popen(rec_data_str).read()
16                 if not cmd_res_str:
17                     cmd_res_str = '没有此命令'
18                 conn.send(str(len(cmd_res_str.encode('utf-8'))).encode('utf-8'))
19                 conn.send(cmd_res_str.encode('utf-8'))
20                 print('\033[1;31m发送完成\033[0m')
21     server.close()
22 
23 
24 if __name__ == '__main__':
25     main()
View Code

client.py

 1 def main():
 2     client = socket.socket()
 3     client.connect(('localhost', 6969))
 4     while True:
 5         send_str = input('>>:').strip()
 6         if send_str:
 7             send_byte = send_str.encode('utf-8')
 8             client.send(send_byte)
 9             total_cmd_res_size_int = int(client.recv(1024))
10             print('总共需要接收\033[1;31m%s\033[0m字节' % total_cmd_res_size_int)
11             total_cur_recv_size = 0
12             while total_cur_recv_size < total_cmd_res_size_int:
13                 recv_data = client.recv(1024)
14                 total_cur_recv_size += len(recv_data)
15                 print(recv_data.decode('utf-8', errors='ignore'))
16             else:
17                 print('I have received \033[1;31m%s\033[0m字节!' % total_cur_recv_size)
18     client.close()
19 
20 
21 if __name__ == '__main__':
22     main()
View Code

 粘包解决方法:

(1)、在两个send中间加一个等待时间time.sleep(0.5),但不建议,会卡顿感。

(2)、可在两个send中接收对方的一条数据。

以下例子模拟客户端从ftp服务器下载数据,并且经过md5校验。

server.py

 1 import socket
 2 import os
 3 import hashlib
 4 
 5 
 6 def main():
 7     server = socket.socket()
 8     server.bind(('localhost', 6969))
 9     server.listen()
10     while True:
11         conn, addr = server.accept()
12         print('新客户端:', addr)
13         while True:
14             try:
15                 # 接收客户端命令
16                 com_byte = conn.recv(1024)
17             except ConnectionResetError:
18                 # 处理客户端断开
19                 print('客户端已断开')
20                 break
21             # 获取文件名
22             file_name = com_byte.decode('utf-8').split(' ')[1]
23             # 判断文件是否存在
24             if os.path.isfile(file_name):
25                 m = hashlib.md5()
26                 # 获取文件大小
27                 file_size = os.stat(file_name)[6]
28                 # print(file_size)
29                 # print(type(file_size))
30                 # 发送文件大小给客户端
31                 conn.send(str(file_size).encode('utf-8'))
32                 # 收到客户端的回复
33                 conn.recv(1024)
34                 f = open(file_name, 'rb')
35                 for line in f:
36                     m.update(line)
37                     conn.send(line)
38                 conn.send(m.hexdigest().encode('utf-8'))
39                 print('\033[1;31m发送完成\033[0m')
40                 f.close()
41     server.close()
42 
43 
44 if __name__ == '__main__':
45     main()
View Code

client.py

 1 import socket
 2 import hashlib
 3 
 4 
 5 def main():
 6     client = socket.socket()
 7     client.connect(('localhost', 6969))
 8     while True:
 9         # 提示输入命令
10         cmd_str = input('>>:').strip()
11         if cmd_str.startswith('get'):
12             file_name = cmd_str.split(' ')[1]
13             cmd_byte = cmd_str.encode('utf-8')
14             # 发送命令给服务器
15             client.send(cmd_byte)
16             # 获取下载文件大小
17             file_size = int(client.recv(1024))
18             client.send('我已经准备好了接收'.encode('utf-8'))
19             print('总共需要接收\033[1;31m%s\033[0m字节' % file_size)
20             # 总接收大小
21             received_size = 0
22             f = open(file_name + '.new', 'wb')
23             m = hashlib.md5()
24             # 循环接收数据
25             while received_size < file_size:
26                 # 不是最后一次收数据
27                 if file_size - received_size > 1024:
28                     size = 1024
29                 # 最后一次收数据
30                 else:
31                     size = file_size - received_size
32                 recv_data = client.recv(size)
33                 m.update(recv_data)
34                 received_size += len(recv_data)
35                 f.write(recv_data)
36             else:
37                 print('\033[1;31m下载文件完成\033[0m')
38                 print('从服务器端收到的md5是', client.recv(1024).decode('utf-8'))
39                 print('本地文件的md5是', m.hexdigest())
40                 f.close()
41     client.close()
42 
43 
44 if __name__ == '__main__':
45     main()
View Code

 

posted on 2019-05-06 22:39  Treelight  阅读(384)  评论(0编辑  收藏  举报