Python-TCP编程

1、Socket介绍:

  socket 套接字

  Python中提供socket.py标准库,非常底层的接口库。

  Socket 是一种通用的网络编程接口,和网络底层没有一一对应的关系

  协议族:

    AF:address family, 用于socket()第一个参数

  

  Socket 类型

  

2、TCP编程:

  Socket编程,需要两端,一般来说,需要一个服务器端,一个客户端,服务器端为server,客户端为client

2.1、TCP服务器端编程:

  服务器端编程步骤:

    • 创建Socket对象
    • 绑定IP 地址 address, 和端口 port,bind() 方法,IPv4地址为一个二元组(‘IP地址字符串’,port)
    • 开始监听,将在指定的ip 端口上监听,listen()方法
    • 获取用于传递数据的socket 对象

      socket.accept() ---> (socket.object, address info)

      accept 方法 阻塞等待客户端建立连接,返回一个新的额socket对象和客户端地址的二元组

      地址是远程客户端的地址,IPV4 中他是一个二元组(clientaddr,port)

        • 接受数据:recv( bufsize [, flags])使用了缓冲区接受数据
        • 发送数据:send(bytes)发送数据

  

   只有一个client 请求的 样例:     

 1 import socket
 2 
 3 # 1、创建套接字
 4 socket = socket.socket()
 5 
 6 # 2、绑定ip 和 port
 7 ip = '127.0.0.1'
 8 port = 9999
 9 laddr = (ip, port)
10 
11 socket.bind(laddr)
12 
13 # 3、监听 套接字
14 socket.listen()
15 
16 # 4\、等待接受 客户端数据,并生产新的socket,用于数据交互
17 data = socket.accept()
18 newsocket, raddr = data
19 
20 # 5、接受数据
21 
22 msg = newsocket.recv() # 收到的是字节类型
23 
24 # 6、发送数据
25 newsocket.send(msg) # 发送也得是字节类型
26 
27 # 7、当前socket 通信结束,可以关闭
28 newsocket.close()
29 
30 # 8、关闭资源 ,这里关闭的是 创建连接时的 socket
31 socket.close()
服务器端基本样例

 

  newsocket:  

<socket.socket fd=212, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 50287)>

  raddr:远程套接字  

('127.0.0.1', 50287)  

  recv(1024):缓冲区大小一般是1024 的整数倍

  socket.accept():阻塞直到 和客户端 成功建立连接,返回一个socket对象和客户端地址。

  newsocket.recv(1024):连接成功后,没有收到数据,会阻塞在这里。

    注:    

      TCP的服务端编程:
         端口是给进程的,线程共享资源。
        通过监听来获得端口,对端口进行暴露 ,同时监听
         一个socket 是为了连接,创建新的socket进行资源通信
  
  win cmd:
  netstat -anp tcp | findstr 9999 (win的管道)
  -b:显示进程
  Linux:
     一般使用ss
  测试:
    1、服务器端,主线程是用来做其他工作,所以,不能让accept占据主线程,处于阻塞状态
    2、每个客户端应该都在自己的线程中工作,否则会recv阻塞,影响别人
    3、为了方便的将信息通知给所有的客户端,所以某个人发出的信息,必须都发给其他人一份,所以这里通过字典,将所有newsocket收集起来
    4、提供退出机制,因为使用 sindows上的调试工具,直接退出,会返回一个 b'',所以这里使用 b'quit' 或 b''退出客户端
    5、主线程(server),关闭的时候,线程也依次结束,避免占用资源。
    6、可以提供日志功能,将日志输出到文件
  
 1 import socket
 2 import logging
 3 import threading
 4 
 5 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
 6 logging.basicConfig(format=FORMAT, level=logging.INFO)
 7 
 8 class ChatServer:
 9     def __init__(self, ip='127.0.0.1', port=9999):
