Chinaunix首页 | 论坛 | 博客
  • 博客访问: 417019
  • 博文数量: 48
  • 博客积分: 1032
  • 博客等级: 上士
  • 技术积分: 1256
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-19 13:24
文章分类

全部博文(48)

文章存档

2014年(3)

2013年(23)

2012年(22)

分类: LINUX

2012-10-22 20:14:25

    相信做过通信的人对于套接字一点都不陌生,套接字通信主要用于网络通信,不同计算机之间进行通信。其具有相当强大的功能,套接字通信说到底还是两个进程之间的通信,很多人传统的观念认为使用套接字,TCP/UDP就涉及到网络,涉及到不同的计算机之间,但是套接字不止这种使用方式。它同样可以作为IPC通信完成本机之间两个进程的通信。实现这种方法需要回环网卡的支持,(也就是lo网卡127.0.0.1)。
   在实现通信之前,需要知道程序设计的流程:(传统的经典流程图如下):


套接字:
套接字是计算机操作系统为应用于TCP/IP协议交互提供的一种通用接口。套接字代表了通信的端点。也就是说是数据通过TCP/IP协议在电缆上传输后进入目的主机的入口。
套接字作为通信端点的抽象,与应用程序要使用文件描述符访问文件一样,访问套接字也需要使用套接字描述符。


  1. #include <sys/socket.h>
  2. int socket(int domain, int type, int protocol);
  3. 成功返回文件(套接字)描述符,若出错则返回-1;



参数说明:
1、int domain :确定通信的特性,表示各个域的常数都是以AF_开头,表示地址族(address family):
AF_INET         IPv4因特网域 
AF_INET6        IPv6因特网域
AF_UNIX          UNIX域
AF_UNSPEC        未指定
2、type:确定套接字的类型其有下列4中类型:
SOCK_DGRAM 长度固定的、无连接的不可靠的报文传输(基于UDP)
SOCK_RAW IP协议的数据报接口(原始套接字)
SOCK_SEQPACKET 长度固定、有序、可靠的面向连接报文传递
SOCK_STREAM 有序、可靠、双向的面向连接字节流(TCP)
3、protocol通常为零,表示按给定的域和套接字类型选择默认协议。AF_INET中SOCK_STREAM的默认协议时TCP,SOCK_DGRAM默认的协议时UDP。

在建立连接之前,还需要做一些准备工作;字节序和地址格式结构填充。
运行在同一台电脑上的进程相互通信时,一般不用考虑字节的顺序,字节序是一个处理器架构的特性,用于指示像整数这样的大数据类型的内部字节顺序。
大端(big-endian)字节序:最大字节地址对应于数字最低有效字节(LSB)上。小端(little-endian)字节序则相反。



处理器架构 字节序
Intel Pentium 小端
PowerPC 大端
Sun SPARC 大端
测试处理器是什么字节序:
union 
{
int number;
char s;
}test;

int main(void)
{
test.number=0x01000010;
if(test.s == 0x01)
printf("big-endian\n");
else
printf("little-endian\n");
return 0;
}
网络传输的数据顺序一定要是统一的,网络字节序采用大端存储(big-endian)字节序。
#include  有的老版本的定义在#include

uint32_t htonl(uint32_t,hostint32);  
返回值:以网络字节序表示的32位整形数;
uint16_t htons(uint16_t,hostint16);  
返回值:以网络字节序表示的16位整形数;

uint32_t ntohl(uint32_t,hostint32);  
返回值:以主机字节序表示的32位整形数;
uint16_t ntohs(uint16_t,hostint16);  
返回值:以主机字节序表示的16位整形数;

h:表示host(主机)字节序; l:表示long长整形数;
n:表示net(网络)字节序; s:表示short整形数;

地址格式:涉及到了两个结构体
#include

struct sockadd_in {
  sa_family_t   sin_family; //协议族
  in_port_t   sin_port; //端口号
  struct in_addr  sin_addr; //IP4地址
};
struct in_addr {
  in_addr_t s_addr; //IP4地址
}

点击(此处)折叠或打开

  1. struct sockaddr_in;
  2. bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
  3. server_addr.sin_family=AF_INET; // Internet
  4. server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
  5. //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
  6. server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
服务器端程序设计:

点击(此处)折叠或打开

  1. #include <sys/socket.h>

  2. int bind(int sockfd, const struct sockaddr *addr, socklen_t len);

  3. 返回值:成功返回0,失败返回-1;
注意:
1、在进程运行的机器上,指定的地址必须有效,不能指定一个其他机器的地址;
2、地址必须和创建套接字时的地址族所支持的格式相匹配;
3、端口号必须比小于1024,除非是超级用户;
4、一般只有套接字端点能够与地址绑定;
    对于因特网域,如果指定IP地址为INADDR_ANY,套接字端点可以被绑定到所有的系统网络接口。也就是说可以收到这个系统所安装的所有网卡的数据包。

监听listen():

点击(此处)折叠或打开

  1. #include <sys/socket.h>

  2. int listen(int sockfd, int backlog);

  3. 返回值:成功为0,失败为-1;
参数:backlog提供了一个提示,用于表示该进程所要入列的连接请求数量。一旦队列满,系统会拒绝多余的连接请求,一旦系统调用了listen(),套接字就能接收连接请求。使用函数access()获得连接请求并建立连接。


点击(此处)折叠或打开

  1. #include <sys/socket.h>

  2. int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);

  3. 返回值:成功返回文件描述,失败返回-1;
   函数accept所返回的文件描述符是套接字描述符,该描述符连接到调用connect的客户端。这个新的套接字描述符和原始套接字(sockfd)具有相同的套接字类型和地址族。传给accept的原始套接字没有关联到这个链接,而是继续保持可用状态并接受其他链接请求。

 到这里服务器程序链接就建立好了,接下来就可以调用read(),write()函数进行读写操作;

