原文出处: Andrew_liu

python的网络变成比c语言简单许多, 封装许多底层的实现细节, 方便程序员使用的同时, 也使程序员比较难了解一些底层的东西, 我觉得学网络编程还是用c语言更好一点.

写这篇博文, 也希望回顾并整理一下以前学过的c语言和linux下一些东西, 会将一些Linux网络编程的函数和Python网络变成函数做一个简单的对照, 方便记忆

1. Socket套接字的概念


Socket(翻译为套接字, 我觉得很挫),是操作系统内核中的一个数据结构,它是网络中的节点进行相互通信的门户。它是网络进程的ID。网络通信,归根到底还是进程间的通信(不同计算机上的进程间通信, 又称进程间通信, IP协议进行的主要是端到端通信)。在网络中,每一个节点(计算机或路由)都有一个网络地址,也就是IP地址。两个进程通信时,首先要确定各自所在的网络节点的网络地址。但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接口中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系。

所以,使用端口号和网络地址的组合可以唯一的确定整个网络中的一个网络进程.

端口号的范围从0~65535,一类是由互联网指派名字和号码公司ICANN负责分配给一些常用的应用程序固定使用的“周知的端口”,其值一般为0~1023, 用户自定义端口号一般大于等于1024, 我比较喜欢用8888

每一个socket都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过socket来实现的

1.1. Socket类型

socket类型在Liunx和Python是一样的, 只是Python中的类型都定义在socket模块中, 调用方式socket.SOCK_XXXX

  • 流式socket(SOCK_STREAM) 用于TCP通信

流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性

  • 数据报socket(SOCK_DGRAM) 用于UDP通信

数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP

  • 原始socket(SOCK_RAW) 用于新的网络协议实现的测试等

原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以, 其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。

2. Socket编程


2.1. TCP通信

TCP通信的基本步骤如下:
服务端:socket—bind—listen—while(True){—accept—recv—send—-}—close
客户端:socket———————————-connect—send—recv——-close

TCP

socket函数
使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14

#Linux
int socket(int domain, int type, int protocol);
domain:AF_INET:Ipv4网络协议 AF_INET6:IPv6网络协议
type : tcp:SOCK_STREAM   udp:SOCK_DGRAM
protocol : 指定socket所使用的传输协议编号。通常为0.
返回值:成功则返回套接口描述符,失败返回-1。
#python
socket.socket([family[, type[, proto]]])
family : AF_INET (默认ipv4), AF_INET6(ipv6) or AF_UNIX(Unix系统进程间通信).
type : SOCK_STREAM (TCP), SOCK_DGRAM(UDP) .
protocol : 一般为0或者默认
如果socket创建失败会抛出一个socket.error异常

2.1.1. 服务器端函数

bind函数
将套接字绑定到地址, python下,以元组(host,port)的形式表示地址, Linux下使用sockaddr_in结构体指针

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

#Linux
int bind(int sockfd, struct sockaddr * my_addr, int addrlen);
sockfd : 前面socket()的返回值
my_addr : 结构体指针变量
#####
struct sockaddr_in  //常用的结构体
{
unsigned short int sin_family;  //即为sa_family AF_INET
uint16_t sin_port;  //为使用的port编号
struct in_addr sin_addr;  //为IP地址
unsigned char sin_zero[8];  //未使用
};
struct in_addr
{
uint32_t s_addr;
};
####
addrlen : sockaddr的结构体长度。通常是计算sizeof(struct sockaddr);
返回值:成功则返回0,失败返回-1
#python
s.bind(address)
s为socket.socket()返回的套接字对象
address为元组(host,port)
host: ip地址, 为一个字符串
post: 自定义主机号, 为整型

listen函数
使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求。如果客户端有连接请求,端口就会接受这个连接

Python
1
2
3
4
5
6
7
8
9
10

#Linux
int listen(int sockfd,int backlog);
sockfd : 为前面socket的返回值.
backlog : 指定同时能处理的最大连接要求,通常为10或者5。最大值可设至128
返回值:成功则返回0,失败返回-1
#python
s.listen(backlog)
s为socket.socket()返回的套接字对象
backlog : 操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了

