Unix/Linux网络编程实验一:Socket基本编程(python实现)

0.写在最前面

系列文章的作用

帮助有python基础的同学,了解网络通讯的原理,并使用python语言实现简单的通信。
阅读前需有以下基础:
1. 掌握python语言
2. 最好学过一点计算机网络原理

1.最基本的服务器与客户端

1.1 套接字:通信端点

1.1.1 套接字

套接字是计算机网络数据结构,在任何通信开始之前,网络应用程序必须创建套接字。可以将它们比作电话的插孔,没有它将无法通信。

套接字是一种抽象的封装,底部原理不用管他,知道怎么用就可以了。知道打电话要将电话接口接入插孔就行了,无需知道其原理

套接字最初是为同一主机上的应用程序所创建,使主机上运行的一个程序(进程)与另一个运行的程序进行通信。这就是所谓的进程间通信。

有两种类型的套接字:基于文件的和面向网络的。

1.1.1.1 基于文件的套接字(网络通讯不用管这方面的知识)

UNIX 套接字是我们所讲的套接字的第一个家族,并且拥有一个“家族名字”:AF_UNIX(又名 AF_LOCAL ,在 POSIX1.g 标准中指定),它代表地址家族(address family):UNIX。

包括 Python 在内的大多数受欢迎的平台都使用术语地址家族及其缩写 AF;其他比较旧的系统可能会将地址家族表示成域(domain)或协议家族(protocol family),并使用其缩写 PF 而非 AF。类似地,AF_LOCAL (在 2000~2001 年标准化)将代替 AF_UNIX。然而,考虑到后向兼容性,很多系统都同时使用二者,只是对同一个常数使用不同的别名。Python 本身仍然在使用 AF_UNIX

1.1.1.2 面向网络的套接字(重点掌握)

第二种类型的套接字是基于网络的,它也有自己的家族名字 AF_INET,或者地址家族:因特网。另一个地址家族 AF_INET6 用于第 6 版因特网协议(IPv6)寻址。此外,还有其他的地址家族,这些要么是专业的、过时的、很少使用的,要么是仍未实现的。在所有的地址家族之中,目前 AF_INET 是使用得最广泛的。

总的来说,Python 包含 AF_UNIX、AF_NETLINK、AF_TIPC 和 AF_INET等家族。下面的内容中,我们将使用 AF_INET。

1.1.2 套接字地址:主机-端口对

如果一个套接字像一个电话插孔一允许通信的一 些基础设施,那么主机名和端口号就像区号和电话号码的组合。

然而,拥有硬件和通信的能力本身并没什么卵用

即–你想实现通信(打电话给家里人),你有一台能打电话没什么卵用

因为你还需要知道家里的电话号码。

一个网络地址由主机名和端口号对组成,而这是网络通信所需要的。

有效的端口号范围为0~65535 (尽管小于1024的端口号预留给了系统)。如果你正在使用POSIX兼容系统(如Linux、MacOSX等),那么可以在/etc/services文件中找到预留端口号的列表(以及服务器/协议和套接字类型)。众所周知的端口号列表可以在这个网站中查看:http://www.iana.org/assignments/port-numbers。

网络通讯还需要知道主机端口对

通俗化解释SOCKET通讯原理

1.1.3 面向连接的套接字与无连接的套接字

1.1.3.1 面向连接的套接字

不管你采用的是哪种地址家族,都有两种不同风格的套接字连接。第一种是面向连接的,这意味着在进行通信之前必须先建立一个连接,例如,使用电话系统给一个朋友打电话。 这种类型的通信也称为虚拟电路或流套接字.。

面向连接的通信提供序列化的、可靠的和不重复的数据交付,而没有记录边界。这基本上意味着每条消息可以拆分成多个片段,并且每一条消息片段都能确保能够到达目的地,然后将他们按顺序组合在一起,最后将完整消息传递给正在等候的应用程序。

实现这种连接类型的主要协议是传输控制协议(更为人熟知的是它的缩写 TCP)。为了创建 TCP 套校字,必须使用 SOCK_STREAM 作为套接字类型。TCP 套接字的名字 SOCK_SIREAM 基于流套接字的其中一种表示。 因为这些套接字( AF_INHT )的网络版本使用因特网协议( IP ) 来搜寻网络中的主句,所以整个系统通常结合这两种协议( TCP 和 IP )来进行(当然,也可以使用 TCP 和本地[非网络的 AF_LOCALAF/ AF_UNIX]套接字,但是很明显此时并没有使用 IP )。

1.1.3.2 无连接的套接字

与虚拟电路形成鲜明对比的是数据报类型的套接字,它是种无连接的套接字。 这意味着,在通信开始之前并不需要建立连接。此时,在数据传输过程中并无法保证它的顺序性、可靠性或重复性。然而,数据报确实保存了记录边界,这就意味着消息是以整体发送的,而并非首先分成多个片段,例如,使用面向连接的协议。

