day7 socket网络编程基础
Posted (野生程序员)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了day7 socket网络编程基础相关的知识,希望对你有一定的参考价值。
Socket
Socket是什么?
下面来看一下网络的传输过程:
上面图片显示了网络传输的基本过程,传输是通过底层实现的,有很多底层,我们写传输过程的时候,要知道所有的过程那就太复杂了,socket为我们封装了底层的传输流程,让我们直接可以在socket上直接实现数据交换。
socket本质:对底层网络协议的封装。
socket实现数据的发送和接收,通过什么建立连接呢?下面看一幅简单的图片:
在计算机上,我们运行了很多线程,我们如何实现数据的定向交换呢?如何实现客户端和服务器的连接呢?连接我们可以通过IP地址进行连接,连接上之后发送给那个程序呢?这时候我们就要通过port(端口号)进行指明,因为实现连接的过程是通过IP+端口号(port)进行连接。
客户端
import socket while True: s = socket.socket() \'\'\'生成一个socket连接\'\'\' s.connect(("localhost",6970)) #建立连接,连接本地,端口号是6969端口 message = input("请输入您要发送的信息:").encode(\'utf-8\') if message == "quit": break s.sendall(message) #python3上只能传输二进制字节 data = s.recv(1024) #接收服务器端传来的内容 print(data) s.close() #关闭连接
上面代码就是客户端,通过客户端发送数据到服务器,实现交互,客户端加上了一个while循环,能够实现多次交互,我们知道,正常情况下,交互一次就退出了,通过While循环,让客户端不停的产生新的连接,就能不断与客户端进行数据交换。
下面是客户算发送的数据,只能以字符的形式进行发送,所以发送的是字符,汉字看不到,进行转换了。而且不知道为什么,输入空之后,客户端没有响应,静止不动了:
请输入您要发送的信息:dasfda b\'dasfda\' 请输入您要发送的信息:不能发送汉字吗 b\'\\xe4\\xb8\\x8d\\xe8\\x83\\xbd\\xe5\\x8f\\x91\\xe9\\x80\\x81\\xe6\\xb1\\x89\\xe5\\xad\\x97\\xe5\\x90\\x97\' 请输入您要发送的信息:放大法是否对 b\'\\xe6\\x94\\xbe\\xe5\\xa4\\xa7\\xe6\\xb3\\x95\\xe6\\x98\\xaf\\xe5\\x90\\xa6\\xe5\\xaf\\xb9\' 请输入您要发送的信息:dfafdas b\'dfafdas\' 请输入您要发送的信息:dfasdfa b\'dfasdfa\' 请输入您要发送的信息:dfasfd b\'dfasfd\' 请输入您要发送的信息:afdasdfas fb\'afdasdfas\' 请输入您要发送的信息:dasfda b\'fdasfda\' 请输入您要发送的信息:dfa fdab\'dfa\' 请输入您要发送的信息: b\'fda\' 请输入您要发送的信息:afasfda b\'afasfda\' 请输入您要发送的信息:afdasfd b\'afdasfd\' 请输入您要发送的信息:afdasdfa b\'afdasdfa\' 请输入您要发送的信息:
服务器
import socket \'\'\'生成socket实例\'\'\' s = socket.socket() s.bind(("localhost",6970)) #绑定本地IP和6969端口号 s.listen(10) #监听客户端发送的信息,一旦有客户端发送过来连接,就接收,现在是等待状态,防止堵塞 print("连接建立完毕,正在等待数据.......") while True: conn,addr = s.accept() #接收数据,accept()会接收两个数据,一个是连接conn,一个是地址addr(IP和端口号) print("Addr:",addr) data = conn.recv(1024) #通过连接接收数据 print(data) conn.send(data) #发送数据,把接收到的信息发送 conn.close() s.close()
上面是服务器的代码,我们也使用了一个循环,让服务器一直挂着,等待客户端发送数据,不停的接收:
下面是服务器接收的数据:
连接建立完毕,正在等待数据....... Addr: (\'127.0.0.1\', 51924) b\'dasfda\' Addr: (\'127.0.0.1\', 51926) b\'\\xe4\\xb8\\x8d\\xe8\\x83\\xbd\\xe5\\x8f\\x91\\xe9\\x80\\x81\\xe6\\xb1\\x89\\xe5\\xad\\x97\\xe5\\x90\\x97\' Addr: (\'127.0.0.1\', 51928) b\'\\xe6\\x94\\xbe\\xe5\\xa4\\xa7\\xe6\\xb3\\x95\\xe6\\x98\\xaf\\xe5\\x90\\xa6\\xe5\\xaf\\xb9\' Addr: (\'127.0.0.1\', 51930) b\'dfafdas\' Addr: (\'127.0.0.1\', 51932) b\'dfasdfa\' Addr: (\'127.0.0.1\', 51934) b\'dfasfd\' Addr: (\'127.0.0.1\', 51936) b\'afdasdfas\' Addr: (\'127.0.0.1\', 51938) b\'fdasfda\' Addr: (\'127.0.0.1\', 51940) b\'dfa\' Addr: (\'127.0.0.1\', 51942) b\'fda\' Addr: (\'127.0.0.1\', 51944) b\'afasfda\' Addr: (\'127.0.0.1\', 51946) b\'afdasfd\' Addr: (\'127.0.0.1\', 51948) b\'afdasdfa\' Addr: (\'127.0.0.1\', 51950)
可以看出,数据进行了多次的交互,并且接收了数据,是以字符编码形式进行接收的。
客户端 import socket client = socket.socket() #声明socket类型,同时生成socket连接对象 client.connect(("localhost",6969)) client.send("我要下载A片".encode("utf-8")) #在python3中只能发送字节 data = client.recv(1024) #默认接收数据的大小,定义1024个字节 print("recv:",data.decode()) client.close() #关闭连接 服务器 #服务器端 import socket server = socket.socket() server.bind(("localhost",6969)) #绑定要监听端口 server.listen() #监听 print("我要开始等电话了") conn,addr = server.accept() #等待接收,conn对方的连接,addr对方的地址,接收会接收到一个IP编号和地址 #conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn,addr) print("电话来了") data = conn.recv(1024) #接收的数据大小,不能直接调用server接收,打电话新的进来,可以切换 print("recv:",data) conn.send(data.upper()) server.close()
下面来看一个循环的代码:
#服务器端 import socket server = socket.socket() server.bind(("localhost",6969)) #绑定要监听端口 server.listen() #监听 print("我要开始等电话了") while True: conn,addr = server.accept() #等待接收,conn对方的连接,addr对方的地址,接收会接收到一个IP编号和地址 #conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn,addr) print("电话来了") data = conn.recv(1024) #接收的数据大小,不能直接调用server接收,打电话新的进来,可以切换 print("recv:",data) conn.send(data.upper()) server.close()
首先启动服务器端,等待客户端发送数据:
客户端:
import socket client = socket.socket() #声明socket类型,同时生成socket连接对象 client.connect(("localhost",6969)) while True: msg = input(">>:").strip() #输入空会卡主,我们知道,QQ也是不允许用户输入空的,会提示输入为空 client.send(msg.encode("utf-8")) #在python3中只能发送字节 data = client.recv(1024) #默认接收数据的大小,定义1024个字节 print("recv:",data.decode()) client.close() #关闭连接
客户端输入:
>>:我
recv: 我
>>:要
上面可以看出,客户端输入第一次正常,第二次卡住了,只能进行一次通讯,说明客户端的实现一次通讯之后终止了。
服务端接收:
我要开始等电话了
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 6969), raddr= (\'127.0.0.1\', 58460)> (\'127.0.0.1\', 58460)
电话来了
recv: b\'\\xe6\\x88\\x91\'
可以看出,服务器端第一次接收数据之后,也卡住了,服务器端卡主是由于没有新的连接进来,连接并没有终端,而只是客户端自己断开,找不到新的连接挂在哪里。
下面来修改服务器代码:
#服务器端 import socket server = socket.socket() server.bind(("localhost",7071)) #绑定要监听端口 server.listen() #监听 print("我要开始等电话了") conn, addr = server.accept() # 等待接收,conn对方的连接,addr对方的地址,接收会接收到一个IP编号和地址 # conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn, addr) print("电话来了") while True: data = conn.recv(1024) #接收的数据大小,不能直接调用server接收,打电话新的进来,可以切换 # if not data: # break print("recv:",data) conn.send(data.upper()) server.close()
现在重新启动服务器,然后启动客户端,在客户端输入交互内容,如下:
客户端输入呢绒: >>:wo recv: WO >>:yao recv: YAO >>:zixue recv: ZIXUE 服务器端接收内容: 我要开始等电话了 <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr=(\'127.0.0.1\', 51508)> (\'127.0.0.1\', 51508) 电话来了 recv: b\'wo\' recv: b\'yao\' recv: b\'zixue
现在显示一切都是那么完美,现在来断开客户端,看会怎样,如下:
ecv: b\'\'
recv: b\'\'
recv: b\'\'
recv: b\'\'
recv: b\'\'
......
客户端断开之后,服务器端并没有断开,由于服务器端在循环接收消息,由于没有客户端发送消息,因为接收到的消息一直是空的,并且服务器端server = socket.accept()并没有执行接收过程,因为一直在运行循环。
下面我们来验证服务器端的情况,看客户端断了之后,服务器端是如何执行的,为此我们需要简单修改一下服务器端代码,如下:
#服务器端 import socket server = socket.socket() server.bind(("localhost",7071)) #绑定要监听端口 server.listen() #监听 print("我要开始等电话了") conn, addr = server.accept() # 等待接收,conn对方的连接,addr对方的地址,接收会接收到一个IP编号和地址 # conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn, addr) print("电话来了") count = 0 while True: data = conn.recv(1024) #接收的数据大小,不能直接调用server接收,打电话新的进来,可以切换 # if not data: # break print("recv:",data) conn.send(data.upper()) count += 1 if count >= 10: break server.close()
运行结果如下:
我要开始等电话了
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr= (\'127.0.0.1\', 51532)> (\'127.0.0.1\', 51532)
电话来了
recv: b\'wo\'
recv: b\'yao\'
recv: b\'\\xe8\\x87\\xaa\\xe5\\xad\\xa6\'
recv: b\'\\xe5\\xbe\\x80\'
recv: b\'\'
recv: b\'\'
recv: b\'\'
recv: b\'\'
recv: b\'\'
recv: b\'\'
从上面代码可以看出,当客户端端口之后,服务器端并没有断开,由于连接断开了,服务器一直在循环接收数据。(在Windows上,如果客户端断开,服务器端也会断开)
下面来修改一下代码,让客户端断开,服务器端也断开。
如下:
#服务器端 import socket server = socket.socket() server.bind(("localhost",7071)) #绑定要监听端口 server.listen() #监听 print("我要开始等电话了") conn, addr = server.accept() # 等待接收,conn对方的连接,addr对方的地址,接收会接收到一个IP编号和地址 # conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn, addr) print("电话来了") while True: data = conn.recv(1024) #接收的数据大小,不能直接调用server接收,打电话新的进来,可以切换 if not data: #加入一个判断,如果接收为空,则断开服务器 break print("recv:",data) conn.send(data.upper()) server.close()
上面代码中,对服务器端代码进行了简单完善,让客户端断开的时候,服务器端也断开,如何实现呢?只需要判断客户端接收到空的时候终止既可以。然后启动服务器,启动客户端进行数据传输如下:
客户端输入: /usr/bin/python3.5 /home/zhuzhu/第七天/socket_client.py >>:woyao recv: WOYAO >>:自学 recv: 自学 >>:Traceback (most recent call last): File "/home/zhuzhu/第七天/socket_client.py", line 7, in <module> msg = input(">>:").strip() #输入空会卡主,我们知道,QQ也是不允许用户输入空的,会提示输入为空 KeyboardInterrupt 服务器端接收: 我要开始等电话了 <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr=(\'127.0.0.1\', 51548)> (\'127.0.0.1\', 51548) 电话来了 recv: b\'woyao\' recv: b\'\\xe8\\x87\\xaa\\xe5\\xad\\xa6\'
从上面结果可以看出,客户端断开时候,服务器端也断开了,那么如何实现客户端断开,服务器端接收新的客户端的消息,只断开当前连接,重新生成一个新的连接。如下:
服务器端
#服务器端 import socket server = socket.socket() server.bind(("localhost",7071)) #绑定要监听端口 server.listen() #监听 while True: print("我要开始等电话了") conn, addr = server.accept() # 等待接收,conn对方的连接,addr对方的地址,接收会接收到一个IP编号和地址 # conn就是客户端连过来而在服务器端为其生成的一个连接实例 print(conn, addr) print("电话来了") while True: data = conn.recv(1024) #接收的数据大小,不能直接调用server接收,打电话新的进来,可以切换 if not data: break print("recv:",data) conn.send(data.upper()) server.close()
上面代码进行了两层嵌套,内层嵌套是如果接收消息为空,则断开本次连接,外层循环是内存连接断开之后,重新生成一个新的连接,因此我们首先启动服务器端,现在同时打开三个客户端如下:
启动服务器:
我要开始等电话了
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr= (\'127.0.0.1\', 51568)> (\'127.0.0.1\', 51568)
电话来了
客户端1:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_client.py
>>:
客户端2:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_client.py
>>:
客户端3:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_client.py
>>:
首先在客户端1发送消息,如下:
>>:11
recv: 11
服务器:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_server.py
我要开始等电话了
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr= (\'127.0.0.1\', 51568)> (\'127.0.0.1\', 51568)
电话来了
recv: b\'11\'
可以看到,服务器接收到了客户端1发送的消息,在客户端2和客户端3发送消息,如下:
客户端2:
>>:shibushi
客户端3:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_client.py
>>:接收不到吗?
服务器端:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_server.py
我要开始等电话了
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr= (\'127.0.0.1\', 51568)> (\'127.0.0.1\', 51568)
电话来了
recv: b\'11\'
可以看到,服务器并没有接收到客户端2和客户端3的信息,现在终端客户端1,如下:
客户端1:
>>:11
>>:Traceback (most recent call last):
File "/home/zhuzhu/第七天/socket_client.py", line 7, in <module>
msg = input(">>:").strip() #输入空会卡主,我们知道,QQ也是不允许用户输入空的,会提示输入为空
KeyboardInterrupt
服务器:
我要开始等电话了
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr= (\'127.0.0.1\', 51568)> (\'127.0.0.1\', 51568)
电话来了
recv: b\'11\'
我要开始等电话了
<socket.socket fd=5, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr= (\'127.0.0.1\', 51570)> (\'127.0.0.1\', 51570)
电话来了
recv: b\'shibushi\'
从上面可以看出,客户端1断开后,服务器与客户端1的连接也断开了,生成了一个新的连接,连接客户端2,并接收客户端2发送过来的消息。
因此我们得出结论,上面代码实现了客户端断开后,服务器端重新挂起,等待新的连接的任务。
当所有客户端中断后,如下:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_server.py 我要开始等电话了 <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr=(\'127.0.0.1\', 51568)> (\'127.0.0.1\', 51568) 电话来了 recv: b\'11\' 我要开始等电话了 <socket.socket fd=5, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr=(\'127.0.0.1\', 51570)> (\'127.0.0.1\', 51570) 电话来了 recv: b\'shibushi\' 我要开始等电话了 <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(\'127.0.0.1\', 7071), raddr=(\'127.0.0.1\', 51572)> (\'127.0.0.1\', 51572) 电话来了 recv: b\'\\xe6\\x8e\\xa5\\xe6\\x94\\xb6\\xe4\\xb8\\x8d\\xe5\\x88\\xb0\\xe5\\x90\\x97\\xef\\xbc\\x9f\' recv: b\'shibush\' recv: b\'duankaiyixai\' recv: b\'exit\' 我要开始等电话了
可以看出,当客户端所有连接断开后,服务器端还在等待消息。等待新的连接介入。
上面客户端代码是有缺陷的,因为send()是不能发送空的,我们发送空就会让客户端卡主,如下:
客户端输入:
>>:eoyoa
recv: EOYOA
>>:dfasfd
recv: DFASFD
>>: #发送空,卡住了
从上面客户端执行代码可以看出,确实当前卡主了,现在来完善一下客户端,让当用户输入为空的时候,跳过本次过程,告诉用户不能发送空,重新输入:
import socket client = socket.socket() #声明socket类型,同时生成socket连接对象 client.connect(("localhost",7071)) while True: msg = input(">>:").strip() #输入空会卡主,我们知道,QQ也是不允许用户输入空的,会提示输入为空 if not msg: print("对不起,发送消息不能为空!") continue client.send(msg.encode("utf-8")) #在python3中只能发送字节,send()不能发送空 data = client.recv(1024) #默认接收数据的大小,定义1024个字节 print("recv:",data.decode()) client.close() #关闭连接
上面代码中,客户端加入了一个判断,当用户输入发送消息为空时,告诉用户不能为空,并跳过本次循环(我们知道,QQ聊天也是不能发送空消息的),运行如下:
客户端输入:
/usr/bin/python3.5 /home/zhuzhu/第七天/socket_client.py
>>:dasfd
arecv: DASFD
>>:dfasfd
recv: ADFASFD
>>:
对不起,发送消息不能为空!
>>:发顺丰达
recv: 发顺丰达 #不小心打了一个广告
>>:
对不起,发送消息不能为空!
>>:
可以看出,上述代码实现了不能发送为空的功能,如果想要空的时候退出,可以把continue改成break,当然这是非主流做法了。任何聊天工具不会让用户输入空就终止聊天的。
以上是关于day7 socket网络编程基础的主要内容,如果未能解决你的问题,请参考以下文章