accept函数
接受远程计算机的连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求。

Python
1
2
3
4
5
6
7
8
9
10
11

#Linux
int accept(int s,struct sockaddr * addr,int * addrlen);
sockfd : 为前面socket的返回值.
addr : 为结构体指针变量,和bind的结构体是同种类型的,系统会把远程主机的信息(远程主机的地址和端口号信息)保存到这个指针所指的结构体中。
addrlen : 表示结构体的长度,为整型指针
返回值:成功则返回新的socket处理代码new_fd,失败返回-1
#python
s.accept()
s为socket.socket()返回的套接字对象
返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址

2.1.2. 客户端函数

connect函数
用来请求连接远程服务器

Python
1
2
3
4
5
6
7
8
9
10
11

#Linux
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
sockfd : 为前面socket的返回值.
serv_addr : 为结构体指针变量,存储着远程服务器的IP与端口号信息
addrlen : 表示结构体变量的长度
返回值:成功则返回0,失败返回-1
#python
s.connect(address)
s为socket.socket()返回的套接字对象
address : 格式为元组(hostname,port),如果连接出错,返回socket.error错误

2.1.3. 通用函数

接收远端主机传来的数据

recv函数

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#Linux
int recv(int sockfd,void *buf,int len,unsigned int flags);
sockfd : 为前面accept的返回值.也就是新的套接字。
buf : 表示缓冲区
len : 表示缓冲区的长度
flags : 通常为0
返回值:成功则返回实际接收到的字符数,可能会少于你所指定的接收长度。失败返回-1
#python
s.recv(bufsize[,flag])
s为socket.socket()返回的套接字对象
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略
返回值为数据以字符串形式

send函数
发送数据给指定的远端主机

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#Linux
int send(int s,const void * msg,int len,unsigned int flags);
sockfd : 为前面socket的返回值.
msg : 一般为常量字符串
len : 表示长度
flags : 通常为0
返回值:成功则返回实际传送出去的字符数,可能会少于你所指定的发送长度。失败返回-1
#python
s.send(string[,flag])
s为socket.socket()返回的套接字对象
string : 要发送的字符串数据
flag : 提供有关消息的其他信息,通常可以忽略
返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall(string[,flag])
#完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。
返回值 : 成功返回None,失败则抛出异常。

close函数
关闭套接字

Python
1
2
3
4
5
6
7
8

#Linux
int close(int fd);
fd : 为前面的sockfd
返回值:若文件顺利关闭则返回0,发生错误时返回-1
#python
s.close()
s为socket.socket()返回的套接字对象

2.2. 简单的客户端服务器TCP连接

一个简单的回显服务器和客户端模型, 客户端发出的数据, 服务器会回显到客户端的终端上(只是一个简单的模型, 没考虑错误处理等问题)

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#服务器端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket   #socket模块
import commands   #执行系统命令模块
BUF_SIZE = 1024  #设置缓冲区大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #生成一个新的socket对象
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  #设置地址复用
server.bind(server_addr)  #绑定地址
server.listen(5)  #监听, 最大监听数为5
while True:
    client, client_addr = server.accept()  #接收TCP连接, 并返回新的套接字和地址
    print 'Connected by', client_addr
    while True :
        data = client.recv(BUF_SIZE)  #从客户端接收数据
        print data
        client.sendall(data)  #发送数据到客户端
server.close()

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

#客户端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import socket
BUF_SIZE = 1024  #设置缓冲区的大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
try :
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #返回新的socket对象
except socket.error, msg :
    print "Creating Socket Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
    sys.exit()
client.connect(server_addr)  #要连接的服务器地址
while True:
    data = raw_input("Please input some string > ")  
    if not data :
        print "input can't empty, Please input again.."
        continue
    client.sendall(data)  #发送数据到服务器
    data = client.recv(BUF_SIZE)  #从服务器端接收数据
    print data
client.close()

2.2.1. 带错误处理的客户端服务器TCP连接

在进行网络编程时, 最好使用大量的错误处理, 能够尽量的发现错误, 也能够使代码显得更加严谨

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#服务器端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import socket   #socket模块
BUF_SIZE = 1024  #设置缓冲区大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
try :
  server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #生成一个新的socket对象
