1 socket

插座?呵呵,想多了,翻译过来意思是套接字!

A network socket is an internal endpoint for sending or receiving data at a single node in a computer network. Concretely, it is a representation of this endpoint in networking software (protocol stack), such as an entry in a table (listing communication protocol, destination, status, etc.), and is a form of system resource.

socket是一种进程间通信机制(inter process communication ,IPC),提供一种供应用程序访问通信协议的操作系统调用,并且通过将socket与Unix系统文件描述符相整合,使得网络读写数据(或者服务调用)和读写本地文件一样容易。很显然,这货已经离插座越来越远了,已经完全不再是硬件上的物件,而是一序列的“指令” ,按汉语的理解,已经具备了“套接”(建立网络通讯或进程间通讯)和“字”(可交互的有序指令串)的概念。
2 socket address :主机-端口对
如果一个套接字像一个电话插孔——允许通信的一些基础设施,name主机名和端口号就像区号和电话号码的组合。
有效的端口号范围0-65535,尽管小于1024的端口号预留给了系统,Linux和mac os 可以在/etc/services文件中找到预留端口号的列表。
A socket address is the combination of an IP address and a port number, much like one end of a telephone connection is the combination of a phone number and a particular extension. Sockets need not have an address (for example for only sending data), but if a program binds a socket to an address, the socket can be used to receive data sent to that address. Based on this address, internet sockets deliver incoming data packets to the appropriate application process or thread.
有两种类型的套接字:基于文件的和面向网络的
AF:address family地址家族
AF_UNIX 面向文件
AF_INET面向网络

参数一:地址簇

  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型

  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议

  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

3.面向连接的套接字也称为虚拟电路或流套接字

面向连接的通信提供序列化的、可靠的和不重复的数据交付,并且没有记录边界,实现这种连接的主要协议是TCP(传输控制协议)。

创建TCP套接字必须使用SOCK_STREAM作为套接字类型。

无连接的传输无法保证传输的内容的顺序性、可靠性。无连接传输的优势是没有维护虚拟电路连接带来的开销,从而拥有更低的成本。实现无连接的主要协议是UDP(用户数据报协议),创建UDP套接字必须使用SOCK_DGRAM作为套接字类型。

4.Socket 参数介绍

sk.bind(address)
  s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 sk.listen(backlog)
  开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列 sk.setblocking(bool)
  是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 sk.accept()
  接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
  接收TCP 客户的连接(阻塞式)等待连接的到来 sk.connect(address)
  连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 sk.connect_ex(address)
  同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 sk.close()
  关闭套接字 sk.recv(bufsize[,flag])
  接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 sk.recvfrom(bufsize[.flag])
  与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 sk.send(string[,flag])
  将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 sk.sendall(string[,flag])
  将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 内部通过递归调用send,将所有内容发送出去。 sk.sendto(string[,flag],address)
  将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 sk.settimeout(timeout)
  设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) sk.getpeername()
  返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 sk.getsockname()
  返回套接字自己的地址。通常是一个元组(ipaddr,port) sk.fileno()
  套接字的文件描述符

5.创建socket

socket.socket(AddressFamily, Type)

函数 socket.socket 创建⼀个 socket, 返回该 socket 的描述符, 该函数带有两个参数:

Address Family: 可以选择 AF_INET( ⽤于 Internet 进程间通信) 或者AF_UNIX( ⽤于同⼀台机器进程间通信) ,实际⼯作中常⽤AF_INET

Type: 套接字类型, 可以是 SOCK_STREAM( 流式套接字, 主要⽤于TCP 协议) 或者 SOCK_DGRAM( 数据报套接字, 主要⽤于 UDP 协议)创建⼀个tcp socket( tcp套接字)

5.1创建⼀个tcp socket( tcp套接字)

TCP通信需要建立一个可靠连接的过程,而且通信双方以流的形式发送数据。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print 'Socket Created'

tcp_server

# -*- coding: utf-8 -*-
# 2017/11/25 16:31
import socket
import threading
import time
def dealClient(sock, addr):
#第四步:接收传来的数据,并发送给对方数据
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Hello,I am server!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
print('-->>%s!' % data.decode('utf-8'))
sock.send(('Loop_Msg: %s!' % data.decode('utf-8')).encode('utf-8'))
#第五步:关闭套接字
sock.close()
print('Connection from %s:%s closed.' % addr) if __name__=="__main__":
#第一步:创建一个基于IPv4和TCP协议的Socket
# 套接字绑定的IP(127.0.0.1为本机ip)与端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('127.0.0.1', 9999))
#第二步:监听连接
s.listen(5)
print('Waiting for connection...')
while True:
# 第三步:接受一个新连接:
sock, addr = s.accept()
# 创建新线程来处理TCP连接:
t = threading.Thread(target=dealClient, args=(sock, addr))
t.start()

