socket 编程 TCP 实现简单聊天功能

各个主要函数的功能:

1、socket

int socket(int domain, int type,int protocol)     
 
    domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX  和  AF_INET  等). 
AF_UNIX 只能够用于单一的 Unix 系统进程间通信,而 AF_INET 是针对 Internet 的,因而可以允
许在远程  主机之间通信(当我们  man  socket 时发现  domain 可选项是  PF_*而不是 AF_*,因
为 glibc 是 posix 的实现  所以用 PF 代替了 AF,不过我们都可以使用的).     
 
    type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等)  SOCK_STREAM
表明我们用的是 TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流.  SOCK_DGRAM 
表明我们用的是 UDP 协议,这样只会提供定长的,不可靠,无连接的通信.     
 
    protocol:由于我们指定了  type,所以这个地方我们一般只要用  0  来代替就可以了 
socket 为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看 errno 可知道出错
的详细情况.     
 
 
2、bind
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)     
 
    sockfd:是由 socket 调用返回的文件描述符.     
 
    addrlen:是 sockaddr 结构的长度.     
 
    my_addr:是一个指向 sockaddr 的指针.  在中有  sockaddr 的定义     
 
                struct sockaddr{   
                                unisgned short    as_family;   
                                char                        sa_data[14];   
                                };   
 
    不过由于系统的兼容性, 我们一般不用这个头文件, 而使用另外一个结构(struct sockaddr_in)  来代替.在中有 sockaddr_in 的定义     
                struct sockaddr_in{   
                                unsigned short                    sin_family;             
                                unsigned short int            sin_port;   
                                struct in_addr                    sin_addr;   
                                unsigned char                      sin_zero[8];   
 
    我们主要使用 Internet 所以 sin_family 一般为 AF_INET,sin_addr 设置为 INADDR_ANY
表示可以和任何的主机通信,sin_port  是我们要监听的端口号.sin_zero[8]是用来填充的.  bind
将本地的端口同 socket 返回的文件描述符捆绑在一起.成功是返回 0,失败的情况和 socket 一
样     
 

3、listen

int listen(int sockfd,int backlog)     
 
sockfd:是 bind 后的文件描述符.     
 
backlog:设置请求排队的最大长度.当有多个客户端程序和服务端相连时,  使用这个表示
可以介绍的排队长度. listen 函数将 bind 的文件描述符变为监听套接字.返回的情况和 bind 一
样.     
 
 
4、 accept     
int accept(int sockfd, struct sockaddr *addr,int *addrlen)     
 
sockfd:是 listen 后的文件描述符.     
 
addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了.  bind,listen
和 accept 是服务器端用的函数,accept 调用时,服务器端的程序会一直阻塞到有一个客户程序
发出了连接. accept 成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描
述符写信息了.  失败时返回-1     
 
 
5、 connect     
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)     
 sockfd:socket 返回的文件描述符.     
 
serv_addr:储存了服务器端的连接信息.其中 sin_add 是服务端的地址     
 
addrlen:serv_addr 的长度     
 
connect 函数是客户端用来同服务端连接的.成功时返回 0,sockfd 是同服务端通讯的文件
描述符  失败时返回-1.     

 

6、

gethostbyname 和 gethostbyaddr 都涉及到一个 hostent 的结构体,如下所示: 
 
