首页 socket简介
文章
取消

socket简介

1:socket大致介绍

socket编程是一门技术,它主要是在网络通信中经常用到

既然是一门技术,由于现在是面向对象的编程,一些计算机行业的大神通过抽象的理念,在现实中通过反复的理论或者实际的推导,提出了抽象的一些通信协议,基于tcp/ip协议,提出大致的构想,一些泛型的程序大牛在这个协议的基础上,将这些抽象化的理念接口化,针对协议提出的每个理念,专门的编写制定的接口,与其协议一一对应,形成了现在的socket标准规范,然后将其接口封装成可以调用的接口,供开发者使用

目前,开发者开发出了很多封装的类来完善socket编程,都是更加方便的实现刚开始socket通信的各个环节,所以我们首先必须了解socket的通信原理,只有从本质上理解socket的通信,才可能快速方便的理解socket的各个环节,才能从底层上真正的把握

2:TCP/IP协议

要理解socket必须的得理解tcp/ip,它们之间好比送信的线路和驿站的作用,比如要建议送信驿站,必须得了解送信的各个细节。

TCP/IP协议不同于iso的7个分层,它是根据这7个分层,将其重新划分,好比打扫卫生,本来有扫帚,垃圾斗,抹布,涂料,盆栽等就好比OSI的标准几个分层,tcp/ip根据用途和功能,将扫帚,垃圾斗放到粗略整理层,抹布涂料放到中度整理层,盆栽放到最终效果层。这里TCP/IP也对OSI的网络模型层进行了划分:大致如下:

OSI模型:

20150615140039701

TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中

应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等

传输层:TCP,UDP

网络层:IP,ICMP,OSPF,EIGRP,IGMP

数据链路层:SLIP,CSLIP,PPP,MTU

每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的

20150615140707753

通过上面的图形,由于底一层的需要向高一层的提供服务,我们大致的理解应用程序需要传输层的tcp和网络层的ip协议提供服务,但是我们这章要分析的socket它是在tcpip协议的那一部分呢,就好比,我们的通讯线路已经有明确的规定,我们的驿站要设计在哪个地方一样

3:回过头再来理解socket

到目前为止,大致的了解了应用程序和tcpip协议的大致关系,我们只是知道socket编程是在tcp/IP上的网络编程,但是socket在上述的模型的什么位置呢。这个位置被一个天才的理论家或者是抽象的计算机大神提出并且安排出来

20150615141705040

我们可以发现socket就在应用程序的传输层和应用层之间,设计了一个socket抽象层,传输层的底一层的服务提供给socket抽象层,socket抽象层再提供给应用层,问题又来了,应用层和socket抽象层之间和传输层,网络层之间如何通讯的呢,了解这个之前,我们还是回到原点

要想理解socket编程怎么通过socket关键词实现服务器和客户端通讯,必须得实现的了解tcp/ip是怎么通讯的,在这个的基础上在去理解socket的握手通讯

在tcp/ip协议中,tcp通过三次握手建立起一个tcp的链接,大致如下

第一次握手:客户端尝试连接服务器,向服务器发送syn包,syn=j,客户端进入SYN_SEND状态等待服务器确认

第二次握手:服务器接收客户端syn包并确认(ack=j+1),同时向客户端发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

三次握手如下图:

20150615142700226

根据tcp的三次握手,socket也定义了三次握手,也许是参考tcp的三次握手,一些计算机大神们画出了socket的三次握手的模型图

模型图如下:

20150615144404136

在上面图的基础上,如果我们得到上面的图形,需要我们自己开发一些接口,来满足上面的通讯的三次握手,问题就出来了,我们会需要开发哪些函数

4:socket的一些接口函数原理

通过上面的图,我们清楚,我们好比一些泛型的程序员,一些理论提供者提供给了我们上面的图形的理论,我们需要做的就是讲上面的图形的抽象化的东西具体化

第一次握手:客户端需要发送一个syn j 包,试着去链接服务器端,于是客户端我们需要提供一个链接函数

第二次握手:服务器端需要接收客户端发送过来的syn J+1 包,然后在发送ack包,所以我们需要有服务器端接受处理函数

第三次握手:客户端的处理函数和服务器端的处理函数

三次握手只是一个数据传输的过程,但是,我们传输前需要一些准备工作,比如将创建一个套接字,收集一些计算机的资源,将一些资源绑定套接字里面,以及接受和发送数据的函数等等,这些功能接口在一起构成了socket的编程

下面大致的按照客户端和服务端将所需的函数详细的列举出来

20150615150446559

20150615150618996

上面的两个图都概述了socket的通讯原理。

5:socket的一个例子,总结上述的问题

详细就不在说明,通过一段代码详细的解释(使用python实现)。

TCP客户端

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
33
34
35
36
37
38
import socket
import time


