Socket网络编程-TCP编程

                                   作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.socket介绍

1>.TCP/IP协议

2>.跨网络的主机间通讯

  在建立通信连接的每一端,进程间的传输要有两个标志:

  IP地址和端口号,合称为套接字地址 socket address

  客户机套接字地址定义了一个唯一的客户进程

  服务器套接字地址定义了一个唯一的服务器进程

3>.什么是socket套接字

  套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

  Socket:套接字,进程间通信IPC的一种实现,允许位于不同主机(或同一主机)上不同进程之间进行通信和数据交换,SocketAPI出现于1983年,4.2 BSD实现。

  Socket API:封装了内核中所提供的socket通信相关的系统调用。

  Python中提供了socket标准库,非常底层的接口库。

4>.协议族(Socket Domain)

AF表示Address Family,用于socket()第一个参数
  AF_INET:
    对应IPV4
  AF_INET6
    对应IPV6
  AF_UNIX
    同一主机不同进程之间通信时使用,对应Unix Domain Socket,windows没有。

5>.socket Type(根据使用的传输层协议)

SOCK_STREAM
  可靠的传递,面向连接的流套接字。默认值,TCP协议。
SOCK_DGRAM
  不可靠的传递,无连接的数据报文套接字。UDP协议。
SOCK_RAW:
  裸套接字,无须tcp或udp,APP直接通过IP包通信

6>.C/S编程

  Socket编程,需要两端,一般来说需要一个服务端、一个客户端,服务端称为Server,客户端称为Client。 这种编程模式也称为C/S编程。

  套接字相关的系统调用:
    socket(): 创建一个套接字
    bind(): 绑定IP和端口
    listen(): 监听
    accept(): 接收请求
    connect(): 请求连接建立
    write(): 发送
    read(): 接收
    close(): 关闭连接

7>.python中的socket常用方法

  socket.recv(bufsize[, flags])
    获取数据。默认是阻塞的方式

  socket.recvfrom(bufsize[, flags])
    获取数据,返回一个二元组(bytes, address)

  socket.recv_into(buffer[, nbytes[, flags]])
    获取到nbytes的数据后,存储到buffer中。如果 nbytes没有指定或0,将buffer大小的数据存入buffer中。返回接收的字节数。

  socket.recvfrom_into(buffer[, nbytes[, flags]])
    获取数据,返回一个二元组(bytes, address)到buffer中

  socket.send(bytes[, flags])
    TCP发送数据

  socket.sendall(bytes[, flags])
    TCP发送全部数据,成功返回None

  socket.sendto(string[,flag],address)
    UDP发送数据

  socket.sendfile(file, offset=0, count=None)
    python 3.5版本开始,发送一个文件直到EOF,使用高性能的os.sendfile机制,返回发送的字节数。如果win下不支持sendfile, 或者不是普通文件,使用send()发送文件。offset告诉 起始位置。   socket.getpeername()
    返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)

  socket.getsockname()
    返回套接字自己的地址。通常是一个元组(ipaddr,port)

  socket.setblocking(flag)
    如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值),非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常

  socket.settimeout(value)
    设置套接字操作的超时期,timeout是一个浮点数,单位是 秒。值为None表示没有超时期。一般,超时期应该在刚创 建套接字时设置,因为它们可能用于连接的操作(如 connect())

  socket.setsockopt(level,optname,value)
    设置套接字选项的值。比如缓冲区大小。太多了,去看文档。不同系统,不同版本都不尽相同   socket.makefile(mode='r', buffering=None, *, encoding=None, errors=None, newline=None)
    创建一个与该套接字相关连的文件对象,将recv方法看做读方法,将send方法看做写方法。

二.TCP服务端编程

1>.服务器端编程步骤

  创建Socket对象 

  绑定IP地址Address和端口Port。bind()方法
    IPv4地址为一个二元组('IP地址字符串', Port) 开始监听,将在指定的IP的端口上监听。listen()方法   获取用于传送数据的Socket对象
    socket.accept() -> (socket object, address info)
      accept方法阻塞等待客户端建立连接,返回一个新的Socket对象和客户端地址的二元组 地址是远程客户端的地址,IPv4中它是一个二元组(clientaddr, port)
      接收数据
        recv(bufsize[, flags]) 使用缓冲区接收数据
      发送数据
        send(bytes)发送数据   Server端开发
    socket对象 --> bind((IP, PORT)) --> listen --> accept --> close
     |--> recv or send --> close

