小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本地通信
1 相关概念
套接字通信最开始也和前六种进程间通信方式一样,也是只能实现一台主机的多个进程间通信,称之为本地通信,UNIX域套接字
- socket同样可以用于本地通信
- 创建套接字时使用本地协议AF_UNIX(或AF_LOCAL)。
- 分为流式套接字和用户数据报套接字
- 和其他进程间通信方式相比使用方便、效率更高
- 常用于前后台进程通信
2 本地信息结构体
#include <sys/un.h> struct sockaddr_un { sa_family_t sun_family; 地址族,AF_UNIX char sun_path[108]; 文件名,最终会创建一个套接字文件 };
1 TCP本地通信
1.1 流程
服务器:
- 创建套接字 socket( )
- 填充服务器本地信息结构体 sockaddr_in
- 将套接字与服务器本地信息结构体绑定 bind( )
- 将套接字设置为被动监听状态 listen( )
- 阻塞等待客户端的连接请求 accept( )
- 进行通信 recv( )/send( )
客户端:
- 创建套接字 socket( )
- 填充服务器本地信息结构体 sockaddr_in
- 发送客户端的连接请求 connect( )
- 进行通信 send( )/recv( )
1.2 服务器
//TCP本地通信之服务器
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>
#include <sys/un.h>
#define N 128
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
int main(int argc, char const *argv[])
{
if(argc < 2)
{
fprintf(stderr, "Usage: %s <socket file>\n", argv[0]);
exit(1);
}
int sockfd, acceptfd;
struct sockaddr_un serveraddr, clientaddr;
socklen_t addrlen = sizeof(serveraddr);
char buf[N] = {0};
ssize_t bytes;
//第一步:创建套接字
if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
ERRLOG("socket error");
}
//第二步:填充服务器本地信息结构体
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, argv[1]);
//第三步:将套接字与服务器网络信息结构体绑定
if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
{
ERRLOG("bind error");
}
//第四步:将套接字设置为被动监听状态
if(listen(sockfd, 5) == -1)
{
ERRLOG("listen error");
}
NEXT:
//第五步:阻塞等待客户端的连接
if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
{
ERRLOG("accept error");
}
//进行通信
while(1)
{
if((bytes = recv(acceptfd, buf, N, 0)) == -1)
{
ERRLOG("recv error");
}
else if(bytes == 0)
{
printf("客户端退出了\n");
goto NEXT;
}
if(strcmp(buf, "quit") == 0)
{
printf("客户端退出了\n");
goto NEXT;
}
printf("客户端:%s\n", buf);
strcat(buf, "^_^");
if(send(acceptfd, buf, N, 0) == -1)
{
ERRLOG("send error");
}
}
return 0;
}
1.3 客户端
//TCP本地通信之客户端
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>
#include <sys/un.h>
#define N 128
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
int main(int argc, char const *argv[])
{
if(argc < 2)
{
fprintf(stderr, "Usage: %s <socket file>\n", argv[0]);
exit(1);
}
int sockfd;
struct sockaddr_un serveraddr;
socklen_t addrlen = sizeof(serveraddr);
//第一步:创建套接字
if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
ERRLOG("socket error");
}
//第二步:填充服务器本地信息结构体
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, argv[1]);
//第三步:给服务器发送客户端的连接请求
if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
{
ERRLOG("connect error");
}
//进行通信
char buf[N] = {0};
while(1)
{
fgets(buf, N, stdin);
buf[strlen(buf) - 1] = '\0';
if(send(sockfd, buf, N, 0) == -1)
{
ERRLOG("send error");
}
if(strcmp(buf, "quit") == 0)
{
printf("客户端退出了\n");
exit(0);
}
memset(buf, 0, N);
if(recv(sockfd, buf, N, 0) == -1)
{
ERRLOG("recv error");
}
printf("服务器:%s\n", buf);
}
return 0;
}
2 UDP本地通信
1.1 流程
服务器:
- 创建套接字 socket( )
- 填充服务器本地信息结构体 sockaddr_un
- 将套接字与服务器本地信息结构体绑定 bind( )
- 进行通信 recvfrom( ) / sendto( )
客户端:
- 创建套接字 socket( )
- 填充客户端本地信息结构体 sockaddr_un
- 将套接字与客户端本地信息结构体绑定
- 填充服务器本地信息结构体 sockaddr_un
- 进行通信 sendto( ) / recvfrom( )
2.2 服务器
//UDP本地通信之服务器
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>
#include <sys/un.h>
#define N 128
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
int main(int argc, char const *argv[])
{
if(argc < 2)
{
fprintf(stderr, "Usage: %s <socket file>\n", argv[0]);
exit(1);
}
int sockfd;
struct sockaddr_un serveraddr, clientaddr;
socklen_t addrlen = sizeof(serveraddr);
char buf[N] = {0};
//第一步:创建套接字
if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
{
ERRLOG("socket error");
}
//第二步:填充服务器本地信息结构体
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, argv[1]);
//第三步:将套接字与服务器网络信息结构体绑定
if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
{
ERRLOG("bind error");
}
//进行通信
while(1)
{
NEXT:
if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
{
ERRLOG("recvfrom error");
}
if(strcmp(buf, "quit") == 0)
{
printf("客户端退出了\n");
goto NEXT;
}
printf("客户端[%s]:%s\n", clientaddr.sun_path, buf);
strcat(buf, "^_^");
if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, addrlen) == -1)
{
ERRLOG("sendto error");
}
}
return 0;
}
2.3 客户端
//UDP本地通信之客户端
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <string.h>
#include <sys/un.h>
#define N 128
#define ERRLOG(errmsg) do{\
perror(errmsg);\
printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
exit(1);\
}while(0)
int main(int argc, char const *argv[])
{
if(argc < 2)
{
fprintf(stderr, "Usage: %s <serversocket file> <clientsocket file>\n", argv[0]);
exit(1);
}
int sockfd;
struct sockaddr_un serveraddr;
socklen_t addrlen = sizeof(serveraddr);
//第一步:创建套接字
if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
{
ERRLOG("socket error");
}
//UDP本地通信里面,客户端必须绑定自己的信息,否则服务器无法
//获取客户端的信息,所以无法给客户端发送数据
struct sockaddr_un clientaddr;
clientaddr.sun_family = AF_UNIX;
strcpy(clientaddr.sun_path, argv[2]);
if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) == -1)
{
ERRLOG("bind error");
}
//第二步:填充服务器本地信息结构体
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, argv[1]);
//进行通信
char buf[N] = {0};
while(1)
{
fgets(buf, N, stdin);
buf[strlen(buf) - 1] = '\0';
if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
{
ERRLOG("sendto error");
}
if(strcmp(buf, "quit") == 0)
{
printf("客户端退出了\n");
exit(0);
}
memset(buf, 0, N);
if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&serveraddr, &addrlen) == -1)
{
ERRLOG("recvfrom error");
}
printf("服务器:%s\n", buf);
}
return 0;
}