从今年10月22号开始我的python学习之路,一个月下来,磕磕碰碰,勉勉强强把基础部分算是学完了,一个月走过来,我过着别人看似单调,重复的生活,确实是,每天,每周都是一样的生活模式,早上7点40起床,吃个早餐,8点到达教室,中午1点去吃个午饭,然后回到教室,下午6点去吃个晚饭,然后回到教室,待到晚上11点回家洗个澡睡觉,每天都一样的。我的朋友会问我,这样的生活不无聊吗?我回答是不,我的朋友可能认为两年的军旅生活早就让我习惯了单调无味的生活,我觉得有可能两年军旅生活确实让我有强大的适应性,但我认为最主要的是我真心觉得学习编程语言让我很感兴趣,以前的我感觉网络啊,计算机等这类东西感觉好遥远,根本无法触及,但现在我能去控制它,是多么牛逼的事。前一个月基础部分不算很难,只要逻辑思维跟上,就基本不是问题,而且涛哥真的讲的很好,很有耐心,很感谢涛哥。从这周三开始接触网络编程,一上来就很懵逼,什么ip啊,MAC地址啊,交换机啊,路由器等等关于计算机和网络的东西真的让我很萌,根本不知道是啥,相当于重新认知新事物,但几天学习下来,感觉这类东西是要学习的,但对于现阶段的我来说,不用太深入去专研,而主要是的是学会网络编程过程,接下来,我就把这几天所学到知识跟大家分享一下。

一,名词解释

  路由器:电脑上所有与公网之间的消息的传递的进出口都在路由器上,路由器有公网IP,这个IP是全球网络连接的唯一标识,路由器具有消息转发的功能

  交换机:主要是把连接到交换机上的电脑连接到一起,其次是交换机还可以设定一个IP范围,从而使得广播的范围缩小

  IP:IP分为两个,一个是电脑上由交换机分配的IP,这个IP在一个子网内是不可以重复的;另一个是公网IP,是路由器上的,这个是全球网络连接间的唯一标识

  MAC地址:电脑上的网卡在出厂时被烧制上的全球唯一标识码

  DHCP协议:这是交换机上动态分配电脑IP的协议

  ARP协议:这是交换机上的IP和MAC对应表,我们可以通过IP来查找出对应的MAC地址

  DNS服务器:这是域名解析器,我们可以通过输入域名来查找对应的公网IP

  网关:这相当于路由器上看门的,也就是 路由器上的公网IP,在公网上传输的数据,只有在目标IP和网关一致时,网关才会让数据进来

  子网掩码:主要用于判断两个IP是否属于一个子网

二,网络通信流程

网络编程socket之一-LMLPHP

  上图为网络通信流程图,主要分为以下三种情况:(以下三种情况描述纯属个人行为,不正确之处请指正)

  一、从1号电脑传输数据到2号电脑

  首先1号电脑把数据发到交换机A,数据主要包括2号电脑IP,自身电脑IP和MAC地址,加上真正要传的内容,数据到交换机A后,经过ARP协议,加上2号电脑的IP获得对应的MAC地址,交换机A就会在自身所连接的子网内广播,在这子网内的电脑都会收到信息,在2号电脑收到消息后,确认是自己IP和MAC地址,然后就确认接收数据,其他的电脑确认不是自身的IP和MAC,就直接扔掉,这样就完成了 数据传输。

  二、从1号电脑传输数据到3号电脑

  首先1号电脑把数据发到交换机A,数据主要包括3号电脑IP,自身电脑IP和MAC地址,加上真正要传的内容,数据到交换机A后,交换机A就会在自身所连接的子网内广播,但没找到,于是把数据抛给路由器,然后由路由器广播到接入此路由器的交换机,找到对应的交换机2,通过交换机2广播,找到3号电脑,完成通信。

  三,从1号电脑到4号服务器

  首先在1号电脑上浏览器上输入4号服务器上一个网的域名,通过DNS服务器查找域名对应的公网IP,然后把请求一层层发到路由器,路由器经过计算最有路径,找到目标公网IP对应的路由,然后根据公网IP和程序端口号找到要访问的网页,然后服务器在刚才的路径返回回去,把网页内容返回给1号电脑,此时我们就完成了通信,就可以上网了。

