您的位置:首页 > 理论基础 > 计算机网络

python 网络编程,Socket 编程

2018-01-23 21:35 417 查看
 

有人说 ‘一切皆Socket!’

大家应该知道在python中一切皆对象,

而在网络中一切皆socket,现在的网络编程协议通信几乎都是用的socket.

我们现在聊天,上百度,发送邮件等等大多数的应用程序都是通过

socket来实现的,网络中的进程无处不在,所以一切皆socket

1. 本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类

a) 消息传递(管道、FIFO、消息队列)

b) 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)

c) 共享内存(匿名的和具名的)

d) 远程过程调用(Solaris门和Sun RPC)

2. 为什么网络可以通信?

a) 首先应该明白每一个计算机都有一个唯一的标识

b) 计算机的这个标识就是网络层的”ip地址”

c) 其次应该明白每一个系统的进程都是一个应用程序

d) 每一个进程都有一个标识,就是传输层的”协议+端口”

e) 利用这三元组(ip地址,端口,协议)就可以标识网络的进程了,网络中的进程通信就可以利用这个表示与其他的进程进行交互

3. 什么是socket?

a) Socket就是套接字,套接字是什么??

i. 应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。它就是用来数据通信的

ii. 大家知道协议吗?

1. 协议就是---数据的交换

2. 协议有俩种方式:

 a) 发send

 b) 收 receive

c) 协议之间的本质就是这俩种方式

d) 协议就是把这俩种方式进行了封装,这种封装整体叫做socket,这种封装就是将需要的tcp/ip,udp这些底层的协议进行了打包,大多的协议都是通过socket来进行数据的交换的

iii. 数据通信为服务器和客户端进行,一个服务器可以有多个客户端

 1. 客户端和客户端是不能直接进行通信的,只能通过服务器通信

iv. 给大家普及一下socket的起源

 在组网领域的首次使用是在1970年2月12日发布的文献IETF
RFC33中发现的,撰写者为Stephen Carr、Steve Crocker和Vint Cerf。根据美国计算机历史博物馆的记载,Croker写道:“命名空间的元素都可称为套接字接口。一个套接字接口构成一个连接的一端,而一个连接可完全由一对套接字接口规定。”计算机历史博物馆补充道:“这比BSD的套接字接口定义早了大约12年。”

 

Python 中,我们用 socket()函数来创建套接字,语法格式如下:

******** socket.socket([family[, type[, proto]]])

socket参数

· family:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

· type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。

· protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

服务器和客户端的连接:

在使用socket()函数的时候需要先导入socket模块

1. 当我们调用socket()创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数绑定,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

2. 所以我们使用bind()给他绑定一个ip地址和端口(host,port)

3. 作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,监听获取客户端的访问,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。注意:一般应用程序参数设成5就好

4. 在服务器做好这些操作后,就会监听指定的socket地址了,客户端就可以通过socket(),connect(),来连接服务器,connet()返回的是一个元组,这时候服务器的listen()监听客户端的访问后,就会调用accept()来接受客户端的连接请求,这样服务器和客户端之间就建立连接了

5. 注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。(关于这一点,现在看不懂别心急,以后会慢慢明白的)

 
***上面是服务器和客户端建立连接的基本操作,简述下来就是:
服务器通过socket()创建一个socket  -->  服务器通过bind()绑定一个IP地址和端口  -->  服务器调用listen() 监听(准备获取客户端的连接请求)  -->  客户端通过socket()创建一个socket  -->  客户端通过connect()给服务器发送连接请求  -->  此时服务器的listen()监听到客户端的这个连接请求,就会调用accept()来接受客户端的连接请求
 -->  这样服务器和客户端就建立连接可以传输数据了


6. 服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!

7. 网络I/O操作有下面几组

 a) read()/write()
 b) recv()/send()
 c) readv()/writev()
 d) recvmsg()/sendmsg()
 e) recvfrom()/sendto()
 f) 这个是一些读取和写入的函数
 g) 左边的是可以用来接受数据
 h) 右边的用来发送数据
下面给大家演示一下服务器和客户端建立连接:
服务器:

import socket
#协议     #协议类型为面向连接
serversocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = socket.gethostname()# 这个方法可以获取本机的主机标识

# 绑定端口和IP
serversocket.bind((host,9999))
print('服务器已经等待')#这个时候服务就已经做好了准备

# 设置最大连接数,并监听,超过后排队
serversocket.listen(5)

#接收客户端的连接,类型为元组,表示得到的客户端的地址和端口
c = serversocket.accept()

print(c)#打印后会发现这是一个元组,里面包括ip地址,协议,端口

while True:     #c[0]指的是主机 因为accept()获取的是一个元组
msg = c[0].recv(1024) # 接收客户端的消息,1024是指一个范围(接收小于 1024 字节的数据)
print(msg.decode())#将收到的消息进行解码并打印