Struct hostent{ 
      char *h_name;/*正式主机名*/ 
      char **h_aliases;/*主机别名*/ 
      int h_addrtype;/*地址类型*/ 
      int h_length;/*地址长度*/ 
      char **h_addr_list;/*指向 IPv4或 IPv6的地址指针数组*/ 

所需头文件  #include <netdb.h> 
函数原型   Struct hostent *gethostbyname(const char *hostname) 
函数传入值   Hostname:主机名 
函数返回值

成功:  hostent 类型指针 
  出错:−1 

服务端源码:

 

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include <sys/types.h>  
  5. #include <sys/time.h>  
  6. #include <unistd.h>   
  7. #include <string.h>   
  8. #include <netdb.h>   
  9. #include <sys/types.h>   
  10. #include <netinet/in.h>   
  11. #include <sys/socket.h>  
  12.   
  13. int main(int argc, char *argv[])  
  14. {  
  15.     int sockfd,new_fd;   
  16.     int sockaddr_size;  
  17.     int ret;  
  18.     int len;  
  19.     struct sockaddr_in server_addr;   //结构体sockaddr_in 和 aockaddr 是兼容的  
  20.     struct sockaddr_in client_addr;     
  21.     int sin_size,portnumber;     
  22.     char buf[1024];  
  23.   
  24.     fd_set rfds;  
  25.     struct timeval tv;  
  26.     int maxfd;  
  27.     FD_ZERO(&rfds);  
  28.     FD_SET(STDIN_FILENO, &rfds);  
  29.   
  30.   
  31.     if((portnumber = atoi(argv[1]))<0)  
  32.     {  
  33.         printf("port number error, will exit\n");  
  34.         exit(1);  
  35.     }  
  36.     /*  服务器端开始建立 socket 描述符  */       
  37.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)    //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议     
  38.     {                                                       //而 SOCK_DGRAM 表明我们用的是 UDP 协议  
  39.         printf("socket, will exit\n");  
  40.         close(sockfd);  
  41.         exit(1);    
  42.     }  
  43.     /*  服务器端填充  sockaddr 结构    */       
  44.     bzero(&server_addr,sizeof(struct sockaddr_in));     
  45.     server_addr.sin_family=AF_INET;     
  46.     server_addr.sin_addr.s_addr=htonl(INADDR_ANY);   //标识可以和任何主机通信  
  47.     server_addr.sin_port=htons(portnumber);    
  48.   
  49.     /*  捆绑 sockfd 描述符    */       
  50.     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)     
  51.     {     
  52.         printf("bind error, will exit\n");     
  53.         close(sockfd);  
  54.         exit(1);     
  55.     }  
  56.     /*  监听 sockfd 描述符    */     
  57.     if(listen(sockfd,5)==-1)  //设置客户端请求排队的最大长度,允许最大5个   
  58.     {        
  59.         printf("listen, will exit\n");   
  60.         close(sockfd);        
  61.         exit(1);     
  62.     }  
  63.   
  64.   
  65.     while(1)  
  66.     {  
  67.          /*  服务器阻塞,直到客户程序建立连接    */     
  68.         printf("Will go to talk, wait connecting...\n");  
  69.         sockaddr_size = sizeof(struct sockaddr);  
  70.         //int accept(int sockfd, struct sockaddr *addr,int *addrlen)      
  71.         if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sockaddr_size))==-1)    
  72.         {  
  73.             printf("accept error, will exit\n");  
  74.             close(sockfd);  
  75.             exit(1);  
  76.         }  
  77.         else  
  78.         {  
  79.             printf("server: get connection from %s, port %d, socket %d\n",  
  80.                    (char *)inet_ntoa(client_addr.sin_addr),  
  81.                    ntohs(client_addr.sin_port),  
  82.                    new_fd);  
  83.         }  
  84.         //进行会话  
  85.         while(1)  
  86.         {  
  87.   
  88.             FD_ZERO(&rfds);  
  89.             FD_SET(STDIN_FILENO, &rfds);  
  90.             FD_SET(new_fd, &rfds);  
  91.             //maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO;  
  92.             //maxfd = maxfd + 1;  
  93.             maxfd = new_fd + 1;  
  94.               
  95.             tv.tv_sec = 1;  
  96.             tv.tv_usec = 0;//设置等待时间  
  97.             ret = select(maxfd, &rfds, NULL, NULL, &tv);  
  98.             if(ret<0)  
  99.             {  
  100.                 printf("select error, process will eixt\n");  
  101.                 exit(0);  
  102.             }  
  103.             else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据  
  104.             {  
  105.                     bzero(buf,1024);  
  106.                     //printf("Server    :");  
  107.                     fgets(buf, 1024, stdin);  
  108.                     if (!strncasecmp(buf, "quit", 4))   
  109.                     {  
  110.                         printf("current talk exit\n");  
  111.                         FD_CLR(new_fd, &rfds);   
  112.                         close(new_fd);  
  113.                         break;  
  114.                     }  
  115.                     //printf("Server    :%s\n",buf);  
  116.                     //  int recv(int sockfd,void *buf,int len,int flags)     
  117.                     //  int send(int sockfd,void *buf,int len,int flags)     
  118.                     //最后一个参数可以是0,也可以是  
  119. /*                    _______________________________________________________________    
  120. |    MSG_DONTROUTE                |    不查找路由表                                                  |    
  121. |    MSG_OOB                            |    接受或者发送带外数据                                    |    
  122. |    MSG_PEEK                          |    查看数据,并不从系统缓冲区移走数据            |    
  123. |    MSG_WAITALL                    |    等待所有数据                                                  |    
  124. |--------------------------------------------------------------|  
  125. */  
  126.                     len = send(new_fd,buf,strlen(buf)-1,0);  
  127.                     if(len < 0)  
  128.                     {  
  129.                         printf("send fail\n");  
  130.                     }  
  131.                       
  132.                       
  133.                   
  134.             }  
  135.             else if(FD_ISSET(new_fd, &rfds))  
  136.             {  
  137.               
  138.                 bzero(buf,1024);  
  139.                 len = recv(new_fd,buf,strlen(buf)-1,0);  
  140.                 if(len < 0)  
  141.                 {  
  142.                     printf("receive fail\n");  
  143.                 }  
  144.                 if(len == 0)//当客户端终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0  
  145.                 {  
  146.                     printf("Client has already exit, will exit\n");  
  147.                     FD_CLR(new_fd, &rfds);   
  148.                     close(new_fd);  
  149.                     break;  
  150.                   
  151.                 }  
  152.                 if (!strncasecmp(buf, "quit", 4))   
  153.                 {  
  154.                     printf("current talk exit\n");  
  155.                     FD_CLR(new_fd, &rfds);   
  156.                     close(new_fd);  
  157.                     break;  
  158.                 }  
  159.                 buf[len+1]='\0';  
  160.                 printf("        Client  :%s\n",buf);  
  161.               
  162.             }  
  163.           
  164.         }  
  165.       
  166.     }  
  167.       
  168. }  