10         self.socket = socket.socket()
11         self.laddr = (ip, port)
12         self.event = threading.Event()
13         self.clients = {}
14 
15     def start(self):
16         self.socket.bind(self.laddr)
17         self.socket.listen()
18         threading.Thread(target=self.accept, name='accept').start()
19 
20     def accept(self):
21         while not self.event.is_set():
22             newsocket, raddr = self.socket.accept()
23             self.clients[raddr] = newsocket
24             threading.Thread(target=self.recv,args=(newsocket, raddr)).start()
25 
26     def recv(self, s, raddr):
27         while not self.event.is_set():
28             data = s.recv(1024)
29             logging.info(data)
30 
31             if data.strip() == b'quit' or data == b'':
32                 self.clients.pop(raddr)
33                 s.close()
34                 break
35             for s in self.clients.values():
36                 logging.info(data)
37                 msg = 'msgs is that  {}'.format(data.decode())
38                 s.send(msg.encode())
39 
40 
41     def stop(self):
42         for s in self.clients.values():
43             s.close()
44         self.socket.close()
45         self.event.set()
46 
47 
48 cs = ChatServer()
49 cs.start()
50 
51 while True:
52     cmd = input('>>')
53     if cmd.strip() == 'quit':
54         cs.stop()
55         break
56     logging.info(threading.enumerate())
服务器端代码
  socket常用方法:

 

   

   

2.2、MakeFile:

  socket.makefile(mode='r', buffering=None, * , encoding=None, error=None, newline=None)

  创建一个与该套接子相关联的文件对象,将recv 方法看做读方法,将send 方法看做是写方法。

  测试:显示读和写

 1 import socket
 2 sock = socket.socket()
 3 
 4 ip = '127.0.0.1'
 5 port = 9999
 6 
 7 addr = (ip, port)
 8 
 9 sock.bind(addr)
10 sock.listen()
11 
12 
13 s, _ = sock.accept()
14 print(s)
15 
16 # 这儿权限只能 r w a 不能 出现 +
17 # 创建一个 类文件对象
18 f = s.makefile(mode='rw')
19 
20 line = f.read(10) # recv
21 print(line )
22 
23 f.write(' -----{}-----'.format(line))
24 f.flush()# 写完之后 flush一下
简单测试 read
 1 import socket
 2 sock = socket.socket()
 3 
 4 ip = '127.0.0.1'
 5 port = 9999
 6 
 7 addr = (ip, port)
 8 
 9 sock.bind(addr)
10 sock.listen()
11 
12 
13 s, _ = sock.accept()
14 print(s)
15 
16 # 这儿权限只能 r w a 不能 出现 +
17 # 创建一个 类文件对象
18 f = s.makefile(mode='rw')
19 
20 line = f.readline(10) # recv
21 print(line )
22 
23 f.write(' -----{}-----'.format(line))
24 f.flush()# 写完之后 flush一下
readline

 

   readline 是遇到回车换行才 输出,read 是 够了字符数,输出 

  s   :    <socket.socket fd=212, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 52129)>
  f   :     <_io.TextIOWrapper mode='rw' encoding='cp936'>

  测试:将群聊服务器端 改为makefile

 1 import socket
 2 import logging
 3 import threading
 4 
 5 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
 6 logging.basicConfig(format=FORMAT, level=logging.INFO)
 7 
 8 class ChatServer:
 9     def __init__(self, ip='127.0.0.1', port=9999):
10         self.socket = socket.socket()
11         self.laddr = (ip, port)
12         self.event = threading.Event()
13         self.clients = {}
14 
15     def start(self):
16         self.socket.bind(self.laddr)
17         self.socket.listen()
18         threading.Thread(target=self.accept, name='accept').start()
19 
20     def accept(self):
21         while not self.event.is_set():
22             newsocket, raddr = self.socket.accept()
23 
24             f  = newsocket.makefile('rw')
25 
26             self.clients[raddr] = f
27             threading.Thread(target=self.recv,args=(newsocket, f, raddr)).start()
28 
29     def recv(self, s,f, raddr):
30         while not self.event.is_set():
31             data = f.readline(10)
32             logging.info(data)
33 
34             if data.strip() == 'quit' or data == b'':
35                 self.clients.pop(raddr)
36                 f.close() # 只关文件描述符,可能完全断不开,所以把socket也关一下
37                 s.close()
38                 break
39             for f in self.clients.values():
40                 logging.info(data)
41                 f.write(data)
42                 f.flush()
43 
44 
45     def stop(self):
46         for s in self.clients.values():
47             s.close()
48         self.socket.close()
49         self.event.set()
50 
51 
52 cs = ChatServer()
53 cs.start()
54 
55 while True:
56     cmd = input('>>')
57     if cmd.strip() == 'quit':
58         cs.stop()
59         break
60     logging.info(threading.enumerate())
改为makefile

 

2.2、TCP客户端编程

  客户端编程步骤:

    • 创建socket对象
    • 连接到远程服务端的IP 和port ,connect()方法
    • 传输数据
      • 使用send, recv方法,发送数据,接受数据

    关闭连接,释放资源

  

 

2.3、一个群聊程序:注意:群聊,所以,不发信息,也能recv信息,所以recv放到一个线程中,一直执行

  服务器端程序:  

 1 import socket
 2 import logging
 3 import threading
 4 
 5 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
 6 logging.basicConfig(format=FORMAT, level=logging.INFO)
 7 
 8 class ChatServer:
 9     def __init__(self, ip='127.0.0.1', port=9999):
10         self.socket = socket.socket()
11         self.laddr = (ip, port)
12         self.event = threading.Event()
13         self.clients = {}
14 
15     def start(self):
16         self.socket.bind(self.laddr)
17         self.socket.listen()
18         threading.Thread(target=self.accept, name='accept').start()
19 
20     def accept(self):
21         while not self.event.is_set():
22             newsocket, raddr = self.socket.accept()
23 
24             f  = newsocket.makefile('rw')
25 
26             self.clients[raddr] = f
27             threading.Thread(target=self.recv,args=(newsocket, f, raddr)).start()
28 
29     def recv(self, s,f, raddr):
30         while not self.event.is_set():
31             data = f.readline(10)
32             logging.info(data)
33 
34             if data.strip() == 'quit' or data == b'':
35                 self.clients.pop(raddr)
36                 f.close() # 只关文件描述符,可能完全断不开,所以把socket也关一下
37                 # s.close()
38                 break
39             for f in self.clients.values():
40                 logging.info(data)
41                 f.write(data)
42                 f.flush()
43 
44 
45     def stop(self):
46         for s in self.clients.values():
47             s.close()
48         self.socket.close()
49         self.event.set()
50 
51 
52 cs = ChatServer()
53 cs.start()
54 
55 while True:
56     cmd = input('>>')
57     if cmd.strip() == 'quit':
58         cs.stop()
59         break
60     logging.info(threading.enumerate())
server

 

  客户端程序:

 1 import socket
 2 import threading
 3 
 4 class ChatClient:
 5     def __init__(self, ip='127.0.0.1', port=9999):
 6         self.socket = socket.socket()
 7         self.event = threading.Event()
 8         self.raddr = (ip, port)
 9 
10     def start(self):
11         self.socket.connect(self.raddr)
12 
13         self.send('hello I am client')
14         # while not self.event.is_set():
15             # data = self.socket.recv(1024)
16             # print(data)
17         threading.Thread(target=self.recv).start()
18 
19     def send(self, msg):
20         self.socket.send(msg.encode())
21 
22     def recv(self):
23         while not self.event.is_set():
24             data = self.socket.recv(1024)
25             print(data)
26 
27     def stop(self):
28         self.socket.close()
29         self.event.set()
30 
31 cc = ChatClient()
32 cc.start()
33 
34 while True:
35     cmd = input('>>')
36     if cmd.strip() == 'quit':
37         cc.stop()
38         break
39     cc.send(cmd)
client

  server:

  client 1

  client 2:

 

posted @ 2018-11-01 13:13  JerryZao  阅读(608)  评论(0编辑  收藏  举报