三,两种架构

  C/S架构:即client客户端/server服务端架构,比如qq,微信,客户端需要下载应用程序,安装之后才可以使用

  B/S架构:即browser浏览器端和server服务器端,比如各种网页啊,这个是不需要下载安装应用程序

四、osi七层模型

网络编程socket之一-LMLPHP

五,TCP协议和UDP协议区别

  tcp协议:可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;文件传输程序

  udp协议:不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文(数据包),尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)

网络编程socket之一-LMLPHP

在tcp协议下,是基于连接的,为了保证数据安全,存在一个三次握手,四次挥手的过程,而udp协议无连接的,所以没有这过程。

网络编程socket之一-LMLPHP

三次握手:

  1. TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
  2. TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
  3. TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
  4. TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
  5. 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。 

四次挥手:

  数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。服务端也可以主动关闭,一个流程。

  1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
  2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
  3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
  6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

六、套接字socket

  套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。

网络编程socket之一-LMLPHP

网络编程socket之一-LMLPHP

七、基于tcp下的socket

网络编程socket之一-LMLPHP

在tcp下,基于连接的,需要先启动服务端,在启动客户端。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

服务端程序
import
socket #引入模块 server=socket.socket() #创建server对象 ip_port=('192.168.15.78',8888) #声明服务端的IP和程序端口 server.bind(ip_port) #把ip_port绑定到对象 server.listen() #监听 while 1: conn,addr=server.accept() #等待客户端连接 while 1: server_msg=input('服务端:') conn.send(server_msg.encode('utf-8')) #向客户端发送消息 from_client_msg=conn.recv(1024) #接收客户端消息 print(from_client_msg.decode('utf-8')) if from_client_msg.decode('utf-8')=='byebye': #如果收到消息为byebye,就断开此次连接,继续等待下一个客户端连接 break #这就是优雅的断开 conn.close() server.close()
客户端程序
import
socket client=socket.socket() server_ip_port=('192.168.15.78',8888) #设置要连接服务端程序的IP和端口 client.connect(server_ip_port) #进行连接 while 1: from_server_msg=client.recv(1024) #接收服务端消息 print(from_server_msg.decode('utf-8')) client_msg=input('客服端:') client.send(client_msg.encode('utf-8')) #向服务端发送消息 if client_msg=='byebye': #如果输入为byebye,就断开连接 break client.close()

tcp属于长连接,长连接就是一直占用着这个链接,这个连接的端口被占用了,第二个客户端过来连接的时候,他是可以连接的,但是处于一个占线的状态,就只能等着去跟服务端建立连接,除非一个客户端断开了(优雅的断开可以,如果是强制断开就会报错,因为服务端的程序还在第一个循环里面),然后就可以进行和服务端的通信了

八、基于udp协议下的socket

网络编程socket之一-LMLPHP

基于udp协议下的socket是不需要连接的。服务器端先初始化Socket,然后与端口绑定(bind),recvform接收消息,这个消息有两项,消息内容和对方客户端的地址,然后回复消息时也要带着你收到的这个客户端的地址,发送回去,最后关闭连接,一次交互结束

服务端
import
socket server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server_ip_port=('192.168.12.39',8888) server.bind(server_ip_port) while 1: from_client_msg, adrr = server.recvfrom(1024) print('来自%s的消息:%s'%(adrr,from_client_msg.decode('utf-8'))) if from_client_msg.decode('utf-8')=='bye': break msg=input('请输入:') msg1=msg+','+from_client_msg.decode('utf-8').replace('sb','alexsb') server.sendto(msg1.encode('utf-8'),adrr)
客户端
import
socket client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server_ip_port=('192.168.12.39',8888) while 1: msg=input('请输入:') client.sendto(msg.encode('utf-8'),server_ip_port) if msg=='bye': break from_server_msg,adrr=client.recvfrom(1024) print(from_server_msg.decode('utf-8')) client.close()
11-24 09:03