socket通讯

 

socket是什么?

 

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket和file的区别:

  • file模块是针对某个指定文件进行【打开】【读写】【关闭】
  • socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭

 

 

更多功能

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

参数一:地址簇

  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型

  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议

  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

 

 

web服务应用demo

 1 #!/usr/bin/env python
 2 #coding:utf-8
 3 import socket
 4  
 5 def handle_request(client):
 6     buf = client.recv(1024)
 7     client.send("HTTP/1.1 200 OK\r\n\r\n")
 8     client.send("Hello, World")
 9  
10 def main():
11     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
12     sock.bind(('localhost',8080))
13     sock.listen(5)
14  
15     while True:
16         connection, address = sock.accept()
17         handle_request(connection)
18         connection.close()
19  
20 if __name__ == '__main__':
21   main()

 

一、利用socket实现简单的即时通讯

 

server端:

 

 1 #! usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import socket
 5 
 6 server = socket.socket()
 7 
 8 server.bind(('127.0.0.1', 8888))
 9 server.listen(5)
10 # print("before")
11 while True:
12     print("before")
13     conn, addr = server.accept()    # 等待链接
14     conn.sendall(bytes("welcome, hanhan...", encoding='utf-8'))    # python3.0之后必须是bytes类型
15     while True:
16         ret_byte = conn.recv(1024)
17         ret_str = str(ret_byte, encoding='utf-8')
18         if ret_str == 'q':
19             break
20         conn.sendall(bytes(ret_str + 'good', encoding='utf-8'))
21     print(addr)
22     print("after")

 

client端:

 

 1 #! usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import socket
 5 
 6 client = socket.socket()
 7 
 8 client.connect(("127.0.0.1", 8888))
 9 result_byte = client.recv(1024)
10 print(str(result_byte, encoding='utf-8'))
11 
12 while True:
13     inp = input("Enter:")
14     if inp == 'q':
15         client.sendall(bytes(inp, encoding='utf-8'))
16         break
17     else:
18         client.sendall(bytes(inp, encoding='utf-8'))
19         ret = client.recv(1024)
20     print(str(ret, encoding='utf-8'))

 

 

二、使用socket实现文件上传

 

server端:

 

 1 import socket
 2 
 3 server = socket.socket()
 4 
 5 server.bind(('127.0.0.1', 9999))
 6 
 7 server.listen(5)
 8 print("before")
 9 while True:
10     conn, addr = server.accept()
11     # 接受文件大小
12     size = str(conn.recv(1024), encoding='utf-8')
13     total_size = int(size)
14     print(total_size)
15     has_size = 0
16     # 先等待接收接收客户端发送的消息后,再去接收数据,避免粘包问题
17     ret = str(conn.recv(1024), encoding='utf-8')
18     print(ret, 'start recving data...')
19     f = open('f1.png', 'wb')
20     while has_size <= total_size:
21         content = conn.recv(1024)
22         f.write(content)
23         has_size += len(content)
24     f.close()

 

client端:

 

 1 import socket
 2 import os
 3 client = socket.socket()
 4 
 5 client.connect(('127.0.0.1', 9999))
 6 # 先获取文件大小,并发送给服务端
 7 size = os.stat('f.png').st_size
 8 print(size)
 9 client.sendall(bytes(str(size), encoding='utf-8'))
10 # 发送文件前,先和服务端进行通讯,完成后在传输文件,避免粘包问题
11 client.sendall(bytes("start sending data...", encoding='utf-8'))
12 print("start sending data...")
13 with open('f.png', 'rb') as f:
14     for line in f:
15         client.sendall(line)
16 
17 print(os.stat('f.png').st_size)
18 print(os.stat('f1.png').st_size)

 

 

三、使用socket server实现简单的并发

 

 1 #! usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import socketserver
 5 
 6 
 7 class Myserver(socketserver.BaseRequestHandler):
 8     def handle(self) -> None:
 9         # self.request, self.client_address, self.server
10         print("before...")
11         conn = self.request
12         conn.sendall(bytes("welcome, hanhan...", encoding='utf-8'))
13         while True:
14             ret_byte = conn.recv(1024)
15             ret_str = str(ret_byte, encoding='utf-8')
16             if ret_str == 'q':
17                 break
18             conn.sendall(bytes(ret_str + 'good', encoding='utf-8'))
19         # print(addr)
20         print("after")
21 
22 
23 if __name__ == '__main__':
24     server = socketserver.ThreadingTCPServer(('127.0.0.1', 9999), Myserver)
25     server.serve_forever()

 四、I/O多路复用

 

I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

 

一、 python中的应用

python是通过其内置的select模块,它提供了select,poll,epoll三个方法,通过调用系统的select,poll,epoll方法实现;

Windows Python:
    提供: select
Mac Python:
    提供: select
Linux Python:
    提供: select、poll、epoll

注意⚠️:网络操作、文件操作、终端操作等均属于IO操作。windows只支持socket操作,其他系统支持其他的IO操作,但是无法检测,普通文件操作自上次是否发生变化;

 

对于select方法:

 1 句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)
 2  
 3 参数: 可接受四个参数(前三个必须)
 4 返回值:三个列表
 5  
 6 select方法用来监视文件句柄,如果句柄发生变化,则获取该句柄。
 7 1、当 参数1 序列中的句柄发生可读时(accetp和read),则获取发生变化的句柄并添加到 返回值1 序列中
 8 2、当 参数2 序列中含有句柄时,则将该序列中所有的句柄添加到 返回值2 序列中
 9 3、当 参数3 序列中的句柄发生错误时,则将该发生错误的句柄添加到 返回值3 序列中
10 4、当 超时时间 未设置,则select会一直阻塞,直到监听的句柄发生变化
11   当 超时时间 = 1时,那么如果监听的句柄均无任何变化,则select会阻塞 1 秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

 

 

server端

server.py

 1 #! usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import socket
 4 import select
 5 
 6 sk1 = socket.socket()
 7 sk1.bind(('127.0.0.1', 9999))
 8 sk1.listen(5)
 9 
10 sk2 = socket.socket()
11 sk2.bind(('127.0.0.1', 9998))
12 sk2.listen(5)
13 
14 server_list = [sk1, sk2]
15 while True:
16     r_list, w_list, e_lirt = select.select(server_list, [], [], 1)
17     '''
18     rlist -- wait until ready for reading
19     wlist -- wait until ready for writing
20     xlist -- wait for an ``exceptional condition''
21     '''
22     for sk in r_list:
23         conn, addr = sk.accept(1024)
24         conn.sendall(bytes('hello', encoding='utf-8'))
25         conn.close()

 client端

client.py

1 #! usr/bin/env python
2 # -*- coding:utf-8 -*-
3 import socket
4 
5 client = socket.socket()
6 
7 client.connect(('127.0.0.1', 9999))
8 print(str(client.recv(1024), encoding='utf-8'))

 

posted @ 2020-06-22 18:17  H年轻的心  阅读(9)  评论(0)    收藏  举报