一、认识套接字
TCP用主机的IP地址加上主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。
套接字用(IP地址:端口号)表示。
它是网络通信过程中端点的抽象表示,包含进行网络通信必需的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
套接字(socket)是一种通信机制,凭借这种机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行,Linux所提供的功能(如打印服 务,ftp等)通常都是通过套接字来进行通信的,套接字的创建和使用与管道是有区别的,因为套接字明确地将客户和服务器区分出来,套接字可以实现将多个客 户连接到一个服务器。
但是说了半天,还是理解不了什么是套接字,其实这是一个狗屎一样的翻译问题:
谷歌翻译中socket直译为插座,字典中关于socket的解释为:an electrical device receiving a plug or light bulb to make a connection。译为:接收插头或灯泡以进行连接的电气设备。用c/s模型图来更好的理解插座的意义:
服务器就像一个大插排,包含很多插座,客户端就是像一个插头,每一个线程代表一条电线,客户端将电线的插头插到服务器插排上对应的插座上,就可以开始通信了。
数据传输需要通过协议,而套接字就是给数据传输添加协议的。
二、套接字特性
1、套接字属性
套接字的特性由3个属性确定,他们是,域,类型和协议
域指定套接字通信中使用的网络介质,最常见的套接字域是AF_INET,它指的是Internet网络
2、套接字类型
一个套接字可能有多种不同的通信方式
流套接字,流套接字提供一个有序,可靠,双向节流的链接,流套接字由类型SOCK_STREAM指定,它是在AF_INET域中通过TCP/IP链接实现的,这就是套接字类型(其实就是通信方式)
与流套接字相反,由类型SOCK_DGRAM指定的数据报套接字不建立和维持一个连接,它对可以发送的数据长度有限制,数据报作为一个单独的网络消息被传输,它可能会丢失,复制或乱序
3、套接字协议
套接字协议,通常使用默认就可以了(也就是最后一个参数填0)
三、基于套接字的TCP网络编程
1、创建流程概述
- socket创建一个套接字,SOCK_DGRAM是用在UDP上的,而SOCK_STREAM是用于TCP协议的。
- bind绑定IP和port
- listen使套接字变为可以被动链接
- accept等待客户端的链接
- close关闭资源
2、socket模块常用于TCP的方法
- connect(address):连接远程计算机
- send():发送数据
- recv():接收数据
- listen:开始监听,等待客户端连接
客户端实例:
-
from socket import *
-
#建立套接字,第二个参数是流式套接字 tcp专用
-
clientd_socket = socket(AF_INET,SOCK_STREAM)
-
#目标主机,也就是服务器的ip和端口
-
serverAddress = ('10.10.16.194',9999)
-
#主动和服务器发起链接
-
clientd_socket.connect(serverAddress)
-
-
data = input('请输入数据')
-
#发送数据
-
clientd_socket.send(data.encode('utf-8'))
-
#接受数据
-
re = clientd_socket.recv(128)
-
print(re.decode('utf-8'))
-
-
clientd_socket.close()
- accept():响应客户端的请求
阻塞当前线程
如果没有计算机连接,一直等待
如果有计算机连接,响应客户端
服务端实例:
-
from socket import *
-
server_socket = socket(AF_INET,SOCK_STREAM)
-
-
local_address = ('10.10.16.194',9999)
-
#绑定本机ip和端口,那么此程序就只接收此端口好的所有网络链接
-
server_socket.bind(local_address)
-
-
#限制接受5个客户端的数据
-
server_socket.listen(5)
-
#接受链接,如果没有链接那么处于阻塞状态。
-
#有链接的时候,accept会返回两个值,第一个是socket链接,第二个是远程地址
-
lianjie,remoteAddress = server_socket.accept()
-
re = lianjie.recv(128)
-
print(re.decode('utf-8'))
-
#发送数据
-
data = input('请输入发送内容')
-
lianjie.send(data.encode('utf-8'))
-
-
lianjie.close() #server_socket中的其中一个链接
-
server_socket.close() #server_socket相当于一个大的容器
先运行服务端,在运行客户端:结果
客户端:
服务端:
3、改进一下,让程序不会在一收一发后停止
客户端:
-
-
from socket import *
-
clientd_socket = socket(AF_INET,SOCK_STREAM)
-
serverAddress = ('10.10.16.194',9999)
-
clientd_socket.connect(serverAddress)
-
while True:
-
data = input('请输入数据')
-
clientd_socket.send(data.encode('utf-8'))
-
re = clientd_socket.recv(128)
-
print(re.decode('utf-8'))
服务端:
-
from socket import *
-
server_socket = socket(AF_INET,SOCK_STREAM)
-
local_address = ('10.10.16.194',9999)
-
server_socket.bind(local_address)
-
server_socket.listen(5)
-
lianjie,remoteAddress = server_socket.accept()
-
while True:
-
re = lianjie.recv(128)
-
print(re.decode('utf-8'))
-
data = input('请输入发送内容')
-
lianjie.send(data.encode('utf-8'))
-
lianjie.close()
-
server_socket.close()