2>.实战案例写一个群聊程序的服务端ChatServer

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import logging
import socket
import threading
import datetime logging.basicConfig(level=logging.INFO, format="%(asctime)s %(thread)d %(message)s") """
注意,这个代码为实验用,代码中瑕疵还有很多。Socket太底层了,实际开发中很少使用这么底层的接 口。
"""
class ChatServer:
def __init__(self,ip="127.0.0.1",port=6666):
self.conn = socket.socket() #创建socket对象
self.addr = (ip,port)
self.clients = {} #存放客户端连接容器
self.event = threading.Event() #判断服务是否启动
self.lock = threading.Lock() #为了线程安全而引入的 def start(self): #启动服务,会监听IP地址和端口哟~
self.conn.bind(self.addr) #将IP地址和端口绑定到套接字上
self.conn.listen() #监听绑定的套接字 #accept会阻塞主线程,所以开一个新线程
threading.Thread(target=self.accept).start() def accept(self): #处理客户端的链接
while not self.event.is_set():
conn,client = self.conn.accept() #该方法默认会进入阻塞状态
f = conn.makefile("rw") #将socket封装成一个文件对象来操作,支持读写
with self.lock:
self.clients[client] = f,conn #如果有新链接就添加到客户端字典 #准备接收数据,recv是阻塞的,开启新的线程
threading.Thread(target=self.recv,args=(f,client)).start() def recv(self,f,client): #处理客户端数据
print("in recv")
while not self.event.is_set():
try:
data = f.readline()
print(data)
except Exception as e:
logging.error(e)
data = "quit" msg = data.strip()
print(msg, "++++++++++++++++++") #和客户端约定退出命令
if msg == "quit" or msg == "": #主动端口得到空串
with self.lock:
_,conn = self.clients.pop(client)
f.close()
conn.close()
logging.info("{} quit ...".format(client))
break msg = "{:%Y/%m/%d %H:%M:%S} ip:port => {}:{}\n data => {}\n".format(datetime.datetime.now(),*client, data)
logging.info(msg) with self.lock:
for f1,_ in self.clients.values():
f1.write(msg)
f1.flush() def stop(self): #停止服务
self.event.set()
with self.lock:
for f,s in self.clients.values():
f.close()
s.close()
self.conn.close() def main():
server = ChatServer()
server.start() while True:
cmd = input(">>> ").strip()
if cmd == "quit":
server.stop()
threading.Event.wait(3) #关闭服务需要等待时间
break
logging.info(threading.enumerate()) #用来观察断开后线程的变化
logging.info(server.clients) if __name__ == '__main__':
main()

三.TCP客户端编程

1>.客户端编程步骤

  创建Socket对象 

  连接到远端服务端的ip和port,connect()方法 

  传输数据  
    使用send、recv方法发送、接收数据   关闭连接,释放资源

2>.实战案例写一个群聊程序的客户端ChatClient

 #!/usr/bin/env python
#_*_conding:utf-8_*_
#@author :yinzhengjie
#blog:http://www.cnblogs.com/yinzhengjie import socket
import threading
import datetime
import logging FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO) """
同样,这样的客户端还是有些问题的,仅用于测试。
"""
class ChatClient:
def __init__(self, ip='127.0.0.1', port=6666):
self.sock = socket.socket()
self.addr = (ip, port)
self.event = threading.Event() def start(self): # 启动对远端服务器的连接
self.sock.connect(self.addr)
self.send("I'm ready.")
# 准备接收数据,recv是阻塞的,开启新的线程
threading.Thread(target=self.recv, name="recv").start() def recv(self): # 接收服务端的数据
while not self.event.is_set():
try:
data = self.sock.recv(1024) # 阻塞
except Exception as e:
logging.error(e)
break msg = "{:%Y/%m/%d %H:%M:%S} ip:port => {}:{}\n data => {}\n".format(datetime.datetime.now(),*self.addr, data.strip())
logging.info(msg) def send(self, msg:str):
data = "{}\n".format(msg.strip()).encode() # 服务端需要一个换行符
self.sock.send(data) def stop(self):
self.sock.close()
self.event.wait(3)
self.event.set()
logging.info('Client stops...') def main():
client = ChatClient()
client.start()
while True:
cmd = input('>>>')
if cmd.strip() == 'quit':
client.stop()
break
client.send(cmd) # 发送消息 if __name__ == '__main__':
main()

