python3 socket编程
一、ip地址
作用:用来在网络中标记一台电脑;在本地局域网中是唯一的。
二、端口
端口就好比一间房子的们,是出入这间房子的必经之路。
端口号不是随意分配的,而是按照一定的规定进行分配。
端口分类:
知名端口(Well Known Ports):
也就是是众所周知的端口号,范围是从0到1023;
一般情况下,如果一个程序需要使用知名端口需要有root权限。
(比如80端口是给配饰HTTP服务
211端口是给FTP服务)
动态端口(Dynamic Ports):
范围是从1024到65535;
之所以称为动态端口,是因为它一般不固定分配某种服务,而是动态分配;
动态分配是指当一个系统程序或应用程序程序需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配一个供它使用;
当这个程序关闭时,同时也就释放了所占用的端口号。
查看端口号:
netstat -an
三、socket(套接字)
1.Socket是应用层与TCP/IP协议族通信(传输层)的中间软件抽象层,它是一组接口。socket是传输层一下的一种封装;
2.基于网络类型的套接字家族:套接字家族的名字:AF_INET;
3.客户端怎么通过互联网找到服务端?
通过ip地址中隐藏的Mac地址找到服务器在哪个网段,而其中的Mac地址是找到具体的哪台电脑。
4.详见网络通信原理:http://www.cnblogs.com/linhaifeng/articles/5937962.html(转自别人)
5.套接字分类:
(1)面向连接的套接字(虚拟电路或流套接字)——意味着在进行通信之前必须先建立一个连接
实现该种连接类型的主要协议是传输控制协议(缩写TCP)
(2)无连接的套接字(数据报类型套接字)——在通信开始之前并不需要建立连接
实现该种连接类型的主要协议是用户数据报协议(缩写UDP)
四、tcp
1.tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端;
2.TCP特点:
2.1 面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这一个连接进行。
完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
2.2.可靠传输
1)TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功。
2)超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段;
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
3)错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
4) 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
3.TCP与UDP的不同点
面向连接(确认有创建三方交握,连接已创建才作传输。);
有序数据传输;
重发丢失的数据包;
舍弃重复的数据包;
无差错的数据传输;
阻塞/流量控制。
4.tcp注意点
tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器;
tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机;
tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的;
当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信;
当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务;
listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的;
关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信;
关闭accept返回的套接字意味着这个客户端已经服务完毕;
当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线;
5.tcp三次握手和四次握手
三次握手:

四次挥手:

【摘自别人https://blog.csdn.net/xisuo002/article/details/79161609】
所谓四次挥手就是终止TCP连接,断开一个TCP连接需要客户端和服务端发送四次数据包以确定断开连接。
1.Client发送一个数据包,用来关闭Client到server的数据传输,Client进入最后的等待状态。
2.server接到数据包经过判断后,发送确认信息给Client,自己进入等待关闭状态。
3.server发送一个数据包给Client,用来关闭server到Client的数据传输
4.Client接收到数据经过判断后,自己进入Time_wait状态,server接收数据经过判断无误后,server进入关闭状态,Client等待2MLS(Maxmum segment lifetime),它是任何报文在网络丢弃前在网络内的最长时间,过了这个时间,Client就自动关闭了。
SYN:同步信号(Synchronize)
ACK:确认字符(Acknowledge Character)
RST:重连位(Reset)
FIN:对方终止发送数据(Finally)