tcp_client

# -*- coding: utf-8 -*-
# 2017/11/25 16:32
import socket
#初始化Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#连接目标的ip和端口
s.connect(('127.0.0.1', 9999))
# 接收消息
print('-->>'+s.recv(1024).decode('utf-8'))
# 发送消息
s.send(b'Hello,I am a client')
print('-->>'+s.recv(1024).decode('utf-8'))
s.send(b'exit')
#关闭套接字
s.close()

5.2创建⼀个udp socket( udp套接字)

使用UDP协议时,不需要建立连接,只需要知道对方的ip和port,就可以直接发数据包,但是不关心是否能到达目的端。

UDP --- 用户数据报协议, 是一个无连接的简单的面向数据报的运输层协议。
UDP不提供可靠性, 它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,传输速度很快。
UDP是一种面向无连接的协议, 每个数据报都是一个独立的信息,包括完整的源地址或目的的地址,
它在网络上以任何可能的路径传往目的地, 因此能否到达⽬的地, 到达目的地的时间以及内容的正确性都是不能被保证的。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print 'Socket Created'

udp_server

# -*- coding: utf-8 -*-
# 2017/11/25 16:38
import socket
#创建Socket,绑定指定的ip和端口
#SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.1', 9999))
print('Bind UDP on 9999...')
while True:
# 直接发送数据和接收数据
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' %(addr,data))
s.sendto(b'Hello',addr)

udp_client

# -*- coding: utf-8 -*-
# 2017/11/25 16:39
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Hello', b'World']:
# 发送数据:
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))
s.close()

一下均基于tcp开发:

一对一

server

#__author: greg
#date: 2017/9/16 16:11
import socket
ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)#最大排队数,能开多少人
print ("服务端启动...")
# conn,addr= sk.accept()
# while True:
# client_data=conn.recv(1024)
# if str(client_data,"utf8")=='exit':
# break
# print (str(client_data,"utf8"))
# server_response=input(">>>")
# conn.sendall(bytes(server_response,"utf8"))
# conn.close() while True:
conn,address = sk.accept()
print(address)
while True:
try:
client_data=conn.recv(1024)
except:
print("意外中断")
break
print (str(client_data,"utf8"))
server_response=input(">>>")
conn.sendall(bytes(server_response,"utf8"))
conn.close()

client

#__author: greg
#date: 2017/9/16 16:11
import socket
ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.connect(ip_port)
print ("客户端启动:")
# while True:
# inp = input('>>>')
# sk.sendall(bytes(inp,"utf8"))
# if inp == 'exit':
# break
# server_response=sk.recv(1024)
# print (str(server_response,"utf8"))
# sk.close() while True:
inp=input('>>>')
if inp=="exit":
break
sk.send(bytes(inp,'utf8'))
data=sk.recv(1024)
print(str(data,"utf8"))
sk.close()

一对多,简单并发

server

#__author: greg
#date: 2017/9/16 16:27
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print ("服务端启动...")
while True:
conn = self.request
print (self.client_address)
while True:
client_data=conn.recv(1024)
print (str(client_data,"utf8"))
print ("waiting...")
conn.sendall(client_data)
conn.close() if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8091),MyServer)
server.serve_forever()

client

#__author: greg
#date: 2017/9/16 16:27 import socket ip_port = ('127.0.0.1',8091)
sk = socket.socket()
sk.connect(ip_port)
print ("客户端启动:")
while True:
inp = input('>>>')
sk.sendall(bytes(inp,"utf8"))
if inp == 'exit':
break
server_response=sk.recv(1024)
print (str(server_response,"utf8"))
sk.close()

聊天并发实例

server

#__author: greg
#date: 2017/9/16 20:55
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self): #handle父类有handle方法
print ("服务端启动...")
while True:
conn = self.request
print (self.client_address)
while True:
client_data=conn.recv(1024)
print (str(client_data,"utf8"))
print ("waiting...")
server_response=input(">>>")
conn.sendall(bytes(server_response,"utf8"))
# conn.sendall(client_data)
conn.close()
# print self.request,self.client_address,self.server if __name__ == '__main__':
server = socketserver.ThreadingTCPServer(('127.0.0.1',8098),MyServer)
server.serve_forever()

client

#__author: greg
#date: 2017/9/16 20:54
import socket
ip_port = ('127.0.0.1',8098)
sk = socket.socket()
sk.connect(ip_port)
print ("客户端启动:")
while True:
inp = input('>>>')
sk.sendall(bytes(inp,"utf8"))
server_response=sk.recv(1024)
print (str(server_response,"utf8"))
if inp == 'exit':
break
sk.close()