def create_client(server_host_and_port: tuple, data_size: int, data_type: str):
    """
    创建客户端
    :param server_host_and_port: 服务器IP及端口
    :param data_size: 传输字节大小
    :param data_type: 编解码类型
    :return:
    """
    try:
        # 创建客户端socket对象,模式为TCP
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 连接服务器
        client.connect(server_host_and_port)
        # 退出标致
        exit_flag = 0
        while not exit_flag:
            # 输入要发送的内容
            send_data = input('请输入要发送的字符:')
            # 编码并发送内容
            client.send(send_data.encode(data_type))
            # 接收内容并解码
            receive_data = client.recv(data_size).decode(data_type)
            if receive_data == 'exit':
                exit_flag = 1
            else:
                # 打印服务器返回的内容
                print('[{}]接收到来自服务器的消息:{}'.format(time.asctime(), receive_data))
    except Exception as e:
        print(e)
        raise e


if __name__ == '__main__':
    create_client(('127.0.0.1', 8888), 1024, 'utf-8')

TCP服务器

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import socket
import time


def create_server(hostAndPort: tuple, data_size: int, data_type: str, connect_max: int):
    """
    创建服务器

    :param hostAndPort: 本机IP和开放端口
    :param data_size: 最大传输字节
    :param data_type: 编解码格式
    :param connect_max: 最大连接数
    :return:
    """
    try:
        # 创建服务器socket 对象,模式为TCP
        server = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM  # TCP
        )
        # 绑定主机IP与端口
        server.bind(hostAndPort)
        # 设置最大连接数
        server.listen(connect_max)

        # 退出标致
        exit_flag = 0
        while True:
            print('等待连接。。。', end='')
            # 接收套接字对象与地址
            client_socket, add = server.accept()
            print('\r[{}]{}连接成功!'.format(time.asctime(), add))
            while not exit_flag:
                # 接收内容,并解码,若客户端异常退出,放弃此客户端
                try:
                    receiver_data = client_socket.recv(data_size).decode(data_type)
                except ConnectionResetError:
                    print('[{}]{}异常退出!'.format(time.asctime(), add))
                    break
                # 退出标致
                if receiver_data == 'exit':
                    exit_flag = 1
                    client_socket.send('exit'.encode(data_type))
                    print('[{}]{}已退出'.format(time.asctime(), add))
                else:
                    # 打印接收的内容
                    print('[{}]接收到来自{}的内容:{}'.format(time.asctime(), add, receiver_data))
                    # 将字母全部转为大写
                    send_data = receiver_data.upper()
                    # 利用客户端套接字对象发送给客户端
                    client_socket.send(send_data.encode(data_type))
                    # 打印发送的内容
                    print('已发送给{}内容:{}'.format(add, send_data))
    except Exception as e:
        raise e


if __name__ == '__main__':
    create_server(('127.0.0.1', 8888), 1024, 'utf-8', 5)

UDP客户端

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
import socket

# 服务器IP和开放端口
server_host_port = ('127.0.0.1', 8888)
# 本地IP和开放端口
client_host_port = ('127.0.0.1', 8889)
# 最大传输字节
data_size = 1024
# 编解码格式
data_type = 'utf-8'


def create_client():
    client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client.bind(client_host_port)
    try:
        while True:
            send_data = input('请输入要发送的字符:')
            # 发送内容
            client.sendto(send_data.encode(data_type), server_host_port)
            # 接收内容
            receive_data, add = client.recvfrom(data_size)
            print('接收到来自{}的消息:{}'.format(add, receive_data.decode(data_type)))
    except Exception as e:
        print(e)


create_client()


UDP服务器

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
33
34
35
36
37
import socket

# 本机IP和开放端口
host_port = ('127.0.0.1', 8888)
# 最大传输字节
data_size = 1024
# 编解码格式
data_type = 'utf-8'
# 最大连接数
connect_max = 5


def create_server():
    try:
        # 创建socket对象
        server = socket.socket(
            socket.AF_INET, socket.SOCK_DGRAM  # UDP
        )
        # 绑定主机IP与端口
        server.bind(host_port)
        while True:
            # data为接收数据,add为发送者地址
            receiver_data, add = server.recvfrom(data_size)
            receiver_data = receiver_data.decode(data_type)
            print('接收到来自{}的内容:{}'.format(add, receiver_data))
            # 将字母全部转为大写
            send_data = receiver_data.upper()
            # 发送给客户端
            server.sendto(send_data.encode(data_type), add)
            print('已发送给{}内容:{}'.format(add, send_data))
    except Exception as e:
        print(e)


create_server()


本文由作者按照 CC BY 4.0 进行授权

数据结构模板

TCP连接的建立和释放过程详解(三次握手、四次挥手)