使用数据报的消息传输可以比作邮政服务。信件和包裹或许并不能以发送顺序到达。事实上,它们可能不会到达。为了将其添加到并发通信中,在网络中甚至有可能存在重复的消息。

既然有这么多副作用,为什么还使用数据报呢(使用流套接字肯定有一些优势) ?由于面向连接的套接字所提供的保证,因此它们的设置以及对虚拟电路连接的维护需要大量的开销。然而,数据报不需要这些开销,即它的成本更加“低廉”。因此,它们通常能提供更好的性能,并且可能适合一些类型的应用程序。

实现这种连接类型的主要协议是用户数据报协议(更为人熟知的是其缩写 UDP)。 为了创建 UDP 套接字,必须使用 SOCK_DGRAM 作为套接字类型。你可能知道,UDP 套接字的 SOCK_ DGRAM 名字来自于单词“datagram”(数据报)。因为这些套接字也使用因特网协议来寻找网络中的主机,所以这个系统也有一个更加普通的名字,即这两种协议( UDP 和 IP ) 的组合名字,或 UDP/IP。

1.2 socket 模块函数

要创建套接字,必须使用 socket.socket()函数,它一般语法如下。

socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

其中,family(家族)应该是 AF_INET(默认),AF_INET6,AF_UNIX 等等。type(套接字类型)应该是SOCK_STREAM(默认),SOCK_DGRAM 等等,proto(协议号)通常为0。

所以,为了创建 TCP/IP 套接字,可以用下面的方式调用 socket.socket()。

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

同样,为了创建 UDP/IP 套接字,需要执行以下语句

udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

套接字对象socket(内置库)部分方法

**详见官方文档的Socket Objects部分https://docs.python.org/3.8/library/socket.html#socket-objects

1.3 创建服务器

首先,将展现创建通用 TCP 服务器的一般伪代码,然后对这些代码进行一般性的描述。需要记住的是,这仅仅是设计服务器的一种方式。一旦熟悉了服务器的设计,那么你将能够按照自己的要求修改下面的伪代码来操作服务器。

ss= socket()					# 创建服务器套接字
ss.bind()							# 套接字与地址绑定
ss.listen()						# 监听连接
try:									#try-except 进行异常捕获
    inf_loop:							# 服务器无限循环
    	cs = ss.accept()				# 接受客户端连接
    	comm_loop:					# 通信循环
     		cs.recv()/cs.send()		# 对话(接收/发送)
     		cs.close()						# 关闭客户端套接字
except Exception as e:
   print(e)
ss.close()						# 关闭服务器套接字# (可选)

1.4 创建客户端

1.4.1 一般通用模板

创建 TCP 客户端与服务器类似,先展示伪代码.


cs = socket()						# 创建服务器套接字
try:									#try-except 进行异常捕获
   cs.connect()						# 尝试连接服务器
   comm_loop:							# 通信循环
   	cs.send()/cs.recv()				# 对话(发送/接收)
except Exception as e:
   print(e)
cs.close()							# 关闭客户端套接字

14-1实验(python版本)

客户端

from socket import *

HOST = 'localhost'  #  或 '127.0.0.1'
POST = 3000
BUFSIZ = 1024
ADDR = (HOST, POST)
try:
	tcpCliSock = socket(AF_INET, SOCK_STREAM)
	tcpCliSock.connect(ADDR)
	
	while True:
	    data = input('> ')
	    if not data:
	        break
	    tcpCliSock.send(data.encode('utf-8'))
	    data = tcpCliSock.recv(BUFSIZ)
	    if not data:
	        break
	    print(data.decode('utf-8'))
except Exception as e:
	print(e)
tcpCliSock.close()

服务端

from socket import *
from time import ctime

HOST = ''
POST = 3000
BUFSIZ = 1024
ADDR = (HOST, POST)
try:
	tcpSerSock = socket(AF_INET, SOCK_STREAM)
	tcpSerSock.bind(ADDR)
	tcpSerSock.listen(5)  # listen的参数指的是服务器在拒绝新连接前最多接受的未连接数
	
	while True:
	    print('waiting for connnecting...')
	    
	    tcpCliSock, addr = tcpSerSock.accept()
	    print('...connecting from:', addr)
	    
	    while True:
	        data = tcpCliSock.recv(BUFSIZ)
	        if not data:
	            break
	        data = '[{}] {}'.format(ctime(), data.decode('utf-8'))
	        tcpCliSock.send(data.encode('utf-8'))
	
	    tcpCliSock.close()
except Exception as e:
	print(e)
tcpSerSock.close()

测试结果

参考博客

Python 极简socket多人聊天群 孤独暗星 2019-03-22 17:54:09

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值