欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

网络编程socket之一

程序员文章站 2023-11-17 18:02:10
从今年10月22号开始我的python学习之路,一个月下来,磕磕碰碰,勉勉强强把基础部分算是学完了,一个月走过来,我过着别人看似单调,重复的生活,确实是,每天,每周都是一样的生活模式,早上7点40起床,吃个早餐,8点到达教室,中午1点去吃个午饭,然后回到教室,下午6点去吃个晚饭,然后回到教室,待到晚 ......

  从今年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之一

网络编程socket之一

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

  一、从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之一

五,tcp协议和udp协议区别

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

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

网络编程socket之一

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

网络编程socket之一

三次握手:

  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之一

网络编程socket之一

七、基于tcp下的socket

网络编程socket之一

在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之一

基于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()