服务端和客户端
1 from socket import *
2 tcp_server=socket(AF_INET,SOCK_STREAM)
3
4 ip_port=("127.0.0.1",8080)
5 back_log=5#半连接池
6 buffer_size=1024#缓冲池
7 tcp_server.bind(ip_port)#绑定(主机,端口号)到套接字
8 tcp_server.listen(back_log)#开始TCP监听
9 while True:#循环不停连接
10 conn,addr=tcp_server.accept()#服务端阻塞, 被动接受TCP客户的连####接,(阻塞式)等待连接的到来
11 while True:#循环不停接受发送消息
12 data=conn.recv(buffer_size)
13 print("客户端发来的消息是",data.decode('utf-8'))
14 conn.send(data.upper())
15 conn.close()
16 tcp_server.close()
1 __author__ = 'Administrator'
2 from socket import *
3 ip_port=('127.0.0.1',8080)
4 back_log=5
5 buffer_size=1024
6 tcp_client=socket(AF_INET,SOCK_STREAM)
7 tcp_client.connect(ip_port)
8 while True:##循环不停接受发送消息
9 msg=input('>>>>>>>>')
10 #if not msg:continue
11 tcp_client.send(msg.encode('utf-8'))
12 print('客户端已经发送消息了')
13 data=tcp_client.recv(buffer_size)
14 print("客户端收到服务端返回的消息",data.decode('utf-8'))
15 tcp_client.close()
当客户端发空的时候:
1 __author__ = 'Administrator'
2 from socket import *
3 ip_port=('127.0.0.1',8080)
4 back_log=5
5 buffer_size=1024
6 tcp_client=socket(AF_INET,SOCK_STREAM)
7 tcp_client.connect(ip_port)
8 while True:##循环不停接受发送消息
9 msg=input('>>>>>>>>')
10 #if not msg:continue
11 tcp_client.send(msg.encode('utf-8'))
12 print('客户端已经发送消息了')
13 data=tcp_client.recv(buffer_size)
14 print("客户端收到服务端返回的消息",data.decode('utf-8'))
15 tcp_client.close()
16
17 #####################################
18 >>>>>>>>
19 客户端已经发送消息了
1 __author__ = 'Administrator'
2 from socket import *
3 tcp_server=socket(AF_INET,SOCK_STREAM)
4
5 ip_port=("127.0.0.1",8080)
6 back_log=5
7 buffer_size=1024
8 tcp_server.bind(ip_port)
9 tcp_server.listen(back_log)
10 while True:#循环不停连接
11 conn,addr=tcp_server.accept()#服务端阻塞
12 while True:#循环不停接受发送消息
13 data=conn.recv(buffer_size)
14 print("客户端发来的消息是",data.decode('utf-8'))
15 conn.send(data.upper())
16 conn.close()
17 tcp_server.close()
18 ########################
19 此时服务端根本没反应
反之:
recv在自己这段的缓冲区为空时,阻塞;
当recv接收到客户端的数据时和客户端close时都会解堵塞
【注意】如果出现addre alrea in use的错误:
这个是服务端仍然存在四次挥手的time_wait状态在占用地址;
解决办法:就是它在bind前加:tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
__author__ = 'Administrator' import socket def main(): buffer_size=1024 # 1.创建套接字 tcp_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.连接服务器 dest_ip=input("请输入要链接的服务器的ip:") dest_port=int(input("请输入要链接的服务器的端口:")) server_addr=(dest_ip,dest_port) tcp_client.connect(server_addr) # 3.发送数据/接收数据 send_data=input("请输入要发送的内容:") tcp_client.send(send_data.encode("utf-8")) # 4.关闭套接字 tcp_client.close() if __name__ == '__main__': main()
__author__ = 'Administrator' import socket def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.bind绑定ip和port tcp_server.bind(("127.0.0.1",8082)) # 3.listen使套接字变为被动链接,这样就可以接收别人的链接了——监听 tcp_server.listen(128)#128是可以同时链接的并发量,暂时这么理解 while True:####该循环是为多个客户端服务 # 4.aacept等待客户端的链接,为客户端服务 # accept产生的新的套接字用来为客户端服务(后面的都是用这个) new_client_socket,client_addr=tcp_server.accept() while True:####该循环多次为同一个客户端服务多次 # 5.recv/send接收发送数据 # recv,recvform前者只有数据,后面有来自,表明里面还有ip和port recv_data=new_client_socket.recv(1024) print(recv_data.decode("utf-8")) # 如果recv解堵塞:有两种 # (1)客户端发来数据 # (2)客户端调用close,recv会解堵塞 if recv_data: # 发送数据 send_back_msg=input("请输入要返回的信息:") new_client_socket.send(send_back_msg.encode("utf-8")) else: break # 关闭accept返回的套接字,意味着不会再为这个客户端服务 new_client_socket.close() # 如果将监听套接字关闭了,那么会导致不能再次等待新客户端的到来,及xxxxx.accept会失败。 tcp_server.close() if __name__ == '__main__': main()
下载文件:
[在目录下准备一个a.txt文件]
__author__ = 'Administrator' import socket def main(): # 1.创建套接字 tcp_client=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.获取服务器的ip和端口 dest_ip=input("请输入下载服务器的ip:") dest_port=int(input("请输入下载服务器的port:")) server_addr=(dest_ip,dest_port) # 3.连接服务器 tcp_client.connect(server_addr) # 4.获取下载文件名字 dowdload_file=input("请输入要下载的文件名字:") # 5.将文件名发送给服务器 tcp_client.send(dowdload_file.encode("utf-8")) # 6.接收文件中的数据 recv_data=tcp_client.recv(1024) if recv_data: # 7.保存接收到的数据到一个文件中 with open("[新]"+dowdload_file,"wb") as f: f.write(recv_data) # 8.关闭套接字 tcp_client.close() if __name__ == '__main__': main()
__author__ = 'Administrator' import socket def send_file_2_client(new_client_socket,client_addr): 1# 1.接收客户端需要下载的文件名 filename=new_client_socket.recv(1024).decode("utf-8") print("客户端[%s]需要下载的文件是%s:" %(str(client_addr),filename)) file_content=None # 2.打开这个文件,读取数据 try: f=open(filename,"rb") file_content=f.read() f.close() except Exception as r: print("没有要下载的文件%s" %filename) # 3.发送文件数据发送给客户端 if file_content: new_client_socket.send(file_content) def main(): # 1.创建套接字 tcp_server=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 2.bind绑定ip和port tcp_server.bind(("127.0.0.1",8082)) # 3.listen使套接字变为被动链接,这样就可以接收别人的链接了——监听 tcp_server.listen(128) # 4.aacept等待客户端的链接,为客户端服务 # accept产生的新的套接字用来为客户端服务(后面的都是用这个) while True: new_client_socket,client_addr=tcp_server.accept() # 5.调用发送文件函数,完成客服端服务 send_file_2_client(new_client_socket,client_addr) # 关闭套接字 new_client_socket.close() tcp_server.close() if __name__ == '__main__': main()
6.基于tcp实现远程执行命令
View Code
View Codetcp发送消息为空时候break;
其他的异常可以增加try......except;
View Code
View Code>>: ping www.baidu.com
命令的执行结果是
正在 Ping www.a.shifen.com [180.97.33.107] 具有 32 字节的数据:
来自 180.97.33.107 的回复: 字节=32 时间=35ms TTL=55
来自 180.97.33.107 的回复: 字节=32 时间=35ms TTL=55
来自 180.97.33.107 的回复: 字节=32 时间=33ms TTL=55
来自 180.97.33.107 的回复: 字节=32 时间=34ms TTL=55
180.97.33.107 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 33ms,最长 = 35ms,平均 = 34ms
>>:
新的客户端链接 ('127.0.0.1', 57023)
收到客户端的命令 b'ping www.baidu.com'
五、udp
1.发送数据流程
创建套接字;
发送数据;
关闭套接字。
2.接收数据流程
创建套接字;
绑定(本地自己的信息)ip地址和端口;
接收数据;
关闭套接字。
3.udp无需连接,udp是无链接的,先启动哪一端都不会报错
1 __author__ = 'Administrator'
2 from socket import *
3 ip_port=('127.0.0.1',8080)
4 back_log=5
5 buffer_size=1024
6 udp_server=socket(AF_INET,SOCK_DGRAM)
7 udp_server.bind(ip_port)
8 while True:
9 data,addr=udp_server.recvfrom(buffer_size)
10 print('udp服务端接收消息了')
11 print(data)
12 print(addr)
13 udp_server.sendto(data.upper(),addr)
14 print('udp服务端发送消息了')
1 __author__ = 'Administrator'
2 from socket import *
3 ip_port=('127.0.0.1',8080)
4 back_log=5
5 buffer_size=1024
6 udp_client=socket(AF_INET,SOCK_DGRAM)
7 while True:
8 msg=input('>>>>>>>>').strip()
9 udp_client.sendto(msg.encode('utf-8'),ip_port)
10 print('udp客户端发送消息了')
11 data,addr=udp_client.recvfrom(buffer_size)
12 print(data)
13 print('udp客户端接收消息了')
结果显示:
>>>>>>>>ing
udp客户端发送消息了
b'ING'
udp客户端接收消息了
>>>>>>>>
udp服务端接收消息了
b'ing'
('127.0.0.1', 62066)
udp服务端发送消息了
【注意】:
recvfrom在自己这段的缓冲区为空时,就收一个空。
>>>>>>>>
udp客户端发送消息了
b''
udp客户端接收消息了
>>>>>>>>
udp服务端接收消息了
b''
('127.0.0.1', 65161)
udp服务端发送消息了
__author__ = 'Administrator'
import socket
def main():
buffer_size=1024
# 1.创建套接字
udp_server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 2.绑定一个本地信息
localaddr=("127.0.0.1",8082)
udp_server.bind(localaddr)
while True:
# 3.接收数据
# recv_data这个变量中存储的是一个元祖(接收到的数据,(发送方的ip,port))
recv_data=udp_server.recvfrom(buffer_size)
recv_msg=recv_data[0]
recv_addr=recv_data[1]
# 4.打印接收到的数据
print(recv_msg.decode("utf-8"))
print(recv_addr)
# 5.关闭套接字
udp_server.close()
if __name__ == '__main__':
main()
__author__ = 'Administrator'
import socket
def udp_main():
ip_port=("127.0.0.1",8082)
# 创建一个套接字
udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 循环接收数据
while True:
# 从键盘获取数据
send_data=input("请输入您的数据:")
# 如果输入字符串0,退出程序
if send_data=="0":
break
# 可以使用套接字收发数据,字符串转成字节(计算机)
#udp_socket.sendto(b"hahaha hahah",("127.0.0.1",8082))
udp_socket.sendto(send_data.encode("utf-8"),ip_port)
# 关闭套接字
udp_socket.close()
print("测试程序是否运行")
if __name__ == '__main__':
udp_main()
同一个套接字既可以发也可以收:
recvfrom在为空的时候会堵塞;
单工:数据只在一个方向上传输,不能实现双方通信(比如电视);
半双工:允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,其实际上是切换的单工(比如对讲机);
全双工:允许数据在两个方向上同时传输(比如手机电话)。
socket套接字是全双工。
__author__ = 'Administrator' import socket def send_msg(udp_socket): dest_ip=input("请输入对方的ip:") dest_port=int(input("请输入对方的端口号:")) send_data=input("请输入要发送的信息:") udp_socket.sendto(send_data.encode("utf-8"),(dest_ip,dest_port)) def recv_msg(udp_socket): recv_data=udp_socket.recvfrom(1024) print("%s:%s" %(recv_data[0].decode("utf-8"),str(recv_data[1]))) def main(): # 创建套接字 udp_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # 绑定信息 udp_socket.bind(("",8092)) # 循环来处理事情 while True: print("1:发送信息") print("2:接受信息") print("3:退出系统") op=input("请输入您想要进行的操作:") if op=="1": # 发送 send_msg(udp_socket) elif op=="2": # 接收并显示 recv_msg(udp_socket) elif op=="3": # 接收并显示 break else: print("请重新输入") if __name__ == '__main__': main() ######################### 1:发送信息 2:接受信息 3:退出系统 请输入您想要进行的操作:1 请输入对方的ip:127.0.0.1 请输入对方的端口号:8092 请输入要发送的信息:hhhhhh 1:发送信息 2:接受信息 3:退出系统 请输入您想要进行的操作:2 hhhhhh:('127.0.0.1', 8092) 1:发送信息 2:接受信息 3:退出系统 请输入您想要进行的操作:


浙公网安备 33010602011771号