Linux socket编程

在学习socket编程之前,需要首先弄明白socket是什么,socket的作用,socket给网络带来了哪些改变。首先,我们知道在网络中通信的过程是两个终端之间的通信,而终端有一个唯一的标识,即IP地址,但是在一个终端中我们可能一边在浏览着网页,一遍在给别人发邮件等,而这些事情仅仅靠IP地址来区别是不行的,这时我们引入了传输层的端口来进行区别,即进程号。所以说网络的通信实则是不同进程之间的通信。

这时问题来了,网络中进程之间是如何通信的?

进程通信的概念最初来源于单机系统。即每个进程都在自己的地址范围内运行,为保证两个相互通信的进程之间既互不干扰又协调一致工作,操作系统为进程通信提供了多种方式:

a)   消息传递(管道(pipe)、FIFO、消息队列)

b)   同步(互斥量、信号量、读写锁)

c)   共享内存

目前在网络中通常使用TCP/IP协议的应用程序采用应用编程接口:UNIX BSD的套接字(socket),来实现网络中进程的通信。因而这也就体现出socket编程的重要性。那么什么是socket?由于socket起源于UNIX,而UNIX/LINUX设计的哲学就是“everything is file”,都可以用“open-write/read-close”的模式来操作。所以说socket其实也可以看作一个特殊的文件,通过一系列的函数来实现。

下面就开始简要地介绍socket的接口函数过程:


服务器端初始化socket,然后与端口绑定bind,对端口进行监听listen,调用accept阻塞,等待客户端连接。在这时客户端初始化一个socket,然后请求连接服务器connect,如果连接成功,这时客户端与服务器端的连接就建立起来了。客户端发送请求数据,服务器端接收请求并处理请求,接着把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

具体的socket接口函数可以去查询,这里就不给出具体的详解。

这里就解释下TCP/IP的socket三种类型套接字:

1.   流式套接字(SOCK_STEAM):提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。即TCP传输协议。

2.   数据包式套接字(SOCK_DGRAM):提供了一个无连接服务(UDP)。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。

3.   原始式套接字(SOCK_RAW):该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。

接下来简要介绍socket中TCP的三次握手和四次释放过程:


从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。


图示过程如下:

某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;

另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;

一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;

接收到这个FIN的源发送端TCP对它进行确认。

这样每个方向上都有一个FIN和ACK。

接下来附上TCP client和TCP server端的Python代码:

<span style="font-size:14px;">#!/usr/bin/env python
# -*- coding: utf-8 -*-                 #将编码设置为utf-8

'a socket example which send echo message to server.'

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #其中AF_INET表示IPv4地址,AF_INET6表示IPv6地址
# 建立连接:
s.connect(('127.0.0.1', 9999))         #客户端请求连接127.0.0.1的9999端口,最好不要选择1024之前的
# 接收欢迎消息:
print s.recv(1024)
for data in ['Bob','John', 'Sara','Tom']:
    # 发送数据:
    s.send(data)                        #多个请求,服务器端需要建立多线程
    print s.recv(1024)
s.send('exit')                 #数据发送完毕
s.close()
</span>


<span style="font-size:14px;">#!/usr/bin/env python
# -*- coding: utf-8 -*-

'a server example which send hello to client.'

import time, socket, threading             #库文件

def tcplink(sock, addr):
    print 'Accept new connection from %s:%s...' % addr
    sock.send('Welcome!')
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if data == 'exit' or not data:
            break
        sock.send('Hello, %s!' % data)
    sock.close()
    print 'Connection from %s:%s closed.' % addr

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)     #建立 一个socket
# 监听端口:
s.bind(('127.0.0.1', 9999))
s.listen(5)                                           #设置监听的包为5
print 'Waiting for connection...'
while True:
    # 接受一个新连接:
    sock, addr = s.accept()                           #包括IP地址和sock端口号
    # 创建新线程来处理TCP连接:
    t = threading.Thread(target=tcplink, args=(sock, addr))   #线程处理的方式为tcplink
    t.start()
</span>


接下来附上UDP client和UDP server端的Python代码:

<span style="font-size:14px;">#!/usr/bin/env python
# -*- coding: utf-8 -*-

'a socket example which send echo message to server.'

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)          #UDP无需建立连接,直接发送数据
for data in ['Bob', 'John', 'Sara','Tom']:     
    # 发送数据:
    s.sendto(data, ('127.0.0.1', 9999))       #将数据发送到服务器127.0.0.1的9999端口的进程
    # 接收数据:
    print s.recv(1024)
s.close()
</span>


<span style="font-size:14px;">#!/usr/bin/env python
# -*- coding: utf-8 -*-

'a udp server example which send time to client.'

import socket

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
    s.sendto('Hello, %s!' % data, addr)
</span>

以上整个socket编程的初步工作已经完成,通过socket编程更加来了解TCP、UDP两种协议,这也告诉我们网络中的协议的实现都是需要编程来实现的。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值