客户端:

 

 

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <errno.h>  
  4. #include <sys/types.h>  
  5. #include <sys/time.h>  
  6. #include <unistd.h>   
  7. #include <string.h>   
  8. #include <netdb.h>   
  9. #include <sys/types.h>   
  10. #include <netinet/in.h>   
  11. #include <sys/socket.h>  
  12.   
  13. int main(int argc, char *argv[])  
  14. {  
  15.     int sockfd,new_fd;   
  16.     int sockaddr_size;  
  17.     int ret;  
  18.     int len;  
  19.     struct sockaddr_in server_addr;   //结构体sockaddr_in 和 aockaddr 是兼容的  
  20.      struct hostent *host;  
  21.     int sin_size,portnumber;     
  22.     char buf[1024];  
  23.   
  24.     fd_set rfds;  
  25.     struct timeval tv;  
  26.     int maxfd;  
  27.     FD_ZERO(&rfds);  
  28.     FD_SET(STDIN_FILENO, &rfds);  
  29.   
  30.      if((host=gethostbyname(argv[1])) == 0)  
  31.     {  
  32.         printf("server ip %s error\n",argv[1]);  
  33.         exit(1);  
  34.     }  
  35.       
  36.     if((portnumber = atoi(argv[2]))<0)  
  37.     {  
  38.         printf("port number error, will exit\n");  
  39.         exit(1);  
  40.     }  
  41.     /*  客户程序开始建立  sockfd 描述符    */         
  42.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)    //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议     
  43.     {                                                       //而 SOCK_DGRAM 表明我们用的是 UDP 协议  
  44.         printf("socket, will exit\n");  
  45.         exit(1);    
  46.     }  
  47.     /*  客户程序填充服务端的资料              */        
  48.     bzero(&server_addr,sizeof(struct sockaddr_in));     
  49.     server_addr.sin_family=AF_INET;     
  50.     server_addr.sin_port=htons(portnumber);    
  51.     server_addr.sin_addr=*((struct in_addr *)host->h_addr);   
  52.   
  53.     /*  客户程序发起连接请求                  */       
  54.     if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)     
  55.     {  
  56.         printf("connect will exit\n");  
  57.         exit(1);    
  58.     }  
  59.     printf("start talk...\n");  
  60.       
  61.     FD_SET(sockfd, &rfds);  
  62.         //进行会话  
  63.         while(1)  
  64.         {  
  65.   
  66.             FD_ZERO(&rfds);  
  67.             FD_SET(STDIN_FILENO, &rfds);  
  68.             FD_SET(sockfd, &rfds);  
  69.               
  70.             //maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO;  
  71.             //maxfd = maxfd + 1;  
  72.             maxfd = sockfd + 1;  
  73.               
  74.             tv.tv_sec = 1;  
  75.             tv.tv_usec = 0;//设置等待时间  
  76.             ret = select(maxfd, &rfds, NULL, NULL, &tv);  
  77.             if(ret<0)  
  78.             {  
  79.                 printf("select error, process will eixt\n");  
  80.                 exit(0);  
  81.             }  
  82.             else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据  
  83.             {  
  84.                     bzero(buf,1024);  
  85.                     //printf("Client    :");  
  86.                     fgets(buf, 1024, stdin);  
  87.                     if (!strncasecmp(buf, "quit", 4))   
  88.                     {  
  89.                         printf("current talk exit\n");  
  90.                         FD_CLR(sockfd, &rfds);   
  91.                         close(sockfd);  
  92.                         break;  
  93.                     }  
  94.                     //printf("Client    :%s\n",buf);  
  95.                     //  int recv(int sockfd,void *buf,int len,int flags)     
  96.                     //  int send(int sockfd,void *buf,int len,int flags)     
  97.                     //最后一个参数可以是0,也可以是  
  98. /*                    _______________________________________________________________    
  99. |    MSG_DONTROUTE                |    不查找路由表                                                  |    
  100. |    MSG_OOB                            |    接受或者发送带外数据                                    |    
  101. |    MSG_PEEK                          |    查看数据,并不从系统缓冲区移走数据            |    
  102. |    MSG_WAITALL                    |    等待所有数据                                                  |    
  103. |--------------------------------------------------------------|  
  104. */  
  105.                     len = send(sockfd,buf,strlen(buf)-1,0);  
  106.                     if(len < 0)  
  107.                     {  
  108.                         printf("send fail\n");  
  109.                     }  
  110.                       
  111.                       
  112.                   
  113.             }  
  114.             else if(FD_ISSET(sockfd, &rfds))  
  115.             {  
  116.               
  117.                 bzero(buf,1024);  
  118.                 len = recv(sockfd,buf,strlen(buf)-1,0);  
  119.                 if(len < 0)  
  120.                 {  
  121.                     printf("receive fail\n");  
  122.                 }  
  123.                 if(len == 0)//当服务器终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0  
  124.                 {  
  125.                     printf("Server has already exit, will exit\n");  
  126.                     FD_CLR(sockfd, &rfds);   
  127.                     close(sockfd);  
  128.                     break;  
  129.                   
  130.                 }  
  131.                 if (!strncasecmp(buf, "quit", 4))   
  132.                 {  
  133.                     printf("current talk exit\n");  
  134.                     FD_CLR(sockfd, &rfds);   
  135.                     close(sockfd);  
  136.                     break;  
  137.                 }  
  138.                 buf[len+1]='\0';  
  139.                 printf("        Server: %s\n",buf);  
  140.               
  141.             }  
  142.           
  143.         }  
  144.       
  145.   
  146.       
  147. }  
posted @ 2017-01-09 13:23  DemiGoo  阅读(299)  评论(0编辑  收藏  举报