当前位置:   article > 正文

UNIX域套接字及TCP、UDP示例_udp unix域

udp unix域

UNIX域套接字用于在同一台机器上运行的进程之间的通信。虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高UNIX域套接字仅仅复制数据;它们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。


1.非命名的UNIX域套接字

UNIX域套接字用于在用一台机器上运行的进程之间通信,提供流和数据报两种接口。UNIX域数据报服务是可靠的,既不会丢失消息也不会传递出错。

UNIX域套接字是套接字和管道之间的混合物。

为了创建一对非命名的,相互连接的UNXI域套接字,用户可以使用socketpair函数。

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. int socketpair(int d, int type, int protocol, int sv[2]);

用socketpair创建出来的两个描述符是等价的( create a pair of connected sockets, The two sockets are indistinguishable )。

创建一对匿名的已连接的全双工的socket,建立的两个套接字描述符会放在sv[0]和sv[1]中

参数介绍:

第1个参数d(domain),表示协议族,只能为AF_LOCAL或者AF_UNIX;
第2个参数type,表示类型,只能为0。
第3个参数protocol,表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STREAM建立的套接字对是管道流,与一般的管道相区别的是,套接字对建立的通道是双向的,即每一端都可以进行读写。不管是数据流还是数据报协议,unix域套接字都是可靠的,不丢包的。

第4个参数sv,用于保存建立的套接字对。

为什么man sockepair原型如上,但是使用时,第二个和第三个参数反过来,才正确?

示例:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <sys/types.h>
  6. #include <unistd.h>
  7. int main(void)
  8. {
  9. int i_fd_arr[2];
  10. int i_pid;
  11. char psz_wbuf[16] = "0123456789";
  12. char psz_rbuf[16] = {0};
  13. if ( socketpair( AF_UNIX, SOCK_STREAM, 0, i_fd_arr ) < 0 )
  14. {
  15. perror( "socketpair" );
  16. return -1;
  17. }
  18. if ( ( i_pid = fork() ) < 0 )
  19. {
  20. perror( "fork" );
  21. return -1;
  22. }
  23. else if ( 0 == i_pid )
  24. {
  25. //child
  26. close( i_fd_arr[0] );
  27. if ( write( i_fd_arr[1], psz_wbuf, strlen( psz_wbuf ) ) < 0 )
  28. {
  29. perror( "write" );
  30. exit( -1 );
  31. }
  32. memset( psz_rbuf, 0, sizeof( psz_rbuf ) );
  33. if ( read( i_fd_arr[1], psz_rbuf, 16 ) < 0 )
  34. {
  35. perror( "read" );
  36. exit( -1 );
  37. }
  38. printf( "child read: %s\n", psz_rbuf );
  39. }
  40. else
  41. {
  42. //parent
  43. close( i_fd_arr[1] );
  44. if ( read( i_fd_arr[0], psz_rbuf, 16 ) < 0 )
  45. {
  46. perror( "read" );
  47. exit( -1 );
  48. }
  49. printf( "parent read: %s\n", psz_rbuf );
  50. memset( psz_wbuf, 0, sizeof( psz_wbuf ) );
  51. strncpy( psz_wbuf, "9876543210", sizeof( psz_wbuf ) - 1 );
  52. if ( write( i_fd_arr[0], psz_wbuf, strlen( psz_wbuf ) ) < 0 )
  53. {
  54. perror( "write" );
  55. exit( -1 );
  56. }
  57. }
  58. return 0;
  59. }

执行结果:

yan@yan-vm:~$ ./a.out
parent read: 0123456789
child read: 9876543210

2.命名UNIX域套接字

虽然socketpair函数创建相互连接的一对套接字,但是每一个套接字都没有名字。这意味着无关进程不能使用它们

我们可以命名unix域套接字,并可将其用于告示服务。但是要注意的是,UNXI与套接字使用的地址不同于因特网域套接字。

unix域套接字使用的地址通常是文件系统中一个文件路径(套接口文件:APUE中的4.3节文件类型,是以s开头的),这些文件是特殊的文件,只能作为域套接字通信,不能读写:


是以s开头的文件。


UNIX域套接字的地址由sockaddr_un结构表示。

在linux2.4.22中,sockaddr_un结构按下列形式定义在有文件<sys/un.h>中。

struct sockaddr_un{

sa_family_t sun_family; //AF_UNIX

char sun_path[108]; //pathname

};

sun_path成员包含一路经名,当我们将一个地址绑定至UNIX域套接字时,系统用该路经名创建一类型为S_IFSOCK文件。

该文件仅用于向客户进程告知套接字名字。该文件不能打开,也不能由应用程序用于通信。

如果当我们试图绑定地址时,该文件已经存在,那么bind请求失败。当关闭套接字时,并不自动删除该文件,所以我们必须确保在应用程序终止前,对该文件执行解除链接操作。

服务器进程可以使用标准bind、listen和accept函数,为客户进程安排一个唯一UNIX域连接。客户进程使用connect与服务器进程连接;服务器进程接受了connect请求后,在服务器进程和客户进程之间就存在了唯一连接。这种风格和因特网套接字的操作很像。