Socket网络编程-TCP编程的更多相关文章

  1. python网络编程--socket,网络协议,TCP

    一. 客户端/服务端架构(用到网络通信的地方) 我们使用qq.微信和别人聊天,通过浏览器来浏览页面.看京东的网站,通过优酷.快播(此处只是怀念一下)看片片啥的等等,通过无线打印机来打印一个word文档 ...

  2. 36 - 网络编程-TCP编程

    目录 1 概述 2 TCP/IP协议基础 3 TCP编程 3.1 通信流程 3.2 构建服务端 3.3 构建客户端 3.4 常用方法 3.4.1 makefile方法 3.5 socket交互 3.4 ...

  3. 五十四、linux 编程——TCP 编程模型

    54.1 编程模型介绍 54.1.1 TCP 客户端服务器编程模型 客户端调用序列 调用 socket 函数创建套接字 调用 connect 连接服务器端 调用 I/O 函数(read/write) ...

  4. 五十四 网络编程 TCP编程

    Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可. 客户端 大多数连接都是可靠 ...

  5. [Python 网络编程] TCP编程/群聊服务端 (二)

    群聊服务端 需求分析: 1. 群聊服务端需支持启动和停止(清理资源); 2. 可以接收客户端的连接; 接收客户端发来的数据 3. 可以将每条信息分发到所有客户端 1) 先搭架子: #TCP Serve ...

  6. 五十三、linux 编程——TCP 编程基本介绍

    53.1 socket 套接字 53.1.1 介绍 Socket(套接字)是一种通讯机制,它包含一整套的调用接口和数据结构的定义,它给应用进程提供了使用如 TCP/UDP 灯网络协议进行网络通讯的手段 ...

  7. 63 网络编程(四)——TCP编程

    TCP编程 TCP编程是面向连接的数据传输,所以需要时用IO流来建立连接. 用户输出流到服务器,服务器输入流接收数据. 服务器输出流到用户,用户输入流接收. 基本流程 服务器端 创建服务器端:Serv ...

  8. 基于网络编程 TCP协议 及 socket 基本语法

    socket是什么 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面, ...

  9. Socket网络编程(TCP/IP/端口/类)和实例

    Socket网络编程(TCP/IP/端口/类)和实例 原文:C# Socket网络编程精华篇 转自:微冷的雨 我们在讲解Socket编程前,先看几个和Socket编程紧密相关的概念: TCP/IP层次 ...

随机推荐

  1. HTML基础知识---文本编辑练习

    飘柔兰花去油洗发水液露去屑止痒控油                                         阿道夫净屑舒爽清洁头皮去屑洗发水520ml                      ...

  2. [no_perms] Private mode enable, only admin can publish this module

    在使用npm publish是出现了错误: npm ERR! code E403 npm ERR! 403 Forbidden - PUT https://registry.npm.taobao.or ...

  3. vue中使用element-ui自定义主题后,vue-cli跑不起来了

    环境:vue-cli 2.x版本 自己在官网配置了主题并放到了项目中https://element.eleme.cn/#/zh-CN/theme 然后,我的脚手架在我的电脑中休息了几天,就跑不通了呢! ...

  4. Java编程思想之十四 类型信息

    第十四章 类型信息 运行时类型信息使得你可以在程序运行时发现和使用类型信息 14.1 为什么需要RTTI 面向对象编程中基本的目的是:让代码只操作对基类的引用. 多态: import java.uti ...

  5. [原创]UnLua Emmylua UE4开发环境搭建

    前言 公司开发的第二个虚幻4项目已经上线了,慢慢趋于稳定.回想起开荒的日子,历历在目.从引擎脚本的选择,各工具(导表,协议生成...)的重构.开发, 引擎扩展(多骨骼支持,Notify扩展,技能编辑器 ...

  6. Pod Preset玩转K8S容器时区自动配置

    缘由默认的情况,在K8S里启动一个容器,该容器的设置的时区是UTC0,但是对于很多客户而言,其主机环境并不在UTC0.例如中国客户在UTC8.如果不把容器的时区和主机主机设置为一致,则在查找日志等时候 ...

  7. 【java】使用jsp命令查看系统中java运行的程序及进程号

    对于java独立运行的程序,他们在进程中的名字都是 Java(TM) Platform SE binary,如图 我们想知道这个进程运行的是哪个程序,怎么办呢? 答案是:可以在命令行下,运行:jps命 ...

  8. Laravel文档工具

    laravel-doc ⛵laravel-doc 是一个用来生成文档,通过markdown来撰写文档,并提供web访问文档的项目 安装要求 PHP >= 7.0.0 Laravel >= ...

  9. 用ADB 抓取和存储APP日志方法

    前置条件:电脑已经安装好adb (一)  进入adb目录下连接手机,检测出手机 CD 到SDK的platform-tools (二)adb logcat-c  清除日志  (三)adb  logcat ...

  10. Java8 新特性 Lamdba表达式

    Lamdba 表达式为什么出现   Java8是自java延生以来最大的改变,他允许java中的方法和函数成为一等公民(可以在方法间传递),所以就应运而出现了Lamdba表达式,他可以将表达式传递给另 ...