Socket学习笔记(一)
1.socket介绍
我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。
能够唯一标示网络中的进程后,它们就可以利用socket进行通信了。
什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。
2.套接字发展史
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件类型的套接字家族
套接字家族的名字:AF_UNIX
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信(IPC)。
基于网络类型的套接字家族
套接字家族的名字:AF_INET
AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我么只使用AF_INET。
3.套接字工作流程
4.socket模块函数
1.socket(socket_family, socket_type, protocol=0)
socket_family: AF_UNIX 或 AF_INET。
socket_type:SOCK_STREAM(TCP) 或 SOCK_DGRAM(UDP)
protocol 一般不填,默认值为 0
- 创建一个 TCP/IP 的套接字:
TcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 创建一个 UDP/IP 的套接字:
UdpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
使用技巧:使用'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能大幅减短我们的代码。
2.套接字对象(内建)方法
- 服务器端套接字函数
s.bind() 绑定地址(主机,端口号对)到套接字
s.listen() 开始 TCP 监听
s.accept() 被动接受 TCP 客户的连接, (阻塞式)等待连接的到来
- 客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
- 公共用途的套接字函数
- s.getpeername() 连接到当前套接字的远端的地址
- s.getsockname() 当前套接字的地址
- s.getsockopt() 返回指定套接字的参数
- s.setsockopt() 设置指定套接字的参数
- s.close() 关闭套接字
- s.send() 发送 TCP 数据
- Return the number of bytes sent; this may be less than len(data) if the network is busy.
- s.sendall() 完整发送 TCP 数据
- This calls send() repeatedly until all data is sent. If an error occurs, it's impossible to tell how much data has been sent.
- s.recv() 接收 TCP 数据
- s.recvfrom() 接收 UDP 数据
- s.sendto() 发送 UDP 数据
面向锁的套接字方法
- s.setblocking() 设置套接字的阻塞与非阻塞模式
- s.settimeout() 设置阻塞套接字操作的超时时间
- s.gettimeout() 得到阻塞套接字操作的超时时间
- 面向文件的套接字的函数
- s.fileno() 套接字的文件描述符
- s.makefile() 创建一个与该套接字相关的文件
5.基于TCP的套接字编程
TCP服务端
- ss = socket() #创建服务器套接字
- ss.bind() #把地址绑定到套接字
- ss.listen() #监听链接
- inf_loop: #服务器无限循环
- cs = ss.accept() #接受客户端链接
- comm_loop: #通讯循环
- cs.recv()/cs.send() #对话(接收与发送)
- cs.close() #关闭客户端套接字
- ss.close() #关闭服务器套接字(可选)
TCP客户端
- cs = socket() # 创建客户套接字
- cs.connect() # 尝试连接服务器
- comm_loop: # 通讯循环
- cs.send()/cs.recv() # 对话(发送/接收)
- cs.close() # 关闭客户套接字
代码示例:
- # 服务端
- from socket import *
- ip_port = ('localhost', 8081)
- BUFSIZE = 1024
- s=socket(AF_INET,SOCK_STREAM)
- s.bind(ip_port)
- s.listen(5)
- while True:
- conn, addr = s.accept()
- while True:
- msg = conn.recv(BUFSIZE)
- if len(msg) == 0: break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生
- print(msg)
- conn.send(msg.upper())
- conn.close()
- s.close()
- # 客户端
- from socket import *
- ip_port = ('127.0.0.1', 8081)
- BUFSIZE = 1024
- s = socket(AF_INET, SOCK_STREAM)
- s.connect_ex(ip_port)
- while True:
- msg = input('>>: ').strip()
- if len(msg) == 0:continue
- s.send(msg.encode('utf-8'))
- feedback = s.recv(BUFSIZE)
- print(feedback.decode('utf-8'))
- s.close()
如果在连接过程中遇到端口已被占用,解决办法:
- #加入一条socket配置,重用ip和端口
- phone=socket(AF_INET,SOCK_STREAM)
- phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
- phone.bind(('127.0.0.1',8080))
6.基于UDP的套接字编程
UDP服务端
- ss = socket() #创建一个服务器的套接字
- ss.bind() #绑定服务器套接字
- inf_loop: #服务器无限循环
- cs = ss.recvfrom()/ss.sendto()
- ss.close()
UDP客户端
- cs = socket()
- comm_loop: # 通讯循环
- cs.sendto()/cs.recvfrom()
- cs.close()
代码示例:(UDP不用建立链接)
- # 服务端
- from socket import *
- ip_port = ('localhost',9000)
- BUF = 1024
- udp_server = socket(AF_INET,SOCK_DGRAM)
- udp_server.bind(ip_port)
- while True:
- msg,addr = udp_server.recvfrom(BUF)
- print(msg,addr)
- udp_server.sendto(msg.upper(),addr)
- # 客户端
- from socket import *
- ip_port = ('localhost',9000)
- BUF = 1024
- udp_client = socket(AF_INET,SOCK_DGRAM)
- while True:
- msg = input(">>>>:").strip()
- if not msg:continue
- udp_client.sendto(msg.encode('utf-8'), ip_port)
- back_msg,addr = udp_client.recvfrom(BUF)
- print(back_msg.decode('utf-8'),addr)
模拟QQ聊天
- # 服务端
- import socket
- ip_port=('127.0.0.1',8081)
- udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- udp_server_sock.bind(ip_port)
- while True:
- qq_msg,addr=udp_server_sock.recvfrom(1024)
- print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
- back_msg=input('回复消息: ').strip()
- udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
- # 客户端
- import socket
- BUFSIZE=1024
- udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- qq_name_dic={
- '悟空':('127.0.0.1',8081),
- '八戒':('127.0.0.1',8081),
- '沙和尚':('127.0.0.1',8081),
- '小白龙':('127.0.0.1',8081),
- }
- while True:
- qq_name=input('请选择聊天对象: ').strip()
- while True:
- msg=input('请输入消息,回车发送: ').strip()
- if msg == 'quit':break
- if not msg or not qq_name or qq_name not in qq_name_dic:continue
- udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
- back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
- print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
- udp_client_socket.close()
基于UDP协议发送数据的方式,不用建立双向链接。所以,可以同时链接多个客户端进行通信。
7.Tcp与Udp几个函数之间的区别
发消息,都是将数据发送到自己端操作系统的缓冲区中,收消息都是从自己端操作系统的缓冲区中取出数据。
tcp:send发消息,recv收消息
udp:sendto发消息,recvfrom收消息
- send与sendto
tcp是基于数据流的,而udp是基于数据报的:
send(bytes_data):发送数据流,数据流bytes_data若为空,自己这段的缓冲区也为空,操作系统不会控制tcp协议发空包
sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包。
- recv与recvfrom
- tcp协议:
- (1)如果收消息缓冲区里的数据为空,那么recv就会阻塞(阻塞很简单,就是一直在等着收)
- (2)只不过tcp协议的客户端send一个空数据就是真的空数据,客户端即使有无穷个send空,也跟没有一个样。
- (3)tcp基于链接通信
- 基于链接,则需要listen(backlog),指定半连接池的大小
- 基于链接,必须先运行的服务端,然后客户端发起链接请求
- 对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)
- 对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)
tcp协议
- udp协议
- (1)如果如果收消息缓冲区里的数据为“空”,recvfrom也会阻塞
- (2)只不过udp协议的客户端sendinto一个空数据并不是真的空数据(包含:空数据+地址信息,得到的报仍然不会为空),所以客户端只要有一个sendinto(不管是否发送空数据,都不是真的空数据),服务端就可以recvfrom到数据。
- (3)udp无链接
- 无链接,因而无需listen(backlog),更加没有什么连接池之说了
- 无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失
- recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错
- 只有sendinto发送数据没有recvfrom收数据,数据丢失
udp协议
基于TCP通信,如果客户端发送消息为空,那么服务端和客户端都会卡在recv状态,等待接受消息。如果客户端直接终止,那么就会报错。
而如果基于UDP通信,客户端发送消息为空,服务端会接受到一个空串。单独运行上面的udp的客户端,你发现并不会报错,相反tcp却会报错,因为udp协议只负责把包发出去,对方收不收,我根本不管,而tcp是基于链接的,必须有一个服务端先运行着,客户端去跟服务端建立链接然后依托于链接才能传递消息,任何一方试图把链接摧毁都会导致对方程序的崩溃。而且,UDP通信中,服务端有几个recvfrom就要对应几个sendinto,如果没有相应数量的与之对应,就会处于等待状态。
Socket学习笔记(一)的更多相关文章
- socket学习笔记——实现收发文件(Windows)
记录下来,供自己学习! server.c #define _CRT_SECURE_NO_DEPRECATE #include <stdio.h> #include <stdlib.h ...
- Socket学习笔记
..........(此处略去万万字)学习中曲折的过程不介绍了,直接说结果 我的学习方法,问自己三个问题,学习过程将围绕这三个问题进行 what:socket是什么 why:为什么要使用socket ...
- C# Socket学习笔记二
小记:昨天咱们已经了解了Socket的通信原理,可是点对点的一次通信并不是我们想要的,那么今天那我们就继续学习异步通信,简单来说就是服务器端和客户端可以进行多次 互发信息的通信而不用担心通道会关闭.在 ...
- C# Socket学习笔记一
小记:刚接触网络编程觉得网络是个神奇的东西,所以对它就很有兴趣,想了解下网络是如何进行进行数据传输的,那么开始第一天的学习吧!ReadyGo!!! 首先我们要了解一下几点内容: 1.网络中进程之间如何 ...
- socket学习笔记——select函数的使用(windows)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <winsock2.h ...
- socket学习笔记——线程(聊天程序)
server.c #include <stdio.h> #include <pthread.h> #include <semaphore.h> #include & ...
- socket学习笔记——select与epoll函数的使用(linux)
select.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <u ...
- socket学习笔记——并发服务器与I/O程序分割客户端
client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <u ...
- Java Socket 学习笔记
TCP协议的Socket编程 Socket:英文中的意思是插座.两个Java应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个Socket.Java中所有关于网络编程的类都 ...
- Python实战之网络编程socket学习笔记及简单练习
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0) 参数一:地址簇 socket.AF_INET IPv4(默认) socket.AF_IN ...
随机推荐
- WIN7 系统 右键计算机 点击管理 出现对话框:找不到文件。
解决方法: WIN+R组合键运行 “regedit” HKEY_LOCAL_MACHINE----SOFTWARE----Classes----CLSID----{20D04FE0-3AEA-1069 ...
- C语言与汇编语言对照分析
游戏通常会包含各种各样的功能,如战斗系统.UI渲染.经济系统.生产系统等,每个系统又包含各式各样子功能,如伤害判定.施法.使用道具.角色移动.玩家之间交易等等.这些游戏功能在代码实现中往往少不了条件判 ...
- Machine Learning系列--L0、L1、L2范数
今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化.我们先简单的来理解下常用的L0.L1.L2和核范数规则化.最后聊下规则化项参数的选择问题.这里因为篇幅比较庞大,为了不吓到大家,我将这个五个 ...
- Python Random模块生成伪随机数字
This module implements pseudo-random number generators for various distributions. 对于整数,有一个范围的均匀选择: 对 ...
- Eclipse java项目转换为web项目
1.打开.project文件,并修改文件, 修改如下: 找到:<natures> </natures>代码段,在代码段中加入如下内容并保存: <nature>org ...
- css 水平、垂直居中
水平居中 行内元素 行内元素:(img.span.文字等行内元素),通过在父级元素设置 text-align:center 使元素水平居中. 块级元素 块级元素:(div.p.h1...h6.ul.l ...
- 在Ubuntu上安装Redis MySQL MongoDB memcached Nginx
1.安装Redis sudo apt-get install redis-server 2.安装MySQL sudo apt-get install mysql-server 3.安装MongoDB ...
- serialVersionUID的作用(转)
本文系转载,原文链接:http://swiftlet.net/archives/1268 serialVersionUID适用于Java的序列化机制.简单来说,Java的序列化机制是通过判断类的ser ...
- HBase原理解析(转)
本文属于转载,原文链接:http://www.aboutyun.com/thread-7199-1-1.html 前提是大家至少了解HBase的基本需求和组件. 从大家最熟悉的客户端发起请求开始讲 ...
- 记点事! oracle 调用外部命令
oracle执行系统命令 测试成功环境:windows XP+oracle 10g.window 2008 R2 + 11g 代码如下: www.2cto.com Sql代码 crea ...