socket网络编程

简单架构:

套接字的工作流程:

 

套接字方法使用:

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

import socket
import sys
from time import ctime


# 1.socket(socket_family, socket_type, protocol=0)
# 其中,socket_family 是 AF_UNIX 或 AF_INET,ocket_type 是 SOCK_STREAM或 SOCK_DGRAM, protocol 通常省略,默认为 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)
ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 获取本地机器名
host = socket.gethostname()

# 设置端口
port = 9999

# 2.s.bind绑定本地地址到socket对象
ServerSocket.bind((host, port))
# 3.s.listen监听地址端口,连接几个客户端
ServerSocket.listen(2)
while True:
    # 4.s.accept阻塞接受链接请求,被动接受 TCP 客户端连接,一直等待直到连接到达(阻塞)
    # accept()方法会返回一个含有两个元素的元组(fd,addr)。
    # 第一个元素是新的socket对象,服务器通过它与客户端通信。
    # 第二个元素也是元组,是客户端的地址及端口信息。
    clientsocket, addr = ServerSocket.accept()
    print("连接地址:%s" % str(addr))
    msg = "welcomt to my demo"
    #send()和recv()的数据格式都是bytes。
    # (str和bytes的相互转化,用encode()和decode(),或者用bytes()和str())
    clientsocket.send(msg.encode("utf-8"))
    data = clientsocket.recv(1024)
    data1 = ('[%s] %s' % (ctime(),data.decode())).encode("utf-8")
    clientsocket.send(data1)
    clientsocket.close()
ServerSocket.close()

socket对象:

 

参数 描述
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 可靠的连续数据包服务

 

socket功能:

TCP

服务端

import socket

sk = socket.socket()

sk.bind(("127.0.0.1",8080))
sk.listen(5)

while True:
    conn,address = sk.accept()
    conn.sendall(bytes("欢迎光临我爱我家",encoding="utf-8"))

    size = conn.recv(1024)
    size_str = str(size,encoding="utf-8")
    file_size = int(size_str)

    conn.sendall(bytes("开始传送", encoding="utf-8"))

    has_size = 0
    f = open("db_new.jpg","wb")
    while True:
        if file_size == has_size:
            break
        date = conn.recv(1024)
        f.write(date)
        has_size += len(date)

    f.close()

客户端

import socket
import os

obj = socket.socket()

obj.connect(("127.0.0.1",8080))

ret_bytes = obj.recv(1024)
ret_str = str(ret_bytes,encoding="utf-8")
print(ret_str)

size = os.stat("yan.jpg").st_size
obj.sendall(bytes(str(size),encoding="utf-8"))

obj.recv(1024)

with open("yan.jpg","rb") as f:
    for line in f:
        obj.sendall(line)

 UDP

import socket
ip_port = ('127.0.0.1',9999)
sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
sk.bind(ip_port)

while True:
    data = sk.recv(1024)
    print data




import socket
ip_port = ('127.0.0.1',9999)

sk = socket.socket(socket.AF_INET,socket.SOCK_DGRAM,0)
while True:
    inp = input('数据:').strip()
    if inp == 'exit':
        break
    sk.sendto(bytes(inp,encoding = "utf-8"),ip_port)

sk.close()

 socket粘包问题:

sk.recv(1024)中,bufsize值为1024,最多只能接受1024个字节,那么如果client端发送的数据包特别大时,超过了指定的bufsize的值,超过的不分会留在内核缓冲区中,下次调用recv的时候会继续读剩余的字节。这就是所谓的粘包问题,那么怎么解决呢?

类似于http协议,我们可以:

  1. 在发送之前先告诉接受数据端我要发送数据的字节大小
  2. 接收数据端收到数据后回复给数据发送端一个确认消息
  3. 数据发送端收到确认信息后,发送数据
  4. 数据接收端循环接受数据,直到数据接受完成,收到完整数据包
posted @ 2019-03-07 11:09  Yanyong520  阅读(156)  评论(0)    收藏  举报