#下面的是服务器像客户端发送消息,至于为什么要注释你运行一下就知道了
# msg = input('>>>') # 用户输入想要发送的消息
# c[0].send(msg.encode('utf-8'))# 服务器发送给客户端消息,此时的消息进行的编码

客户端:

import socket

# 协议      #协议类型为面向连接
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

host = socket.gethostname()# 获取本机的主机

client.connect((host, 9999))# #此时客户端开始向服务器发送连接请求
print('客户端已连接')
while True:
msg = input('>>>') # 用户输入想要发送的消息
client.send(msg.encode())# 客户端发送给服务器消息,此时的消息进行的编码

# 下面是客户端接受服务器消息至于为什么要注释你运行一下就知道了
# msg = client.recv(1024) # 接收消息
# print(msg.decode()) # 将收到的消息进行解码并打印

在你们将注释解除以后会发现 客户端不能向服务器发送消息了:

至于这是为什么?
你自己好好想想吧孩子

8. close()函数

a) 在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。
b) close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

9. TCP的状态:SYN, FIN, ACK, PSH, RST, URG.

a) 它们的含义是:
 b) SYN表示建立连接,
 c) FIN表示关闭连接,
 d) ACK表示响应,
 e) PSH表示有 DATA数据传输,
 f) RST表示连接重置。
其中,ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,

如果只是单个的一个SYN,它表示的只是建立连接。

TCP的几次握手就是通过这样的ACK表现出来的。

 但SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。

RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。

 一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。

PSH为1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。

TCP的连接建立和连接关闭,都是通过请求-响应的模式完成的。

10. 三次握手

 a) 假设a/b双方要进行数据通信,a必须发送一个syn(请求建立连接)的请求,当b收到后会发送一个syn+ack(响应连接)的响应,a收到b的响应后要发送一个ack,意思是a收到了b的响应,然后这个远程就建立了,这样就可以发送文件了

b) 为什么要这样: 因为这样才能确保双方可以正确建立连接

    


 c) 从上面图中可以看出当客户端调用connect()时会像服务器发送建立连接  -->  服务器监听到后就会调用accept()进行响应  -->  客户端收到响应后connect()就会发出另一个响应  -->  服务器接受到后会通过accept()返回,此时三次握手成功,建立连接

11. 四次断开

     


 a) 在一个应用程序里面首先一个客户端调用了close()主动关闭了连接,这时候会发送一个关闭连接  --> 服务器接收到后执行被动关闭,同时将接收到的传递给应用进程  --> 一段时间后应用程序调用close()关闭他的socket,同时会也会发送一个关闭连接
 -->  客户端接收到后会对它进行确认

知识点:

网络七层模型 OSI:

分为:

7.应用层

6.表示层

5.会话层

4.数据传输层

3.网络层

2.数据链路层  mac地址

下面那个物理地址就是mac地址,是一个16进制的数

 


1.物理硬件层

 通过cmd ipconfig /all(显示cmd详细信息) 可查看window ip配置.无限局域网适配器.以太网适配器

这是网络编程的基本概念,是一个金字塔类型,一层比一层高级

解释一下上面为什么不能运行成功

原因很简单,因为它是单线程的,要想实现他们表要调用threading多线程模块

多线程实现服务器和客户端的连接

服务器:

import socket
import threading
#协议     #协议类型为面向连接
serversocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = socket.gethostname()# 这个方法可以获取本机的主机标识

# 绑定端口和IP
serversocket.bind((host,9999))
print('服务器已经等待')#这个时候服务就已经做好了准备
# 设置最大连接数,并监听,超过后排队
serversocket.listen(5)
#接收客户端的连接,类型为元组,表示得到的客户端的地址和端口
c = serversocket.accept()
print(c)#打印后会发现这是一个元组,里面包括ip地址,协议,端口
def myrecv(c):
while True:
msg = c[0].recv(1024) # 接收客户端的消息,1024是指一个范围(接收小于 1024 字节的数据)
print(msg.decode())#打印出接收到的数据

threading._start_new_thread(myrecv,(c,))

def myinput():
while True:
msg = input('>>>') # 用户输入想要发送的消息
c[0].send(msg.encode('utf-8'))# 服务器发送给客户端消息,此时的消息进行的编码 c[0]指ip地址
threading._start_new_thread(myinput(),)


客户端:

import socket
import threading
#协议      #协议类型为面向连接
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = socket.gethostname()# 获取本机的主机标识

client.connect((host, 9999))
print('客户端已连接')#此时客户端开始发送连接请求
def myrecv(c):
while True:
msg = c.recv(1024) # 接收消息
print(msg.decode())
threading._start_new_thread(myrecv,(client,))

def myinput():
while True:
msg = input('>>>')
client.send(msg.encode())# 发送消息
threading._start_new_thread(myinput())


此时你已经运行是上面的代码你会发现可以同时运行了

哈哈哈是不是很神奇
有人说在python中多线程是一个鸡肋,但是不是只能由你来判断

你要学会将学过的东西灵活运用,加油吧小伙子

     ***特别说明:上面有一些借鉴于大神gneveek的博客
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: