socket 编程-简单介绍
使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。
socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。
其主要流程如下:
1.服务端首先初始化Socket(),然后和接口进行绑定bind()和监听listen(),然后调用accept()进行阻塞。
2.客户端初始化socket(),然后调用connect()与服务端进行连接,然后客户端负责发送请求,服务端接收并且处理请求。write(),read().在 UNIX/Linux 系统中,为了统一对各种硬件的操作,简化接口,不同的硬件设备也都被看成一个文件。对这些文件的操作,等同于对磁盘上普通文件的操作。
记录常用的函数接口
一. 创建socket
int socket(int domain, int type, int protocol);
说明:
创建socket,类型为AF_LOCAL或AF_UNIX,表示用于进程通信:
参数:
domain:指定协议族,对于本地套接字来说,值必须设置为AF_UNIX枚举值;
type:指定套接字类型,可以被设置为SOCK_STREAM(流式套接字)活SOCK_DGRAM(数据报式套接字)
protocol:指定具体的协议,应被设置为0
返回值:
返回值为生成的套接字描述符。
注意:
对于本地套接字来说,流式套接字(SOCK_STREAM)是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道;
数据报式套接字(SOCK_DGRAM)相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,
但由于其在本地通信,不通过外界网络,这些情况出现的概率很小。
二. 设置socket参数
SOCK_STREAM式本地套接字的通信双方均需要有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用struct sockaddr_un类型的变量
struct sockaddr_un{
sa_family_t sun_family; // AF_UNIX
char sun_path[UNIX_PATH_MAX]; // 路径名
}
三. 绑定
int bind(int socket, const struct sockaddr *address, size_t address_len);
参数
socket:服务端套接字描述符
address:需要绑定的服务端本地地址
address_len:本地地址的字节长度
四. 监听
服务器端套接字创建完毕并赋予本地地址值(名称,本例中为CAN_SERVICE)后,需要进行监听,等待客户端连接并处理请求,监听使用 listen 系统调用,
int listen(int socket, int backlog);
参数
socket:表示服务器端的套接字描述符;
backlog 表示排队连接队列的长度(若有多个客户端同时连接,则需要进行排队);
五、阻塞等待
接受客户端连接使用accept系统调用,它们的原形如下:
int accept(int socket, struct sockaddr *address, size_t *address_len);
参数
address 表示当前连接客户端的本地地址,该参数为输出参数,是客户端传递过来的关于自身的信息;
address_len 表示当前连接客户端本地地址的字节长度,这个参数既是输入参数,又是输出参数。实现监听、接受和处理。
六. 连接
客户端需要 调用connect()连接到服务端,其函数原型如下:
int connect(int socket, const struct sockaddr *address, size_t address_len);
参数
socket:客户端的套接字描述符
address:当前客户端的本地地址,是一个 struct sockaddr_un 类型的变量
address_len:表示本地地址的字节长度
七. 数据交互
无论客户端还是服务器,都要和对方进行数据上的交互。一个进程扮演客户端的角色,另外一个进程扮演服务器的角色,
两个进程之间相互发送接收数据,这就是基于本地套接字的进程通信。
循环读取客户端发送的消息,当客户端没有发送数据时会阻塞直到有数据到来。如果想要多个连接并发处理,需要创建线程,
将每个连接交给相应的线程并发处理。接收到数据后,进行相应的处理,将结果返回给客户端。发送和接收数据要使用 write 和 read 系统调用,它们的原形为:
int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);
代码示例:
服务端代码
#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"); unlink("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); if (recvlen == -1) { perror("recv error"); return -1; } else if (recvlen == 0) { printf("client disconnet...\n"); close(cfd); break; } else { printf("ser:recv buf %s\n", buf); send(cfd, buf, recvlen, 0); } } close(cfd); close(lfd); return 0; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/un.h> #include <arpa/inet.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"); unlink("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("cli:recv buf %s\n", buf); } close(lfd); return 0; }