本期主题:
介绍linux网络编程之socket通信
文章目录
1.socket定义
1.1 从操作系统层面讲
socket是一种特殊的文件
在操作系统中,所有的IO设备(磁盘、外设、网络等)都被模型化成为文件,所有的输入输出动作都被当成相应的文件来进行读、写操作。
这些文件都通过操作系统的VFS机制(虚拟文件系统机制)挂载在linux内核中,对外提供统一的文件操作接口。
因此在网络通信中,为了适配各种复杂的网络协议栈,在网络和进程之间添加了一个抽象层,这一层就被称为socket(套接字),套接字的作用:
- 客户端和服务器通过套接字接口来建立连接,连接以文件描述符的形式提供给进程,客户端和服务器通过读写这些描述符来实现彼此之间的通信,因此从这个角度来说,socket其实是一种特殊的文件。
1.2 从TCP/IP协议栈讲
socket是对ip地址、传输协议、端口号的抽象,是从应用层进入到传输层的接口
1 TCP/IP协议是什么,与ISO7层模型的对照关系
TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,不同于ISO模型的7层结构,TCP/IP协议将所有的TCP/IP协议归类到4个抽象层中,分层可以按照如下方式:
应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
传输层:TCP,UDP
网络层:IP,ICMP,OSPF,EIGRP,IGMP
数据链路层:SLIP,CSLIP,PPP,MTU
对比7层网络协议和4层协议可知socket的具体位置:
2 网络中的两台主机是如何传播的
在TCP/IP协议中,两台网络中的主机通过路由器相连,各主机的应用通过刚介绍的各个层次来进行数据的传播
在本地的进程之间进行通信,需要知道的是两个进程的进程号PID,但是进程号只是在本地的概念,在网络通信中,进程号是很有可能发生冲突的,因此我们使用ip地址+传输层网络协议+端口号来标明想通信的进程。按照这样的思路,我们可以将网络通信简化成以下模型:
从这张图来看,socket是一个对上承接应用进程,对下对运输层协议的一层抽象层。因此,可将socket看成一个对 ip地址、传输协议、端口号的一个抽象内容。
2.socket使用的例子
2.1 socket通信流程
socket的使用与linux中对其他设备的使用模式类似,先打开设备,然后调用对应的API,然后关闭设备。
其中TCP socket的一个常用交互模式如下:
2.2 示例代码
//server.c
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#define MY_SOCK_PATH "/somepath"
#define LISTEN_BACKLOG 50
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define SERVERADDR "192.168.123.100"
#define SERPORT 6600
int
main(int argc, char *argv[])
{
int sock_fd, cfd = 0;
int ret, i = 0;
socklen_t len = 0;
char recv_buf[100] = {0};
struct sockaddr_in seraddr, cliaddr = {0};
//stpe1: 创建socket套接字
sock_fd = socket(AF_INET, SOCK_STREAM, 0); //用AF_INET就可,用AF_UNIX就一直bind失败
if (sock_fd == -1)
handle_error("socket");
printf("socket success!\n");
seraddr.sin_family = AF_INET;
// seraddr.sin_addr.s_addr = inet_addr(SERVERADDR);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
seraddr.sin_port = htons(SERPORT);
printf("ip addr: %ld, port: %d\n", seraddr.sin_addr.s_addr, seraddr.sin_port);
//step2: 将socket以及ip port绑定在一起
if (bind(sock_fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
handle_error("bind");
printf("bind success\n");
//step3: 监听该端口
if (listen(sock_fd, LISTEN_BACKLOG) == -1)
handle_error("listen");
printf("listen success\n");
//step4: 接受该端口,并且返回与其相连的socket另一端
len = sizeof(cliaddr);
cfd = accept(sock_fd, (struct sockaddr *)&cliaddr, &len);
if (cfd == -1)
handle_error("accept");
printf("accept success!\n");
//step5: 接收另外一端发来的数据
ret = recv(cfd, recv_buf, sizeof(recv_buf), 0);
if (ret == -1)
handle_error("recv");
printf("recv success, the recv number is : \n");
for (i = 0; i< sizeof(recv_buf); i++)
{
printf("recv_buf[%d] : %d \n", i, recv_buf[i]);
}
}
//client.c
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#define MY_SOCK_PATH "/somepath"
#define LISTEN_BACKLOG 50
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define SERVERADDR "192.168.123.100"
#define SERPORT 6600
int
main(int argc, char *argv[])
{
int sock_fd, cfd = 0;
int ret, i = 0;
socklen_t len = 0;
char send_buf[100] = {0};
struct sockaddr_in seraddr, cliaddr = {0};
sock_fd = socket(AF_INET, SOCK_STREAM, 0); //用AF_INET就可,用AF_UNIX就一直bind失败
if (sock_fd == -1)
handle_error("socket");
printf("socket success!\n");
seraddr.sin_family = AF_INET;
// seraddr.sin_addr.s_addr = inet_addr(SERVERADDR);
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
seraddr.sin_port = htons(SERPORT);
printf("ip addr: %ld, port: %d\n", seraddr.sin_addr.s_addr, seraddr.sin_port);
// if (bind(sock_fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
// handle_error("bind");
// printf("bind success\n");
// if (listen(sock_fd, LISTEN_BACKLOG) == -1)
// handle_error("listen");
// printf("listen success\n");
// cfd = accept(sock_fd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
// if (cfd == -1)
// handle_error("accept");
// printf("accept success!\n");
// ret = recv(cfd, recv_buf, sizeof(recv_buf), 0);
// if (ret == -1)
// handle_error("recv");
// printf("recv success, the recv number is : \n");
// for (i = 0; i< sizeof(recv_buf); i++)
// {
// printf("recv_buf[%d] : %d, ", i, recv_buf[i]);
// }
if (connect(sock_fd, (struct sockaddr *)&seraddr, sizeof(seraddr)) == -1)
handle_error("connect");
printf("connect success\n");
for (i = 0; i< sizeof(send_buf); i++)
{
send_buf[i] = i;
}
ret = send(sock_fd, send_buf, sizeof(send_buf), 0);
if (ret == -1)
handle_error("send");
printf("send success\n");
}
github链接地址为:源码地址