except socket.error, msg :
    print "Creating Socket Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
    sys.exit()
print "Socket Created!"
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  #设置地址复用
try :
    server.bind(server_addr)  #绑定地址
except socket.error, msg :
  print "Binding Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
  sys.exit()
print "Socket Bind!"
server.listen(5)  #监听, 最大监听数为5
print "Socket listening"
while True:
    client, client_addr = server.accept()  #接收TCP连接, 并返回新的套接字和地址, 阻塞函数
    print 'Connected by', client_addr
    while True :
        data = client.recv(BUF_SIZE)  #从客户端接收数据
        print data
        client.sendall(data)  #发送数据到客户端
server.close()

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

#客户端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import socket
BUF_SIZE = 1024  #设置缓冲区的大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
try :
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #返回新的socket对象
except socket.error, msg :
    print "Creating Socket Failure. Error Code : " + str(msg[0]) + " Message : " + msg[1]
    sys.exit()
client.connect(server_addr)  #要连接的服务器地址
while True:
    data = raw_input("Please input some string > ")  
    if not data :
        print "input can't empty, Please input again.."
        continue
    client.sendall(data)  #发送数据到服务器
    data = client.recv(BUF_SIZE)  #从服务器端接收数据
    print data
client.close()

2.3. UDP通信

UDP通信流程图如下:
服务端:socket—bind—recvfrom—sendto—close
客户端:socket———-sendto—recvfrom—close


UDP

sendto()函数
发送UDP数据, 将数据发送到套接字

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#Linux
int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
sockfd : 为前面socket的返回值.
msg : 一般为常量字符串
len : 表示长度
flags : 通常为0
to : 表示目地机的IP地址和端口号信息, 表示地址的结构体
tolen : 常常被赋值为sizeof (struct sockaddr)
返回值 : 返回实际发送的数据字节长度或在出现发送错误时返回-1。
#Python
s.sendto(string[,flag],address)
s为socket.socket()返回的套接字对象
address : 指定远程地址, 形式为(ipaddr,port)的元组
flag : 提供有关消息的其他信息,通常可以忽略
返回值 : 发送的字节数。

recvfrom()函数
接受UDP套接字的数据, 与recv()类似

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#Linux
int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
sockfd : 为前面socket的返回值.
msg : 一般为常量字符串
len : 表示长度
flags : 通常为0
from :是一个struct sockaddr类型的变量,该变量保存连接机的IP地址及端口号
fromlen : 常置为sizeof (struct sockaddr)。
返回值 : 返回接收到的字节数或当出现错误时返回-1,并置相应的errno。
#Python
s.recvfrom(bufsize[.flag])
返回值 : (data,address)元组, 其中data是包含接收数据的字符串,address是发送数据的套接字地址
bufsize : 指定要接收的数据大小
flag : 提供有关消息的其他信息,通常可以忽略

2.4. 简单的客户端服务器UDP连接

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#服务器端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
BUF_SIZE = 1024  #设置缓冲区大小
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  #生成新的套接字对象
server.bind(server_addr)  #套接字绑定IP和端口
while True :
    print "waitting for data"
    data, client_addr = server.recvfrom(BUF_SIZE)  #从客户端接收数据
    print 'Connected by', client_addr, ' Receive Data : ', data
    server.sendto(data, client_addr)  #发送数据给客户端
server.close()

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#客户端
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import struct
BUF_SIZE = 1024  #设置缓冲区
server_addr = ('127.0.0.1', 8888)  #IP和端口构成表示地址
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  #生成新的套接字对象
while True :
    data = raw_input('Please Input data > ')
    client.sendto(data, server_addr)  #向服务器发送数据
    data, addr = client.recvfrom(BUF_SIZE)  #从服务器接收数据
    print "Data : ", data
client.close()

2.5. 其他

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

s.getpeername()
#返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname()
#返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value)
#设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen])
#返回套接字选项的值。
s.settimeout(timeout)
#设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout()
#返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno()
#返回套接字的文件描述符。
s.setblocking(flag)
#如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile()
#创建一个与该套接字相关连的文件

转载于:https://www.cnblogs.com/gswang/p/7478827.html

