Chinaunix首页 | 论坛 | 博客
  • 博客访问: 651010
  • 博文数量: 195
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 894
  • 用 户 组: 普通用户
  • 注册时间: 2015-09-21 16:48
文章分类

全部博文(195)

文章存档

2024年(1)

2023年(1)

2022年(3)

2021年(16)

2020年(4)

2019年(5)

2018年(10)

2017年(72)

2016年(36)

2015年(47)

我的朋友

分类: 嵌入式

2018-05-08 16:03:53

    转:https://blog.csdn.net/u010073981/article/details/50734484
    socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).

说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

注意:其实socket也没有层的概念,它只是一个facade设计模式的应用,让编程变的更简单。是一个软件抽象层。在网络编程中,我们大量用的都是通过socket实现的。

   使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。

   其主要流程如下:


一. 创建socket

   创建socket,类型为AF_LOCALAF_UNIX,表示用于进程通信:

调用函数socket(),其原型如下:

int socket(int domain, int type, int protocol);

参数:

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];  // 路径名

}

三. 绑定

    绑定要使用 bind 系统调用,其原形如下:    

int bind(int socket, const struct sockaddr *address, size_t address_len);

参数

socket:服务端套接字描述符

address:需要绑定的服务端本地地址

address_len:本地地址的字节长度


四. 监听

    服务器端套接字创建完毕并赋予本地地址值(名称,本例中为CAN_SERVICE)后,需要进行监听,等待客户端连接并处理请求,监听使用 listen 系统调用,接受客户端连接使用accept系统调用,它们的原形如下:
int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);
参数

socket:表示服务器端的套接字描述符;

backlog 表示排队连接队列的长度(若有多个客户端同时连接,则需要进行排队);

address 表示当前连接客户端的本地地址,该参数为输出参数,是客户端传递过来的关于自身的信息;

address_len 表示当前连接客户端本地地址的字节长度,这个参数既是输入参数,又是输出参数。实现监听、接受和处理。

五. 连接

    客户端需要socket系统调用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);


以下为简单的,socket本地进程间通讯的例子:

1. 服务端

[cpp] view plain copy
  1.    
  2. #include     
  3. #include     
  4. #include     
  5. #include      
  6.      
  7. #define CAN_SERVICE "CAN_SERVICE"   
  8. int main(void)    
  9. {        
  10.     int ret;       
  11.     int len;    
  12.     int accept_fd;  
  13.     int socket_fd;   
  14.     static char recv_buf[1024];  
  15.     socklen_t clt_addr_len;   
  16.     struct sockaddr_un clt_addr;    
  17.     struct sockaddr_un srv_addr;    
  18.   
  19.     socket_fd=socket(PF_UNIX,SOCK_STREAM,0);    
  20.     if(socket_fd<0)    
  21.     {    
  22.         perror("cannot create communication socket");    
  23.         return 1;    
  24.     }      
  25.             
  26.     // 设置服务器参数    
  27.     srv_addr.sun_family=AF_UNIX;    
  28.     strncpy(srv_addr.sun_path,CAN_SERVICE,sizeof(srv_addr.sun_path)-1);    
  29.     unlink(CAN_SERVICE);    
  30.   
  31.     // 绑定socket地址   
  32.     ret=bind(socket_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));    
  33.     if(ret==-1)    
  34.     {    
  35.         perror("cannot bind server socket");    
  36.         close(socket_fd);    
  37.         unlink(CAN_SERVICE);    
  38.         return 1;    
  39.     }    
  40.   
  41.     // 监听     
  42.     ret=listen(socket_fd,1);    
  43.     if(ret==-1)    
  44.     {    
  45.         perror("cannot listen the client connect request");    
  46.         close(socket_fd);    
  47.         unlink(CAN_SERVICE);    
  48.         return 1;    
  49.     }    
  50.   
  51.     // 接受connect请求   
  52.     len=sizeof(clt_addr);    
  53.     accept_fd=accept(socket_fd,(struct sockaddr*)&clt_addr,&len);    
  54.     if(accept_fd<0)    
  55.     {    
  56.         perror("cannot accept client connect request");    
  57.         close(socket_fd);    
  58.         unlink(CAN_SERVICE);    
  59.         return 1;    
  60.     }    
  61.   
  62.     // 读取和写入    
  63.     memset(recv_buf,0,1024);    
  64.     int num=read(accept_fd,recv_buf,sizeof(recv_buf));    
  65.     printf("Message from client (%d)) :%s\n",num,recv_buf);      
  66.            
  67.     // 关闭socket  
  68.     close(accept_fd);    
  69.     close(socket_fd);    
  70.     unlink(CAN_SERVICE);    
  71.     return 0;    
  72. }    


2. 客户端

[cpp] view plain copy
  1. #include     
  2. #include     
  3. #include     
  4. #include     
  5.    
  6. #define CAN_SERVICE "CAN_SERVICE"   
  7. int main(void)    
  8. {   
  9.     int ret;    
  10.     int socket_fd;     
  11.     char snd_buf[1024];     
  12.     static struct sockaddr_un srv_addr;    
  13.   
  14. // 创建socket   
  15.     socket_fd=socket(PF_UNIX,SOCK_STREAM,0);    
  16.     if(socket_fd<0)    
  17.     {    
  18.         perror("cannot create communication socket");    
  19.         return 1;    
  20.     }       
  21.     srv_addr.sun_family=AF_UNIX;    
  22.     strcpy(srv_addr.sun_path,CAN_SERVICE);    
  23.   
  24. // 连接到服务器    
  25.     ret=connect(socket_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));    
  26.     if(ret==-1)    
  27.     {    
  28.         perror("cannot connect to the server");    
  29.         close(socket_fd);    
  30.         return 1;    
  31.     }    
  32.     memset(snd_buf,0,1024);    
  33.     strcpy(snd_buf,"message from client");    
  34.   
  35. // 读取和写入      
  36.     write(socket_fd,snd_buf,sizeof(snd_buf));    
  37.     close(socket_fd);    
  38.     return 0;    
  39. }   

gcc -o service service.c 

gcc -o client client.c

运行


与本地套接字相对应的是网络套接字,可以用于在网络上传送数据,换言之,可实现不同机器上的进程通信过程。在 TCP/IP 协议中,IP 地址的首字节为 127 即代表本地,因此本地套接字通信可以使用 IP 地址为 127.x.x.x 的网络套接字来实现。

阅读(896) | 评论(0) | 转发(0) |
0

上一篇:Socket介绍

下一篇:fstat、stat和lstat 区别

给主人留下些什么吧!~~