客户端相关函数:


点击(此处)折叠或打开

  1. #include <sys/socket.h>

  2. int connect(int sockfd, const struct *addr, socklen_t len);

  3. 返回值:成功返回0,错误返回-1;
参考程序:先运行server.c程序,再运行client.c程序,连接成功server将打印出client的ip地址。(./client 127.0.0.1)

服务器端程序:

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <netdb.h>
  6. #include <sys/types.h>
  7. #include <netinet/in.h>
  8. #include <sys/socket.h>

  9. #define portnumber 3333

  10. int main(int argc, char *argv[])
  11. {
  12.     int sockfd,new_fd;
  13.     struct sockaddr_in server_addr;
  14.     struct sockaddr_in client_addr;
  15.     int sin_size;
  16.     int nbytes;
  17.     char buffer[1024];
  18.     

  19.     /* 服务器端开始建立sockfd描述符 */
  20.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP
  21.     {
  22.         fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
  23.         exit(1);
  24.     }

  25.     /* 服务器端填充 sockaddr结构 */
  26.     bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
  27.     server_addr.sin_family=AF_INET; // Internet
  28.     server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
  29.     //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
  30.     server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
  31.     
  32.     /* 捆绑sockfd描述符到IP地址 */
  33.     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
  34.     {
  35.         fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
  36.         exit(1);
  37.     }

  38.     /* 设置允许连接的最大客户端数 */
  39.     if(listen(sockfd,5)==-1)
  40.     {
  41.         fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
  42.         exit(1);
  43.     }

  44.     while(1)
  45.     {
  46.         /* 服务器阻塞,直到客户程序建立连接 */
  47.         sin_size=sizeof(struct sockaddr_in);
  48.         if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
  49.         {
  50.             fprintf(stderr,"Accept error:%s\n\a",strerror(errno));
  51.             exit(1);
  52.         }
  53.         fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串
  54.         
  55.         if((nbytes=read(new_fd,buffer,1024))==-1)
  56.         {
  57.             fprintf(stderr,"Read Error:%s\n",strerror(errno));
  58.             exit(1);
  59.         }         
  60.         buffer[nbytes]='\0';
  61.         printf("Server received %s\n",buffer);
  62.         
  63.         /* 这个通讯已经结束 */
  64.         close(new_fd);
  65.         /* 循环下一个 */
  66.     }

  67.     /* 结束通讯 */
  68.     close(sockfd);
  69.     exit(0);
  70. }
客户端程序:

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <netdb.h>
  6. #include <sys/types.h>
  7. #include <netinet/in.h>
  8. #include <sys/socket.h>

  9. #define portnumber 3333

  10. int main(int argc, char *argv[])
  11. {
  12.     int sockfd;
  13.     char buffer[1024];
  14.     struct sockaddr_in server_addr;
  15.     struct hostent *host;

  16.         /* 使用hostname查询host 名字 */
  17.     if(argc!=2)
  18.     {
  19.         fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
  20.         exit(1);
  21.     }

  22.     if((host=gethostbyname(argv[1]))==NULL)
  23.     {
  24.         fprintf(stderr,"Gethostname error\n");
  25.         exit(1);
  26.     }

  27.     /* 客户程序开始建立 sockfd描述符 */
  28.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP
  29.     {
  30.         fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
  31.         exit(1);
  32.     }

  33.     /* 客户程序填充服务端的资料 */
  34.     bzero(&server_addr,sizeof(server_addr)); // 初始化,置0
  35.     server_addr.sin_family=AF_INET; // IPV4
  36.     server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
  37.     server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
  38.     
  39.     /* 客户程序发起连接请求 */
  40.     if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
  41.     {
  42.         fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
  43.         exit(1);
  44.     }

  45.     /* 连接成功了 */
  46.     printf("Please input char:\n");
  47.     
  48.     /* 发送数据 */
  49.     fgets(buffer,1024,stdin);
  50.     write(sockfd,buffer,strlen(buffer));

  51.     /* 结束通讯 */
  52.     close(sockfd);
  53.     exit(0);
  54. }
使用套接字进行数据处理有两种基本模式:同步和异步。
  同步模式:
  同步模式的特点是在通过Socket进行连接、接收、发送数据时,客户机和服务器在接收到对方响应前会处于阻塞状态,即一直等到收到对方请求进才继续执行下面的语句。可见,同步模式只适用于数据处理不太多的场合。当程序执行的任务很多时,长时间的等待可能会让用户无法忍受。
  异步模式:
  异步模式的特点是在通过Socket进行连接、接收、发送操作时,客户机或服务器不会处于阻塞方式,而是利用callback机制进行连接、接收、发送处理,这样就可以在调用发送或接收的方法后直接返回,并继续执行下面的程序。可见,异步套接字特别适用于进行大量数据处理的场合。
   当遇到在有连接请求的时候就行连接,改变一些状态量,没有连接请求来的时候希望程序跳过accept继续往下执行就需要用到异步模式,实现方法就是改变最初套接字的模式;

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <fcntl.h>
  3. int flag;
  4. if((flag = fcntl(sockfd,F_GETFL,0) == -1)
  5. flag = 0;
  6. fcntl(sockfd,F_SETFL,flag | O_NONBLOCK);
具体关于fcntl操作文件描述的函数可以参考博客:











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

上一篇:IPC---管道

下一篇:fcntl函数

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