一个Web服务之所以不会执行一段时间就结束,本质上是因为它在代码里启动了一个while死循环。另外一个重要特征是,它能够跟客户端打交道,需要进行网络通信,因此需要用到socket这个东西。
socket是一个4元组,标识TCP连接的两个终端:本地IP地址、本地端口、远程IP地址、远程端口。一个socket对唯一地标识着网络上的TCP连接。每个终端的IP地址和端口号,称为socket。服务器创建socket并开始接受客户端连接的流程如下: 1. 创建一个TCP/IP socket,socket.socket() 2. 设置一些socket选项,setsockopt()函数 3. 绑定指定地址,bind()函数 4. 将创建的socket变为监听socket,listen()函数
做完这些以后,服务器开始循环地一次接受一个客户端连接。当有连接到达时,accept调用返回已连接的客户端socket。然后服务器从这个socket读取请求数据,处理后发送一个响应给客户端。然后服务器关闭客户端连接,准备好接受新的客户端连接。
服务端:
TCP服务端一般需要下面几个操作:建立,绑定IP地址和端口,监听端口,等待连接,接收数据,传输数据 ,关闭连接
- 建立:server=socket.socket(socket.AF_INET, socket.SOCK_STREAM) 【参数默认就是socket.AF_INET, socket.SOCK_STREAM】
- 绑定端口:server.bind((‘IP地址’,端口)),【地址和端口号是一个 tuple 】
- 监听:server.listen()
- 接受连接: conn,addr=server.accept(),返回值是一个连接实例和一个地址,地址是连接过来的客户端地址,而数据操作要利用这个连接实例
- 传输数据:conn.send(data),【传输的数据必须是字节流,所以对字符串数据需要使用encode() 】
- 接收数据read:conn.recv(size),【传输的数据必须是字节流,size是接收的字节数,如果需要转成Unicode,需要使用decode() 】
- 关闭连接close:close()
1 | import socket |
上述代码存在一个问题:只能接受一次连接,连接结束后,服务端socket将关闭,更改成不立即关闭能等待下一个连接的:
1 | #服务器端 |
注:上述代码中在linux中正常运行,在windows中会报错!
如果要在windows中运行,需要捕获异常:
1 | #服务器端 |
客户端:
TCP客户端一般需要下面几个操作:建立socket,连接远程socket,传输数据 ,接收数据,关闭连接
- 建立:client=socket.socket()
- 连接:client.connect((‘IP地址’,端口)),其中地址和端口号是一个 tuple
- 传输数据:client.send(data),传输的数据必须是字节流,所以对字符串数据需要使用encode()
- 接收数据recv:client.recv(size),传输的数据是字节流,如果需要转成Unicode,需要使用decode()
- 关闭连接close:close()
1 | import socket |
- 上述代码存在一个问题:只能发送一次数据,发生完数据就会断开连接,改成可以多次发送数据,不自动断开的【前提是服务端能接收多次】:
1 | import socket |
正常情况下,程序运行到accept()函数就会被阻塞,等待客户端发起请求。
接下来再启动一个终端,运行client.py
client接收从server发送过来的字符串。然后继续运行。
client运行后,通过connect()函数向server发起请求,处于监听状态的server被激活,执行accept()函数,接受客户端的请求,然后执行send()函数向client传回数据。client接收到传回的数据后,通过recv()将数据读取出来。
通过socket()函数创建一个套接字,参数AF_INET表示使用IPv4地址,SOCK_STREAM 表示使用面向连接的套接字,在 Linux 中,socket 也是一种文件,有文件描述符,可以使用 send() / recv() 函数进行 I/O 操作。
通过 bind() 函数将套接字 serv_sock 与特定的 IP 地址和端口绑定。
socket() 函数确定了套接字的各种属性,bind() 函数让套接字与特定的IP地址和端口对应起来,这样客户端才能连接到该套接字。
让套接字处于被动监听状态。所谓被动监听,是指套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”。
accept() 函数用来接收客户端的请求。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求。
send() 函数用来向套接字文件中写入数据,也就是向客户端发送数据。
和普通文件一样,socket 在使用完毕后也要用 close() 关闭。
client与server区别 :
通过 connect() 向服务器发起请求,服务器的IP地址和端口号保存在client 结构体中。直到服务器传回数据后,直到退出循环,connect() 才运行结束。
通过 recv() 从套接字文件中读取数据。
UDP:
服务端:
UDP服务端通常有以下几个操作:创建socket,绑定端口,传输数据,接收数据
- 创建socket:server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- 绑定端口:server.bind(addr),【addr是一个元组,内容为(地址,端口)】
- 接收数据:data,client_addr=server.recvfrom(1024)
- 传输数据:server.sendto(data,client_addr)
1 | import socket |
客户端:
UDP客户端通常有以下几个操作:创建socket,传输数据,接收数据
- 创建socket:client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- 传输数据:server.sendto(data,addr),【addr是一个元组,内容为(地址,端口)】
- 接收数据:data,server_addr=client.recvfrom(1024)
1 | import socket,time |