''' UDP(缺乏可靠性,可以是全双工) 效率高 缺点: 1.不保证数据包一定会到达目的地 2.不保证各个数据包先后顺序 3.不保证每个数据包只到达一次 UDP是无连接的 UDP不提供可靠性,UDP本身不提供确认,序列号,RTT估算,超时和重传等机制 TCP(是全双工的) 效率低 优点:(可靠) 1.客户与服务之间先建立连接,在跨该连接与那个服务器交换数据,最后终止这个连接。 2.当TCP向另一端发送数据,要求对端返回一个确认,没有收到确认,TCP就会自动重传数据并等待更长时间(多次失败后,断开连接) 3.TCP提供流量控制(server接受多大字节数) '''
import socket import os import signal import select import queue listened = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listened.bind(('127.0.0.1', 8000)) listened.listen(5) print('start server') def str_echo(conn): while True: data = conn.recv(1024) if len(data) > 0: conn.send(data) if not data: print('read error') break def sig_chid(a, b): while True: try: pid, status = os.waitpid(-1, os.WNOHANG) if status == 0: print('child process pid[%s][%s] is del' % (pid, status)) return except: print('...') break signal.signal(signal.SIGCHLD, sig_chid) while True: try: connfd, addr = listened.accept() print(addr) if (os.fork() == 0): listened.close() str_echo(connfd) exit(0) connfd.close() except KeyboardInterrupt: listened.close() print('close server') break # # # yong lai chu li xiaoxi # outputs = [] # # neihe jiance bingfa lianjie # inputs = [listened] # # cun fang client : MSG_QUEUE # msg_dic = {} # # # select # while True:d # reads, writes, execs = select.select(inputs, outputs, inputs) # print(reads, writes, execs) # # readable # for r in reads: # print('rrr', r) # if r is listened: # client, addr = r.accept() # print(client, addr) # inputs.append(client) # msg_dic[client] = queue.Queue() # else: # data = r.recv(1024) # print('data:', data) # if data: # msg_dic[r].put(data) # outputs.append(r) # else: # #remove client # inputs.remove(r) # del msg_dic[r] # # for w in writes: # print('www', w) # data = msg_dic[w].get() # w.send(data) # # # outputs.remove(w)
import socket import os import signal def create_a_client(): sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sockfd.connect(('127.0.0.1', 8000)) return sockfd # version:1 def str_cli(sock): stdineof = 0 while True: try: if stdineof: # jing li le EOF shoudong guanbi client print('close client nomal') exit(0) msg = input('>>>') sock.send(msg.encode('utf8')) recv_msg = sock.recv(1024) print(stdineof, recv_msg) if recv_msg: print('recieve msg : %s' % recv_msg) else: print('server is closed') except EOFError: # client close stdineof = 1 sock.shutdown(socket.SHUT_WR) continue # nomal # sockfds = [create_a_client()] # # create 5 clients # # for i in range(5): # # sockfds.append(create_a_client()) # # try: # str_cli(sockfds[0]) # except BrokenPipeError: # print('server is closed') # exit(0) # selet solve # 1.zhongzhi input # 2.piliang shuru wenti import sys import select import queue # readable inputs = [create_a_client(), sys.stdin] # writeable outpus = [] # version:2 def select_str_cli(rl: list, sockfd: socket.socket): stdineof = 0 while True: reads, writes, execs = select.select(rl, [], []) print(reads, writes) for r in reads: if r is sys.stdin: # biao zhun shu ru n = sys.stdin.readline() if n == '': print('EOF signal') # EOF # shutdowm stdineof = 1 sockfd.shutdown(socket.SHUT_WR) continue else: # stdin jiaru dao ke xie sockfd.send(n.encode('utf8')) if r is sockfd: # sockfd shuju kedu # sock readable msg = r.recv(1024) if not msg: if stdineof == 1: return else: print('serve is preclosed') return print('recv from server :%s' % msg) # version:3 def fork_str_cli(sockfd:socket.socket): if os.fork() == 0: # son process while True: data = sockfd.recv(1024) if data: print() print('from server :::%s' % data) else: break os.kill(os.getpid(),signal.SIGTERM) exit(0) while True: try: msg = input('>>>') print('sys stdin :%s' % msg) sockfd.send(msg.encode('utf8')) except EOFError: print('EOF') break sockfd.shutdown(socket.SHUT_WR)#EOF FIN return # fork_str_cli(inputs[0]) # select_str_cli(inputs, inputs[0]) exit(0)
select 解决客户端主动接受 服务区FIN信号
以下特殊情况需要注意:
### TCP 客户端关闭,服务端关闭(参考Unix网络编程) #### 1.tcp客户端正常关闭 ``` 当client主动发送完数据,主动键入EOF字符(*nix: Ctrl-D, Windows: Ctrl-Z+Return/Enter) 1.client进程exit(0) 会主动向server发送FIN,服务器TCP会主动回一个ACK,此时关闭了tcp连接终止的前半部分。此时,服务器SOCKET处于CLOSE_WAIT状态,客户端SOCKET处于FIN_WAIT2状态。 2.当服务器TCP接收到FIN时,服务器子进程阻塞与read调用,read调用返回0(即空字符串),此时返回服务器的子进程函数,服务器子进程调用exit终止。就是会触发TCP连接终止的后半部分,服务器TCP发送FIN到客户TCP,客户TCP回一个ACK给服务器TCP。至此客户端TCP处于TIME_WAIT状态。 ``` #### 2.服务器进程终止 ``` 1.找到服务TCP对应的子进程kil,此时子进程所有打开的描述符都被关闭。导致服务TCP会向客户TCP发送FIN,客户TCP回一个ACK给服务TCP,此时服务TCP到客户TCP的连接终止。服务TCP处于FIN_WAIT2,客户TCP处于CLOSE_WAIT。 2.此时客户端阻塞在input,此时客户端是可以向服务端send的,但是由于服务端--》客户端的连接关闭,不能send, 于是服务器响应了一个RST给客户端,客户端recv到一个''数据。 即客户并没有立即去 关闭到服务端的连接。 ``` #### 3.服务器主机崩溃 #### 4.服务器主机崩溃后重启 #### 5.服务器主机关机