先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
一 what
socket套接字也是一种文件格式,和管道文件一样,它是一种伪文件,存在于内核的缓冲区中,大小不变,一直是0。
套接字一定是成对出现的,有server套接字,就一定有client套接字,它是一种全双工通信方式,分别有读写缓冲区
通信框图,也称为cs架构如下
cs架构
1.1 和套接字相关的几个地址结构体
struct sockaddr用来描述ipv4地址协议
1.2 和地址转换相关的几个函数
ip地址转换函数
inet_pton 192.168.1.24 --------------> 网络字节序
/*
* 参数 af,网络ip地址的版本,ipv4或ipv6
* src 192.168.1.24
* dst 网络字节序
*/
int inet_pton(int af, const char *src, void *dst);
inet_ntop 网络字节序 --------------> 点分十进制
/*
* 参数 af,网络ip地址的版本,ipv4或ipv6
* src 网络字节序
* dst 192.168.1.24
* size 字符串dst长度
*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
二 why
为什么需要套接字呢?它的目的是实现进程间通信,既可以实现同一个linux内核(同一台设备)进程间通信;又可以实现不同的linux内核(不同设备)进程间通信,比如我们浏览网页的过程其实就是一个socket通信模型。
三 how
本地套接字用于实现本机进程间通信,有tcp和udp类似两种,我们以类似tcp方式举例。
3.1 server端的流程
1. 创建socket
int ifd = socket(AF_LOCAL, sock_stream, 0); //AF_UNIX也可以,AF_INET是指网络套接字
2. 绑定 struct sockaddr_un
struct sockaddr_un serv;
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.socket"); //server.socket此时还不存在的,它什么时候会存在呢
bind(ifd, (struct sockaddr *)&serv, sizeof(serv)); //绑定成功,server.socket这个文件就会被创建
3. 设置监听
listen()
4. 等待接收连接请求
struct sockaddr_un client;
int len = sizeof(client);
int cfd = accept(ifd, &client, &len);
5. 通信
send //发送数据
recv //接收数据
6. 断开连接
代码如下
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include <sys/stat.h>
#include "string.h"
#include <arpa/inet.h>
#include <sys/un.h>
int main(int argc, char *argv[])
{
int lfd ,ret, cfd;
struct sockaddr_un serv, client;
socklen_t len = sizeof(client);
char buf[1024] = {0};
int recvlen;
//创建socket
lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (lfd == -1) {
perror("socket error");
return -1;
}
//初始化server信息
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.sock");
//绑定
ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
if (ret == -1) {
perror("bind error");
return -1;
}
//设置监听,设置能够同时和服务端连接的客户端数量
ret = listen(lfd, 36);
if (ret == -1) {
perror("listen error");
return -1;
}
//等待客户端连接
cfd = accept(lfd, (struct sockaddr *)&client, &len);
if (cfd == -1) {
perror("accept error");
return -1;
}
printf("=====client bind file:%sn", client.sun_path);
while (1) {
recvlen = recv(cfd, buf, sizeof(buf), 0);
if (recvlen == -1) {
perror("recv error");
return -1;
} else if (recvlen == 0) {
printf("client disconnet...n");
close(cfd);
break;
} else {
printf("recv buf %sn", buf);
send(cfd, buf, recvlen, 0);
}
}
close(cfd);
close(lfd);
return 0;
}
3.2 client端的流程
1. 创建socket
int fd = socket(AF_LOCAL, sock_stream, 0); //AF_UNIX也可以,AF_INET是指网络套接字
2. 绑定套接字文件
struct sockaddr_un client;
client.sun_family = AF_LOCAL;
strcpy(client.sun_path, "client.socket"); //client.socket此时还不存在的,它什么时候会存在呢
bind(ifd, (struct sockaddr *)&client, len); //绑定成功,server.socket这个文件就会被创建
3. 连接服务器
struct sockaddr_un serv;
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.socket"); //server.socket此时还不存在的,它什么时候会存在呢
connect(fd, &serv, sizeof(serv))
4. 通信
recv
send
5. 关闭
close
代码如下
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include <sys/stat.h>
#include "string.h"
#include <arpa/inet.h>
#include <sys/un.h>
int main(int argc, char *argv[])
{
int lfd ,ret;
struct sockaddr_un serv, client;
socklen_t len = sizeof(client);
char buf[1024] = {0};
int recvlen;
//创建socket
lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (lfd == -1) {
perror("socket error");
return -1;
}
//给客户端绑定一个套接字文件
client.sun_family = AF_LOCAL;
strcpy(client.sun_path, "client.sock");
ret = bind(lfd, (struct sockaddr *)&client, sizeof(client));
if (ret == -1) {
perror("bind error");
return -1;
}
//初始化server信息
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.sock");
//连接
connect(lfd, (struct sockaddr *)&serv, sizeof(serv));
while (1) {
fgets(buf, sizeof(buf), stdin);
send(lfd, buf, strlen(buf)+1, 0);
recv(lfd, buf, sizeof(buf), 0);
printf("recv buf %sn", buf);
}
close(lfd);
return 0;
}
四 test4.1 编译运行
分别编译server.c和client.c,先运行./server,我们发现在当前目录下创建了一个serve.sock的套接字文件,文件类型是“s”,如下:
然后运行./client,同样发现创建了一个套接字文件client.sock,然后在client输入信息,就会在server端接收到信息了。
4.2 bug
我们会遇到一个问题,当我们停止./server或者./client,在运行./server或者./client,会发现一个错误提示:
这是因为套接字文件已经存在,不能再bind了,我们需要修改源程序,在bind之前,加上unlink,就是说如果套接字文件存在,需要先删除套接字文件。
server代码修改如下
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include <sys/stat.h>
#include "string.h"
#include <arpa/inet.h>
#include <sys/un.h>
int main(int argc, char *argv[])
{
int lfd ,ret, cfd;
struct sockaddr_un serv, client;
socklen_t len = sizeof(client);
char buf[1024] = {0};
int recvlen;
//创建socket
lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (lfd == -1) {
perror("socket error");
return -1;
}
//如果套接字文件存在,存删除套接字文件
unlink("server.sock");
//初始化server信息
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.sock");
//绑定
ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
if (ret == -1) {
perror("bind error");
return -1;
}
//监听
ret = listen(lfd, 36);
if (ret == -1) {
perror("listen error");
return -1;
}
//等待客户端连接
cfd = accept(lfd, (struct sockaddr *)&client, &len);
if (cfd == -1) {
perror("accept error");
return -1;
}
printf("=====client bind file:%sn", client.sun_path);
while (1) {
recvlen = recv(cfd, buf, sizeof(buf), 0);
if (recvlen == -1) {
perror("recv error");
return -1;
} else if (recvlen == 0) {
printf("client disconnet...n");
close(cfd);
break;
} else {
printf("recv buf %sn", buf);
send(cfd, buf, recvlen, 0);
}
}
close(cfd);
close(lfd);
return 0;
}
client代码如下:
#include "stdio.h"
#include "stdlib.h"
#include <unistd.h>
#include "sys/types.h"
#include <sys/stat.h>
#include "string.h"
#include <arpa/inet.h>
#include <sys/un.h>
int main(int argc, char *argv[])
{
int lfd ,ret;
struct sockaddr_un serv, client;
socklen_t len = sizeof(client);
char buf[1024] = {0};
int recvlen;
//创建socket
lfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (lfd == -1) {
perror("socket error");
return -1;
}
//如果套接字文件存在,存删除套接字文件
unlink("client.sock");
//给客户端绑定一个套接字文件
client.sun_family = AF_LOCAL;
strcpy(client.sun_path, "client.sock");
ret = bind(lfd, (struct sockaddr *)&client, sizeof(client));
if (ret == -1) {
perror("bind error");
return -1;
}
//初始化server信息
serv.sun_family = AF_LOCAL;
strcpy(serv.sun_path, "server.sock");
//连接
connect(lfd, (struct sockaddr *)&serv, sizeof(serv));
while (1) {
fgets(buf, sizeof(buf), stdin);
send(lfd, buf, strlen(buf)+1, 0);
recv(lfd, buf, sizeof(buf), 0);
printf("recv buf %sn", buf);
}
close(lfd);
return 0;
}