做了一大堆东西,发现最重要的还没有做:发送/接收数据。如果有一个程序能够自动帮我们把上面的东西都做掉,这样我们就可以只关心数据的读写,编程就简单的多了。那么这样一个程序就是 socket,它现在已经是操作系统的一部分,在 linux 中是标准的系统调用,只要调用它提供的一组接口(下面会详解常用函数的使用),就能轻松地建立连接,读写数据,关闭连接,让网络操作就像文件操作一样简单。 Show 这下体会到 7 unix 哲学的优点了吧。通信地址现实生活中,两个人要邮寄信件,必须知道对方的地址。网通信也是如此,只不过这里通信的是程序。程序的地址由三元组(ip 地址,端口,协议)界定。 如果你了解网络协议模型的话,你就会知道,ip 地址是网络层用来路由和通信的标识符,端口(port) 是传输层管理的。而 socket 是在这两层之上,所以需要这两个地址来标识。这里的协议指的是 ipv4,ipv6 或者其他协议。 socket 类型创建 socket 的时候需要指定 socket 的类型,一般有三种:
术语表名称含义socket创建一个通信的管道bind把一个地址三元组绑定到 socket 上listen准备接受某个 socket 的数据accept等待连接到达connect主动建立连接send发送数据receive接受数据close关闭连接字节序不同的计算机对数据的存储格式不一样,比如 32 位的整数 0x12345678,可以在内存里从高到低存储为 12-34-56-78 或者从低到高存储为 78-56-34-12。关于字节序的内容不会详细介绍,不了解的可以自己查阅相关的资料。 但是这对于网络中的数据来说就带来了一个严重的问题,当机器从网络中收到 12-34-56-78 的数据时,它怎么知道这个数据到底是什么意思? 解决的方案也比较简单,在传输数据之前和接受数据之后,必须调用 htonl/htons 或 ntohl/ntohs 先把数据转换成网络字节序或者把网络字节序转换为机器的字节序。
C 语言 socket 使用创建一个 socket
如果出错的话,socketid 返回值是 -1。 关闭(close) socket
关闭连接,释放端口。如果有错,返回值 status 为 -1,否则为 0。 绑定(bind)地址三元组到 socket
把 socket 绑定到某个地址三元组,用于 server 端监听端口。第一个参数是 socket 的描述符,第二个参数 8 是地址结构体,第三个参数是地址结构体的长度。绑定失败的话返回值为负数,否则为 -1,并且设置 9。其中最重要的就是地址结构体,它在 0 中被定义:
其中, 1 也是在同一个文件夹被定义,格式为:
服务器端的 s_addr 是本机地址,可以用 2 变量表示接受来自任何地址的连接,记得在使用之前把地址变量初始化为全 0。 3 是通用的 socket 地址结构, 4 是网络 socket 的结构,参数有一个类型转换的过程。监听(listen)socket
5 系统调用让进城坚监听在制定的 socket 上面,第一个参数是 socket 描述符,第二个参数是最大连接数,表示发来请求但是没有被 accept 的连接数量。 5 函数在成功时返回 0,失败时返回 -1,并且设置错误代码。请求连接(connect)客户端要连接自己的 socket 和服务器端监听 socket 的方式就是 7:
socket 是客户端本地创建的套接字, 8 是服务器的三元组地址。成功调用时,服务器端将收到请求, 9 连接之后,就在两者之间建立了 socket 通信的管道,之后的读写就是直接对 socket 进行操作。接受(accept)连接
当客户端有连接请求过来时, 9 函数接受该连接,把客户端的 socket 地址信息保存到 1 变量里,新建一个 socket,返回其描述符,然后数据的读写就能通过新 socket 进行。新 socket 的地址和服务器监听 socket 是一样的,如果不关心客户端地址信息的话,可以把第二个和第三个参数都设置为空指针 2。有了 1 变量,就能得到客户端的 ip 和 port :
如果没有客户端连接, 9 函数将会阻塞,直到有连接过来。读/写(Write)数据上面那么多的函数调用,只是建立了服务器端和客户端的连接,算是通信前的准备工作,两者都有了自己的 socket 描述符。
需要注意的是, 5 函数调用是阻塞的,也就是说如果没有数据发送过来的话,该函数会一直等待,直到可以读到数据。 5和 7 返回的是实际读写的数据,这个数据最大是 buffer 的大小。如果传输的数据大于 buffer 的话,需要在程序里显式地去读取,否则会出错。你可能会想,我一直读到返回的数据小于 sizeof(buff) 不就行了。嗯,这是一个解决方案,不过要判断返回值不是 0,因为返回值是 0 表示连接已经中断(需要调用 close 来关闭 socket),而不是没有数据发送过来。 其他常用函数
简单的 echo server有了上面的知识,我们就来写一个简单的 echo server。这个 server 的功能非常简单,它默认监听在本机的 54321 端口,接受 client 端连接,然后把客户端发送的数据加上时间戳发送回去。 6用 telnet 测试的结果如下: 这个程序没有很好的错误检查,而且每次只能和一个客户端进行通信,后面接进来的客户端必须要等到前面的客户端主动结束之后才能开始。以后会讲到怎么处理多连接的问题,这个例子只是 socket 基础知识的 demo。 |