网络编程详解
1.什么是socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,
它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。 也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,
而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序 而程序的pid是同一台机器上不同进程或者线程的标识
借助一张图来解释
2.套接字的工作流程
一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。
等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。
3.简单sockect通信
server端
''' 先启动套接字服务端 ''' import socket # 买手机 # 默认tcp协议:socket.AF_INET,socket.SOCK_STREAM udp协议:socket.AF_INET,socket.SOCK_DGRAM server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 绑定手机卡 server.bind( # 相当于手机号码 # 127.0.0.1 == localhost 本机回环地址 # 单机测试下: 127.0.0.1 ('127.0.0.1', 9527) ) # 半连接池 最大监听数,同一时间可以有多个电话打进来,但是只能连接一个 server.listen(5) # 等待电话连接(阻塞态) # server.accept()是个元组 conn:连接对象,addr:客户端的连接和端口 conn, addr = server.accept() # 接听对方讲话的内容 # data客户端发送过来的消息 data = conn.recv(1024) # 可接受一次1024bytes的数据 print(data)
#服务端回话
conn.send(b'hello') # 挂电话 conn.close() #关手机 # server.close()
client端
import socket # 买手机 client = socket.socket() # 拨号 client.connect( # 客户端的ip与port必须与服务端一致 ('127.0.0.1', 9527) ) # 必须发送bytes类型的数据 # 讲话给对方听 client.send(b'hello')
#客户端接收
client.recv(1024)
4.解决粘包问题
粘包问题:在发送数据小,时间间隔短,会放到一个包中发送, 所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
解决:发多少,收多少,先发报头 告知数据大小 struct模块
发送时:
先发报头长度
再编码报头内容然后发送
最后发真实内容
接收时:
先手报头长度,用struct取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
client
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket,time,struct s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',8080)) while True: msg=input('>>: ').strip() if len(msg) == 0:continue if msg == 'quit':break s.send(msg.encode('utf-8')) l=s.recv(4) x=struct.unpack('i',l)[0] print(type(x),x) # print(struct.unpack('I',l)) r_s=0 data=b'' while r_s < x: r_d=s.recv(1024) data+=r_d r_s+=len(r_d) # print(data.decode('utf-8')) print(data.decode('gbk')) #windows默认gbk编码 客户端(自定制报头)
server
import socket,struct,json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080)) phone.listen(5) while True: conn,addr=phone.accept() while True: cmd=conn.recv(1024) if not cmd:break print('cmd: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err=res.stderr.read() print(err) if err: back_msg=err else: back_msg=res.stdout.read() conn.send(struct.pack('i',len(back_msg))) #先发back_msg的长度 conn.sendall(back_msg) #在发真实的内容 conn.close() 服务端(自定制报头)
udp套接字: 无连接的
server
client