应用:

传送命令:

cmd_server:

 #__author: greg
#date: 2017/9/16 22:21
import socket
ip_port = ('127.0.0.1', 8879)
sk = socket.socket()
sk.connect(ip_port)
print("客户端启动:")
while True:
inp = input('cdm:>>>').strip()
if len(inp) == 0:
continue
if inp == "q":
break
sk.sendall(bytes(inp, "utf8"))
server_response = sk.recv(1024)
print(str(server_response, "gbk"))
print('receive data size', len(server_response))
if inp == 'exit':
break
sk.close()

cmd_client

 #__author: greg
#date: 2017/9/16 22:21
import socket
ip_port = ('127.0.0.1', 8879)
sk = socket.socket()
sk.connect(ip_port)
print("客户端启动:")
while True:
inp = input('cdm:>>>').strip()
if len(inp) == 0:
continue
if inp == "q":
break
sk.sendall(bytes(inp, "utf8"))
server_response = sk.recv(1024)
print(str(server_response, "gbk"))
print('receive data size', len(server_response))
if inp == 'exit':
break
sk.close()

sendall会把数据直接全部发送到客户端,客户端将所有的数据都放到缓冲区,每次recv多少字节取决于recv内的参数,理论不应该超过8k。

所以,并不能一次recv()无限大数据,所以这里我们应该通过循环去接收。

“粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了、或者数据等待超时了,数据才会被send到客户端,这样就把好几次的小数据拼成一个大数据,统一发送到客户端了,这么做的目地是为了提高io利用效率,一次性发送总比连发好几次效率高嘛。 但也带来一个问题,就是“粘包”,即2次或多次的数据粘在了一起统一发送。

我们在这里必须要想办法把粘包分开, 因为不分开,你就没办法取出来服务器端返回的命令执行结果的大小。

首先你是没办法让缓冲区强制刷新把数据发给客户端的。

你能做的,只有一个。就是,让缓冲区超时,超时了,系统就不会等缓冲区满了,会直接把数据发走,因为不能一个劲的等后面的数据呀,等太久,会造成数据延迟了,那可是极不好的。so如果让缓冲区超时呢?

  1. time.sleep(0.5),经多次测试,让服务器程序sleep 至少0.5就会造成缓冲区超时
  2. 不用sleep,服务器端每发送一个数据给客户端,就立刻等待客户端进行回应,即调用 conn.recv(1024), 由于recv在接收不到数据时是阻塞的,这样就会造成,服务器端接收不到客户端的响应,就不会执行后面的conn.sendall(命令结果)的指令,收到客户端响应后,再发送命令结果时,缓冲区就已经被清空了,因为上一次的数据已经被强制发到客户端了。
#__author: greg
#date: 2017/9/16 22:37
import socketserver
import subprocess
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
while True:
conn=self.request
conn.sendall(bytes("欢迎登录","utf8"))
while True:
client_bytes=conn.recv(1024)
if not client_bytes:break
client_str=str(client_bytes,"utf8")
print(client_str)
command=client_str
result_str=subprocess.getoutput(command)
result_bytes = bytes(result_str,encoding='utf8')
info_str="info|%d"%len(result_bytes)
conn.sendall(bytes(info_str,"utf8"))
# conn.recv(1024)
conn.sendall(result_bytes)
conn.close()
if __name__=="__main__":
server=socketserver.ThreadingTCPServer(("127.0.0.1",9998),Myserver)
server.serve_forever()

big_server

#__author: greg
#date: 2017/9/16 22:36mysql
import socket
ip_port=("127.0.0.1",9998)
sk=socket.socket()
sk.connect(ip_port)
print("客户端启动...")
print(str(sk.recv(1024),"utf8"))
while True:
inp=input(">>>").strip()
sk.sendall(bytes(inp,"utf8"))
basic_info_bytes=sk.recv(1024)
print(str(basic_info_bytes,"utf8"))
# sk.send(bytes('ok','utf8'))
result_length=int(str(basic_info_bytes,"utf8").split("|")[1])
print(result_length)
has_received=0
content_bytes=bytes()
while has_received<result_length:
fetch_bytes=sk.recv(1024)
has_received+=len(fetch_bytes)
content_bytes+=fetch_bytes
cmd_result=str(content_bytes,"utf8")
print(cmd_result)
sk.close()

big_client

#__author: greg
#date: 2017/9/17 0:14
import socket,os ip_port=("127.0.0.1",8898)
sk=socket.socket()
sk.bind(ip_port)
sk.listen(5)
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
while True:
print("waiting connect")
conn,addr=sk.accept()
flag = True
while flag:
client_bytes=conn.recv(1024)
func,file_byte_size,filename=str(client_bytes,"utf8").split("|",2)
path=os.path.join(BASE_DIR,'upload',filename)
has_received=0
file_byte_size=int(file_byte_size)
f=open(path,"wb")
while has_received<file_byte_size:
data=conn.recv(1024)
f.write(data)
has_received+=len(data)
print("ending")
f.close()