TCP示例:

server.c

  1. #include <stdio.h>
  2. #include <sys/socket.h>
  3. #include <sys/un.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. int main(void)
  7. {
  8. int i_listenfd = 0, i_clientfd = 0;
  9. struct sockaddr_un addr_server, addr_client;
  10. char psz_path[32] = "./server_unixsocket_file";
  11. int i_caddr_len = sizeof(struct sockaddr_un);
  12. int i_saddr_len = sizeof(struct sockaddr_un);
  13. char psz_rbuf[32] = {0};
  14. char psz_wbuf[32] = "i am server.";
  15. int i_readlen = 0;
  16. //create a UNIX domain stream socket
  17. if ( ( i_listenfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ) < 0 )
  18. {
  19. perror( "socket" );
  20. return -1;
  21. }
  22. //in case it already exists
  23. unlink( psz_path );
  24. //fill in socket address structure
  25. memset( &addr_server, 0, sizeof( addr_server ) );
  26. addr_server.sun_family = AF_UNIX;
  27. strncpy( addr_server.sun_path, psz_path, sizeof( addr_server.sun_path ) - 1 );
  28. //bind the name to the descriptor
  29. if ( bind( i_listenfd, ( struct sockaddr * )&addr_server, i_saddr_len ) < 0 )
  30. {
  31. perror( "bind" );
  32. return -1;
  33. }
  34. if ( listen( i_listenfd, 10 ) < 0 )
  35. {
  36. perror( "listen" );
  37. return -1;
  38. }
  39. while(1)
  40. {
  41. if ( ( i_clientfd = accept( i_listenfd, ( struct sockaddr * )&addr_client,
  42. ( socklen_t * )&i_caddr_len ) ) < 0 )
  43. {
  44. perror("accept");
  45. return -1;
  46. }
  47. printf( "client is: %s\n", addr_client.sun_path );
  48. if ( ( i_readlen = read( i_clientfd, psz_rbuf, sizeof( psz_rbuf ) - 1 ) ) < 0 )
  49. {
  50. perror( "read" );
  51. return -1;
  52. }
  53. psz_rbuf[i_readlen] = '\0';
  54. printf( "receive msg:%s\n", psz_rbuf );
  55. if ( write( i_clientfd, psz_wbuf, strlen( psz_wbuf ) + 1 ) < 0 )
  56. {
  57. perror("write");
  58. return -1;
  59. }
  60. }
  61. unlink( psz_path );
  62. return 0;
  63. }

client.c
  1. #include <stdio.h>
  2. #include <sys/un.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. int main(void)
  7. {
  8. int i_fd = 0;
  9. struct sockaddr_un addr;
  10. char psz_path[32] = "./client_unixsocket_file";
  11. char serverpath[32] = "./server_unixsocket_file";
  12. int i_addr_len = sizeof( struct sockaddr_un );
  13. char psz_wbuf[32] = "i am client.";
  14. char psz_rbuf[32] = {0};
  15. int i_readlen = 0;
  16. if ( ( i_fd = socket( AF_UNIX, SOCK_STREAM, 0 ) ) < 0 )
  17. {
  18. perror("socket");
  19. return -1;
  20. }
  21. memset( &addr, 0, sizeof( addr ) );
  22. addr.sun_family = AF_UNIX;
  23. strncpy( addr.sun_path, psz_path, sizeof( addr.sun_path ) - 1 );
  24. unlink( psz_path );
  25. if ( bind( i_fd, ( struct sockaddr * )&addr, i_addr_len ) < 0 )
  26. {
  27. perror("bind");
  28. return -1;
  29. }
  30. //fill socket adress structure with server's address
  31. memset( &addr, 0, sizeof( addr ) );
  32. addr.sun_family = AF_UNIX;
  33. strncpy( addr.sun_path, serverpath, sizeof( addr.sun_path ) - 1 );
  34. if ( connect( i_fd, ( struct sockaddr * )&addr, i_addr_len ) < 0 )
  35. {
  36. perror("connect");
  37. return -1;
  38. }
  39. if ( write( i_fd, psz_wbuf, strlen( psz_wbuf ) + 1 ) < 0 )
  40. {
  41. perror( "write" );
  42. return -1;
  43. }
  44. if ( ( i_readlen = read( i_fd, psz_rbuf, sizeof( psz_rbuf ) - 1 ) ) < 0 )
  45. {
  46. perror("write");
  47. return -1;
  48. }
  49. psz_rbuf[i_readlen] = '\0';
  50. printf( "receive msg:%s\n", psz_rbuf );
  51. unlink( psz_path );
  52. return -1;
  53. }

客户端可以不用bind,那么就不用为客户端设置路径。不设路径,那么例子中服务器输出客户端为空。


先运行server,再运行client。

运行结果:

服务端:

yan@yan-vm:~/apue$ ./unixserver
client is: ./client_unixsocket_file
receive msg:i am client.

客户端:

yan@yan-vm:~/apue$ ./unixclient
receive msg:i am server.


UDP示例:

