Socket 网络编程

写一个终端发送消息的服务器客户端。

#  服务端代码
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 8001))
server.listen(5)
clientSocket, clientAddress = server.accept()
print("start-------")
while True:
    data = clientSocket.recv(1024)
    print("server receive:" + data.decode("utf-8"))
    clientSocket.send("发送成功!".encode("utf-8"))
#  客户端代码
import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 8001))
count = 0
while True:
    count += 1
    data = input("please input data to server:")
    client.send(data.encode("utf-8"))
    info = client.recv(1024)
    print("client receive:" + info.decode("utf-8"))

分别运行server和client对应的python文件时,报错:

socket.gaierror: [Errno 8] nodename nor servname provided, or not known

原因是客户端服务端绑定对应的ip地址时,地址错误,最后将bind函数中的ip地址改为本地的127.0.0.1 或者localhost

下面是解析部分代码的含义:

1、socket.socket(socket.AF_INET, socket.SOCK_STREAM)

对套接字socket的理解:

通信的套接字有两种类型,分别是基于文件和基于网络的套接字

源码中socket函数的解释:

def __init__(self, family=-1, type=-1, proto=-1, fileno=None):
        # For user code address family and type values are IntEnum members, but
        # for the underlying _socket.socket they're just integers. The
        # constructor of _socket.socket converts the given argument to an
        # integer automatically.
        if fileno is None:
            if family == -1:
                family = AF_INET
            if type == -1:
                type = SOCK_STREAM
            if proto == -1:
                proto = 0
        _socket.socket.__init__(self, family, type, proto, fileno)
        self._io_refs = 0
        self._closed = False

构造方法有三个参数,默认是-1,在函数内部将其转成对应的含义。family = AF_INET,type = SOCK_STREAM

AF_INET是指套接字面向网络,AF_UNIX是套接字面向文件。还有其他类型的套接字这里不进行说明。

SOCK_STREAM是指这个套接字是面向连接的。有面向连接和无连接的套接字,对应协议为TCP和UDP。

TCP套接字的名字SOCK_STREAM:可靠,通信前必须创建连接

UDP套接字的名字SOCK_DGRAM:不可靠

我把这里的TCP套接字换成UDP套接字之后,报错:OSError: [Errno 102] Operation not supported on socket

2、bind函数

绑定ip地址和端口

3、listen监听

最多监听5个连接

4、recv函数

socket中有发送缓冲区和接收缓冲区

客户端接收到服务器发送的消息后,将其接收并缓存到接收缓冲区中。调用recv函数时,先检查是否有在接收的数据,有则等待,没有则拷贝(判断缓冲区的长度和要接收的数据长度)。recv函数只是把数据缓存在缓冲区中,实际是协议接收数据。

5.send函数

send只负责将数据提交给协议层。send将数据存在缓冲区之前,会比较发送缓冲区的长度和要发送数据的长度,如果要发送的数据长度大于缓冲区的长度则报错socket_error

如果小于缓冲区的长度,则检查协议是否有在发送数据,有则等待。没有则检查发送缓冲区中是否有数据,是否还有剩余位置供等待发送的数据存储。

send函数将数据成功拷贝到发送缓冲区之后就返回了,至于是否发送成功要看传送过程中有无其他网络错误,失败则返回socke_error

6、send和recv的次数不一定相同,可能一对一,一对多,多对一等。