up_server

#__author: greg
#date: 2017/9/17 0:14
import socket
import re,os,sys
ip_port=("127.0.0.1",8898)
sk=socket.socket()
sk.connect(ip_port)
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
print("客户端启动....")
while True:
inp=input(">>>").strip() #post|1.jpg
if inp.startswith("post"):
method,local_path=inp.split("|",1)
local_path=os.path.join(BASE_DIR,local_path)
file_byte_size=os.stat(local_path).st_size
file_name=os.path.basename(local_path)
post_info="post|%s|%s"%(file_byte_size,file_name)
sk.sendall(bytes(post_info,"utf8"))
has_sent=0
file_obj=open(local_path,"rb")
while has_sent<file_byte_size:
data=file_obj.read(1024)
sk.sendall(data)
has_sent+=len(data)
file_obj.close()
print("上传成功")

up_client

下一篇:socketserver

Python网络编程篇之socket的更多相关文章

  1. python 网络编程 TCP/IP socket UDP

    TCP/IP简介 虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多. 计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Micro ...

  2. Python网络编程篇之socketserver

    1.socketserver模块和类 socketserver是标准库中的一个高级模块,目标是简化很多样板代码(创建网络客户端和服务器所必须的代码) 这个模块封装了socket编程所需要的各种各样的类 ...

  3. Python网络编程篇之select和epoll

    1. select 原理 在多路复⽤的模型中, ⽐较常⽤的有select模型和epoll模型. 这两个都是系统接⼝, 由操作系统提供. 当然, Python的select模块进⾏了更⾼级的封装. ⽹络 ...

  4. python 网络编程篇

    基础模拟通话网络程序: #客户端 import socket client = socket.socket() client.connect(('localhost',6969)) client.se ...

  5. python网络编程基础之socket粘包现象

    粘包现象两种 登陆 #服务端import json import socket server=socket.socket()#创建socket对象 ip_port=('127.0.0.1',8001) ...

  6. python 网络编程:socket

    在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一 ...

  7. Python网络编程socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

  8. 从零开始学Python第八周:网络编程基础(socket)

    Socket网络编程 一,Socket编程 (1)Socket方法介绍 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Soc ...

  9. Day07 - Python 网络编程 Socket

    1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...

随机推荐

  1. python 中Dict 转 Json

    近期在公司须要写个小工具.运用到的python,然后须要将Dict转成Json. 之前遇到转换Json失败,然后以为复杂的Entity结构.不能用Json的库Json.dump().进行转换. 自己些 ...

  2. 设置Eclipse的workspace路径

    首次启动Eclipse/MyEclipse时, 会弹出"Workspace Launcher"对话框, 提示设置Workspace路径. 设定好路径后, 若勾选了"Use ...

  3. 我的csdn博客搬家了

    把csdn上的文章都给搬到我的新博客去了, 将会在新的博客上继续写相关的技术文章 欢迎訪问: http://www.kai-zhou.com

  4. C#:StreamReader读取.CSV文件(转换成DataTable)

    using System.Data; using System.IO; /// <summary> /// Stream读取.csv文件 /// </summary> /// ...

  5. 【python】函数闭包

    列表时可以改

  6. 商城项目整理(三)JDBC增删改查

    商品表的增加,修改,删除,订单表的增加,确认,用户表的查看,日志表的增加,查看 商品表建表语句: create table TEST.GOODS_TABLE ( gid NUMBER not null ...

  7. Struts2学习---简单的数据校验、访问Web元素

    1.简单的数据校验 在action里面我们已经给出了一个数据校验: public String execute() { if(user.getUsername().equals("usern ...

  8. 服务器端语言go之开篇分享

    由于之前看过其他脚本语言,此时看服务器端语言go语法时也短短用了半天的时间,如图1所示,是个人学习go语法的目录截图,学习网址:菜鸟网站,为了个人方便学习和记忆,因此写下本篇文章,在本篇文章里我主要是 ...

  9. json小结和fastjson包的实际json操作

    工作中,需要处理与另一方系统数据交换的问题,采用的是调用远程接口的方法,数据格式选择的是json,今天就来聊一聊json,主要分析json数据和java Bean之间的转换问题. 一.json是什么 ...

  10. php生成雪花图像(不美观请见谅)

    <?php /*  //新建图像 //雪花  @header("Content-Type:image/png"); $w = 500; $h = 500; //create ...