server

  1. #include <stdio.h>
  2. #include <sys/socket.h>
  3. #include <sys/un.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. int main(void)
  7. {
  8. int i_listenfd = 0/*, i_clientfd = 0*/;
  9. struct sockaddr_un addr_server, addr_client;
  10. char psz_path[32] = "./server_unixsocket_file";
  11. int i_caddr_len = sizeof(struct sockaddr_un);
  12. int i_saddr_len = 0;
  13. char psz_rbuf[32] = {0};
  14. char psz_wbuf[32] = "i am server.";
  15. int i_readlen = 0;
  16. //create a UNIX domain stream socket
  17. if ( ( i_listenfd = socket( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 )
  18. {
  19. perror( "socket" );
  20. return -1;
  21. }
  22. //in case it already exists
  23. unlink( psz_path );
  24. //fill in socket address structure
  25. memset( &addr_server, 0, sizeof( addr_server ) );
  26. addr_server.sun_family = AF_UNIX;
  27. strncpy( addr_server.sun_path, psz_path, sizeof( addr_server.sun_path ) - 1 );
  28. //bind the name to the descriptor
  29. i_saddr_len = strlen( addr_server.sun_path ) + sizeof( addr_server.sun_family );
  30. if ( bind( i_listenfd, ( struct sockaddr * )&addr_server, i_saddr_len ) < 0 )
  31. {
  32. perror( "bind" );
  33. return -1;
  34. }
  35. while(1)
  36. {
  37. i_readlen = recvfrom( i_listenfd, psz_rbuf, sizeof( psz_rbuf ) - 1, 0,
  38. ( struct sockaddr * )&addr_client, ( socklen_t *)&i_caddr_len );
  39. if ( i_readlen < 0 )
  40. {
  41. perror( "read" );
  42. return -1;
  43. }
  44. printf( "client is: %s\n", addr_client.sun_path );
  45. psz_rbuf[i_readlen] = '\0';
  46. printf( "receive msg:%s\n", psz_rbuf );
  47. if ( sendto( i_listenfd, psz_wbuf, strlen( psz_wbuf ) + 1, 0,
  48. ( struct sockaddr * )&addr_client, i_caddr_len ) < 0 )
  49. {
  50. perror( "write" );
  51. return -1;
  52. }
  53. }
  54. unlink( psz_path );
  55. return 0;
  56. }


client

  1. #include <stdio.h>
  2. #include <sys/un.h>
  3. #include <sys/socket.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. int main(void)
  7. {
  8. int i_fd = 0;
  9. struct sockaddr_un addr;
  10. char psz_clientpath[32] = "./client_unixsocket_file";
  11. char psz_serverpath[32] = "./server_unixsocket_file";
  12. int i_addr_len = 0;
  13. char psz_wbuf[32] = "i am client.";
  14. char psz_rbuf[32] = {0};
  15. int i_readlen = 0;
  16. if ( ( i_fd = socket( AF_UNIX, SOCK_DGRAM, 0 ) ) < 0 )
  17. {
  18. perror("socket");
  19. return -1;
  20. }
  21. memset( &addr, 0, sizeof( addr ) );
  22. addr.sun_family = AF_UNIX;
  23. strncpy( addr.sun_path, psz_clientpath, sizeof( addr.sun_path ) - 1 );
  24. unlink( psz_clientpath );
  25. i_addr_len = strlen( addr.sun_path ) + sizeof( addr.sun_family );
  26. if ( bind( i_fd, ( struct sockaddr * )&addr, i_addr_len ) < 0 )
  27. {
  28. perror("bind");
  29. return -1;
  30. }
  31. //fill socket adress structure with server's address
  32. memset( &addr, 0, sizeof( addr ) );
  33. addr.sun_family = AF_UNIX;
  34. strncpy( addr.sun_path, psz_serverpath, sizeof( addr.sun_path ) - 1 );
  35. i_addr_len = strlen( addr.sun_path ) + sizeof( addr.sun_family );
  36. if ( sendto( i_fd, psz_wbuf, strlen( psz_wbuf ) + 1, 0,
  37. ( struct sockaddr * )&addr, i_addr_len ) < 0 )
  38. {
  39. perror( "write" );
  40. return -1;
  41. }
  42. if ( ( i_readlen = recvfrom( i_fd, psz_rbuf, sizeof( psz_rbuf ) - 1, 0,
  43. ( struct sockaddr * )&addr, ( socklen_t * )&i_addr_len ) ) < 0 )
  44. {
  45. perror("write");
  46. return -1;
  47. }
  48. psz_rbuf[i_readlen] = '\0';
  49. printf( "receive msg:%s\n", psz_rbuf );
  50. unlink( psz_clientpath );
  51. return -1;
  52. }

经测试,在UDP中,如果客户端不绑定一个地址,那么只能从客户端往服务器端发送消息,不能从服务端发送消息给客户端,会报如下错误:

write: Transport endpoint is not connected

如果出现如下错误:Invalid argument(22),那么仔细检查参数,最好是每个参数的值都跟示例中一样。


http://www.th7.cn/system/lin/201402/50519.shtml

http://blog.csdn.net/todd911/article/details/20285711

http://jishublog.iteye.com/blog/1945230


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/158777
推荐阅读
  

闽ICP备14008679号