Python 爬虫 (三) - Socket 网络编程相关推荐

  1. python开发之Socket网络编程

    python开发之Socket网络编程 一.SOCKET基础 二.SOCKET通信流程 1. 流程描述: 2. 相关方法及参数介绍: 三.实例(屌丝追女神的故事) 1. 故事1:(一次通信) 2. 故 ...

  2. Python之TCP Socket网络编程

    0 背景 Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网 ...

  3. python网络编程讲解_详解Python Socket网络编程

    Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...

  4. python学习笔记(十 三)、网络编程

    最近心情有点儿浮躁,难以静下心来 Python提供了强大的网络编程支持,很多库实现了常见的网络协议以及基于这些协议的抽象层,让你能够专注于程序的逻辑,而无需关心通过线路来传输比特的问题. 1 几个网络 ...

  5. Python面向对象进阶和socket网络编程

    写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese:def __init__(self, ...

  6. python编程入门与案例详解-Pythony运维入门之Socket网络编程详解

    Socket是什么? Socket 是电脑网络中进程间数据流的端点Socket 是操作系统的通信机制应用程序通过Socket进行网络数据的传输 首先,简单了解一下TCP通信过程: TCP三次握手(面试 ...

  7. Python Socket网络编程(二)局域网内和局域网与广域网的持续通信

    目录 前言 IP地址 简介 公有IP 私有IP 局域网之间网络通信 前提 功能描述 源码 运行结果 局域网与广域网网络通信 前提 源码 结语 前言 本系列博客是笔者学习Python Socket的过程 ...

  8. Python Socket网络编程(一)初识Socket和Socket初步使用

    目录 前言 网络编程 实质 IP地址和端口 数据传输协议 协议 Socket 概念 套接字 socket对象方法 初步使用 功能 源码 运行结果 结语 前言 本系列博客是笔者学习Python Sock ...

  9. python socket mysql_5.Python操作MySQL,三层架构,Socket网络编程

    Python操作MySQL - MySQL之查询操作 - MySQL之插入数据 - MySQL之批量插入数据 - MySQL之删除数据 - MySQL之更新数据库 - MySQL之事务 - MySQL ...

最新文章

  1. ASP.NET网站建设基本常用代码
  2. 风变编程python小课课件_风变编程Python小课最近很火,大家学完感受如何?
  3. PHP是弱类型语言,自动转换,强制转换
  4. 蓝桥杯-矩阵乘法(java)
  5. 超重磅 | 神策数据“两云一平台”上线,拥抱数字化经营
  6. 在quartusii如何设计出一个 3 位的十进制加法计数器的原理以及它的设计电_从算盘到计算机,从十进制到二进制,人类计算能力的提升...
  7. JDBC的DML增删改查的代码重构设计(下)
  8. 【java笔记】Object类
  9. 微星主板不用DrMOS了?
  10. python出租车收费_使用Python分析纽约出租车搭乘数据
  11. 微信全球MBA创新大赛Roadshow首站登陆斯坦福
  12. Redis重启数据丢失问题
  13. GPU设备架构全面解析(持续更新ing)
  14. IDC最新中国BI市场报告,永洪科技等中国厂商领跑
  15. python里的非_python中或与非
  16. 个人微信小程序开发入门教程:注册个人小程序
  17. 什么是非关系型数据库,Redis概述、安装及部署Redis群集
  18. 拼手气红包算法_线段切割法
  19. mac os 开启redis_在Mac os x 安装 Redis
  20. java设置select选中_按值设置选择选项'selected'

热门文章

  1. 一步一步学FRDM-KE02Z(一):IAR调试平台搭建以及OpenSDA两种工作模式设置
  2. 青云QingCloud打造云端ICT服务 实现战略全面升级
  3. 如何制作手绘地图?如何将图片图层精确地对准在地图上?
  4. 【转】使用oschina的git服务器
  5. Discuz经典函数注释之authcode
  6. codeforces 369C Valera and Elections
  7. Convert your single instance to 10g RAC by manual
  8. 如何获取Oracle存储过程中的参数名称、类型?
  9. 在线代理和缓存工具(转)
  10. 物联卡使用寿命短常具备的四个特征