**
先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题。想要获取完整源码的,关注公众号后回复“socket1”即可。
一 what
**
socket套接字也是一种文件格式,只是和管道文件一样,它是一种伪文件,存在于内核的缓冲区中,大小不变,一直是0.
套接字一定是成对出现的,有server套接字,就一定有client套接字,它是一种全双工通信方式,分别有读写缓冲区
通信框图
cs架构 (bs架构,browser server)
服务器 server 客户端client
1. 请求协议版本 1. 请求协议版本
2. 创建socket 2. 创建socket
3. 创建协议地址族 3. 获取服务器协议地址族
ip地址
网络端口
通信协议
4. 绑定
5. 监听
6. 等待客户端连接 4. 连接服务器
7. 通信 5. 通信
8. 关闭socket 6. 关闭socket
9. 清理协议版本 7. 清理协议版本
和套接字相关的几个地址结构体如下,struct sockaddr用来描述ipv4地址协议
和地址转换相关的几个函数
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
**
**
三 how
**
本地套接字用于实现本机进程间通信,有tcp和udp类似两种,我们以类似tcp方式举例
主机 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. 断开连接
代码如下
server.c
int main(int argc, char *argv[])
{
......
//创建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:%s\n", client.sun_path);
while (1) {
recvlen = recv(cfd, buf, sizeof(buf), 0);
......
}
......
}
客户端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
代码如下
client.c
int main(int argc, char *argv[])
{
......
//创建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 %s\n", buf);
}
......
}
分别编译后,先运行./server,我们发现在当前目录下创建了一个serve.sock的套接字文件,文件类型位s
然后运行./client,同样发现创建了一个套接字文件client.sock,然后在client输入信息,就会在server端接收到信息了。
但是我们会遇到一个问题,当我们停止./server或者./client,在运行./server或者./client,会发现一个错误提示
这是因为套接字文件已经存在,不能再bind了,我们需要修改源程序,在bind之前,加上unlink,就是说如果套接字文件存在,需要先删除套接字文件。
server.c
int main(int argc, char *argv[])
{
......
//创建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:%s\n", client.sun_path);
while (1) {
recvlen = recv(cfd, buf, sizeof(buf), 0);
......
}
......
}
client.c
int main(int argc, char *argv[])
{
......
//创建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 %s\n", buf);
}
......
}
想要获取完整源码的,请关注微信公众号:嵌入式Linux江湖,关注公众号后回复“socket1”即可。