Linux下的Socket编程
主要函数
int socket(int domain, int type, int protocol)
创建一个用于网络通信的socket套接字,唯一标识一个socket,后续的网络通信会以它为参数,来进行一些通信操作。
参数:
domain:协议族,它决定了socket的地址类型,在通信中必须采用对应的地址。常用的协议族有AF_INET、AF_INET6、AF_UNIX、AF_ROUTE等等。例如,AF_INET决定了要使用ipv4地址(32位)与端口号(16位)的组合;AF_UNIX决定了要用一个绝对路径名作为地址。
type:socket类型,常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。例如,SOCK_STREAM决定了套接字类型为流套接字,适用于TCP协议。
protocol:协议类别,常见的有0、IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等等。例如,设为0,会自动选择type类型对应的默认协议;IPPROTO_TCP决定协议类别为TCP协议。
返回值:
- 成功:socket描述符。
- 失败:-1,并将errno设置为对应的错误。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
将套接字与特定的IP地址和端口绑定起来,以便套接字处理之后流经该IP地址和端口的数据。
参数:
sockfd:socket描述符,通过socket()函数创建,唯一标识一个socket。bind()函数将这个描述字绑定一个名字。
addr:const struct sockaddr *指针,指定绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,IPv4和IPv6就有所差别。
1 | //IPv4: |
- addrlen:socket地址的长度。
int listen(int sockfd, int backlog)
将socket由主动类型变为被动类型,等待客户的连接请求,并使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接。
参数:
- sockfd:bind()绑定之后的socket描述符。
- backlog:相应socket连接队列的长度,即可以排队等待的最大连接个数。
返回值:
- 成功:0。
- 失败:-1,并将errno设置为对应的错误。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
客户端通过调用connect()函数来建立与TCP服务器的连接。
参数:
- sockfd:socket()函数返回的socket描述符。
- addr:指定被连接的服务器端地址和端口信息。
- addrlen:socket地址的长度。
返回值:
- 成功:0.
- 失败:-1,并将errno设置为对应的错误。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
由TCP服务器调用,响应连接请求,建立与client的连接。
参数:
- sockfd:listen()函数监听后的通过socket描述符。
- addr:返回已连接客户端的IP、端口等信息。
- addrlen:返回真实addr所指向结构的大小。
返回值:
成功:非负的,客户端和服务端之间进行数据传输的socket描述符。
失败:-1,并将errno设置为对应的错误。
ssize_t read(int filedes, void* buf, size_t nbytes)
读取文件中的数据,并把它放到数据缓存区中。
参数:
- filedes:文件描述符,指定所需要读取的文件。
- buf:数据缓存区,指定读取后文件内容所要存放的区域。
- nbytes:字节数,指定所要读取的字节数。
返回值:
- 成功:实际读取的字节数。
- 失败:-1,并将errno设置为对应的错误。
- 达到文件末尾:0。
ssize_t write(int filedes, void* buf, size_t nbytes)
将数据缓存区中的内容写入文件中。
参数:
- filedes:文件描述符,指定所需要写入的文件。
- buf:数据缓存区,指定要写入文件的内容所在区域。
- nbytes:字节数,指定所要写入的字节数。
返回值:
- 成功:成功写入的字节数。
- 失败:-1,并将errno设置为对应的错误。
ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags)
用于TCP协议中发送数据。
参数:
- sockfd:指定发送端的socket描述符。
- buf:存放要发送数据的缓冲区。
- nbytes:实际要发送的数据字节数。
- flags:置为0或者下表中几个值。
flags | 说明 |
---|---|
MSG_DONTROUTE | 绕过路由表查找 |
MSG_DONTWAIT | 仅本操作非阻塞 |
MSG_OOB | 发送或接收带外数据 |
返回值:
- 成功:已发送的字节数。
- 失败:-1,并将errno设置为对应的错误。
ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags)
用于TCP协议中接收数据。
参数:
- sockfd:指定接收端的socket描述符。
- buf:指向接收数据缓冲区的指针。
- nbytes:buf缓冲区的大小。
- flags:置为0或者下表中几个值。
flags | 说明 |
---|---|
MSG_DONTWAIT | 仅本操作非阻塞 |
MSG_OOB | 发送或接收带外数据 |
MSG_PEEK | 窥看外来数据 |
MSG_WAITALL | 等待所有数据 |
返回值:
- 成功:实际接收的字节数。
- 失败:-1,并将errno设置为对应的错误。
- 对应端已关闭:0。
int close(int sockfd)
用于TCP协议中关闭特定的socket连接。
参数:
- socked:指定要关闭的socked描述符。
返回值:
- 成功:0。
- 失败:-1,并将errno设置为对应的错误。
编程实现
服务端
作为常驻程序,为客户端提供服务。能够根据客户端的不同选择,去进行信息交互或者是传输文件。
代码:
1 |
|
客户端
与服务端建立连接,和服务端交互。能和服务端之间收发消息,请求并接收文件。
代码:
1 |
|
运行测试
服务端运行在虚拟机的Kali Linux中,客户端运行在本机的mac os上面。先启动服务端,服务端处于监听状态。
再启动客户端,这时即可与服务端进行连接,同时客户端显示可选择的服务。
收发传输
先输入1,选择“传输消息”服务。在终端输入信息,即可传送给服务端。服务端接收到消息,也可在终端打印。当不想进行通信时,输入“quit”,即可退出。服务端可以继续保持在线,等待其他客户端接入,选择不同的服务。
传输文件
再运行一个客户端,选择“传输文件”服务。在终端输入文件名传送到服务端,当接收到文件之后,可以选择继续获取文件或者是终止连接。
服务端可以根据文件名查找文件,并将文件传输过去。
之后就可以在“网络通信”文件夹中找到传输过来的图片。
需要说明的是,当客户端只给出文件名时,服务端是只会在server程序所在文件夹内寻找文件的,并不在整个计算机范围内寻找。并且有许多没有考虑到的地方,如果操作不当,可能造成程序崩溃退出。
参考: