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

简介:

转自:http://blog.csdn.net/liujia2100/article/details/9006479

复制代码
各个主要函数的功能:

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

    #include <stdio.h>  
    #include <stdlib.h>  
    #include <errno.h>  
    #include <sys/types.h>  
    #include <sys/time.h>  
    #include <unistd.h>   
    #include <string.h>   
    #include <netdb.h>   
    #include <sys/types.h>   
    #include <netinet/in.h>   
    #include <sys/socket.h>  
      
    int main(int argc, char *argv[])  
    {  
        int sockfd,new_fd;   
        int sockaddr_size;  
        int ret;  
        int len;  
        struct sockaddr_in server_addr;   //结构体sockaddr_in 和 aockaddr 是兼容的  
        struct sockaddr_in client_addr;     
        int sin_size,portnumber;     
        char buf[1024];  
      
        fd_set rfds;  
        struct timeval tv;  
        int maxfd;  
        FD_ZERO(&rfds);  
        FD_SET(STDIN_FILENO, &rfds);  
      
      
        if((portnumber = atoi(argv[1]))<0)  
        {  
            printf("port number error, will exit\n");  
            exit(1);  
        }  
        /*  服务器端开始建立 socket 描述符  */       
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)    //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议     
        {                                                       //而 SOCK_DGRAM 表明我们用的是 UDP 协议  
            printf("socket, will exit\n");  
            close(sockfd);  
            exit(1);    
        }  
        /*  服务器端填充  sockaddr 结构    */       
        bzero(&server_addr,sizeof(struct sockaddr_in));     
        server_addr.sin_family=AF_INET;     
        server_addr.sin_addr.s_addr=htonl(INADDR_ANY);   //标识可以和任何主机通信  
        server_addr.sin_port=htons(portnumber);    
      
        /*  捆绑 sockfd 描述符    */       
        if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)     
        {     
            printf("bind error, will exit\n");     
            close(sockfd);  
            exit(1);     
        }  
        /*  监听 sockfd 描述符    */     
        if(listen(sockfd,5)==-1)  //设置客户端请求排队的最大长度,允许最大5个   
        {        
            printf("listen, will exit\n");   
            close(sockfd);        
            exit(1);     
        }  
      
      
        while(1)  
        {  
             /*  服务器阻塞,直到客户程序建立连接    */     
            printf("Will go to talk, wait connecting...\n");  
            sockaddr_size = sizeof(struct sockaddr);  
            //int accept(int sockfd, struct sockaddr *addr,int *addrlen)      
            if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sockaddr_size))==-1)    
            {  
                printf("accept error, will exit\n");  
                close(sockfd);  
                exit(1);  
            }  
            else  
            {  
                printf("server: get connection from %s, port %d, socket %d\n",  
                       (char *)inet_ntoa(client_addr.sin_addr),  
                       ntohs(client_addr.sin_port),  
                       new_fd);  
            }  
            //进行会话  
            while(1)  
            {  
      
                FD_ZERO(&rfds);  
                FD_SET(STDIN_FILENO, &rfds);  
                FD_SET(new_fd, &rfds);  
                //maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO;  
                //maxfd = maxfd + 1;  
                maxfd = new_fd + 1;  
                  
                tv.tv_sec = 1;  
                tv.tv_usec = 0;//设置等待时间  
                ret = select(maxfd, &rfds, NULL, NULL, &tv);  
                if(ret<0)  
                {  
                    printf("select error, process will eixt\n");  
                    exit(0);  
                }  
                else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据  
                {  
                        bzero(buf,1024);  
                        //printf("Server    :");  
                        fgets(buf, 1024, stdin);  
                        if (!strncasecmp(buf, "quit", 4))   
                        {  
                            printf("current talk exit\n");  
                            FD_CLR(new_fd, &rfds);   
                            close(new_fd);  
                            break;  
                        }  
                        //printf("Server    :%s\n",buf);  
                        //  int recv(int sockfd,void *buf,int len,int flags)     
                        //  int send(int sockfd,void *buf,int len,int flags)     
                        //最后一个参数可以是0,也可以是  
    /*                    _______________________________________________________________    
    |    MSG_DONTROUTE                |    不查找路由表                                                  |    
    |    MSG_OOB                            |    接受或者发送带外数据                                    |    
    |    MSG_PEEK                          |    查看数据,并不从系统缓冲区移走数据            |    
    |    MSG_WAITALL                    |    等待所有数据                                                  |    
    |--------------------------------------------------------------|  
    */  
                        len = send(new_fd,buf,strlen(buf)-1,0);  
                        if(len < 0)  
                        {  
                            printf("send fail\n");  
                        }  
                          
                          
                      
                }  
                else if(FD_ISSET(new_fd, &rfds))  
                {  
                  
                    bzero(buf,1024);  
                    len = recv(new_fd,buf,strlen(buf)-1,0);  
                    if(len < 0)  
                    {  
                        printf("receive fail\n");  
                    }  
                    if(len == 0)//当客户端终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0  
                    {  
                        printf("Client has already exit, will exit\n");  
                        FD_CLR(new_fd, &rfds);   
                        close(new_fd);  
                        break;  
                      
                    }  
                    if (!strncasecmp(buf, "quit", 4))   
                    {  
                        printf("current talk exit\n");  
                        FD_CLR(new_fd, &rfds);   
                        close(new_fd);  
                        break;  
                    }  
                    buf[len+1]='\0';  
                    printf("        Client  :%s\n",buf);  
                  
                }  
              
            }  
          
        }  
          
    }  


客户端:
[cpp] view plain copy

        #include <stdio.h>  
        #include <stdlib.h>  
        #include <errno.h>  
        #include <sys/types.h>  
        #include <sys/time.h>  
        #include <unistd.h>   
        #include <string.h>   
        #include <netdb.h>   
        #include <sys/types.h>   
        #include <netinet/in.h>   
        #include <sys/socket.h>  
          
        int main(int argc, char *argv[])  
        {  
            int sockfd,new_fd;   
            int sockaddr_size;  
            int ret;  
            int len;  
            struct sockaddr_in server_addr;   //结构体sockaddr_in 和 aockaddr 是兼容的  
             struct hostent *host;  
            int sin_size,portnumber;     
            char buf[1024];  
          
            fd_set rfds;  
            struct timeval tv;  
            int maxfd;  
            FD_ZERO(&rfds);  
            FD_SET(STDIN_FILENO, &rfds);  
          
             if((host=gethostbyname(argv[1])) == 0)  
            {  
                printf("server ip %s error\n",argv[1]);  
                exit(1);  
            }  
              
            if((portnumber = atoi(argv[2]))<0)  
            {  
                printf("port number error, will exit\n");  
                exit(1);  
            }  
            /*  客户程序开始建立  sockfd 描述符    */         
            if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)    //AF_INET也可以用PF_INET代替,表明针对錓neternet,可以建立远程连接。SOCK_STREAM表明使用的是TCP协议     
            {                                                       //而 SOCK_DGRAM 表明我们用的是 UDP 协议  
                printf("socket, will exit\n");  
                exit(1);    
            }  
            /*  客户程序填充服务端的资料              */        
            bzero(&server_addr,sizeof(struct sockaddr_in));     
            server_addr.sin_family=AF_INET;     
            server_addr.sin_port=htons(portnumber);    
            server_addr.sin_addr=*((struct in_addr *)host->h_addr);   
          
            /*  客户程序发起连接请求                  */       
            if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)     
            {  
                printf("connect will exit\n");  
                exit(1);    
            }  
            printf("start talk...\n");  
              
            FD_SET(sockfd, &rfds);  
                //进行会话  
                while(1)  
                {  
          
                    FD_ZERO(&rfds);  
                    FD_SET(STDIN_FILENO, &rfds);  
                    FD_SET(sockfd, &rfds);  
                      
                    //maxfd = new_fd > STDIN_FILENO ? newfd :STDIN_FILENO;  
                    //maxfd = maxfd + 1;  
                    maxfd = sockfd + 1;  
                      
                    tv.tv_sec = 1;  
                    tv.tv_usec = 0;//设置等待时间  
                    ret = select(maxfd, &rfds, NULL, NULL, &tv);  
                    if(ret<0)  
                    {  
                        printf("select error, process will eixt\n");  
                        exit(0);  
                    }  
                    else if(FD_ISSET(STDIN_FILENO, &rfds))//测试是否有数据  
                    {  
                            bzero(buf,1024);  
                            //printf("Client    :");  
                            fgets(buf, 1024, stdin);  
                            if (!strncasecmp(buf, "quit", 4))   
                            {  
                                printf("current talk exit\n");  
                                FD_CLR(sockfd, &rfds);   
                                close(sockfd);  
                                break;  
                            }  
                            //printf("Client    :%s\n",buf);  
                            //  int recv(int sockfd,void *buf,int len,int flags)     
                            //  int send(int sockfd,void *buf,int len,int flags)     
                            //最后一个参数可以是0,也可以是  
        /*                    _______________________________________________________________    
        |    MSG_DONTROUTE                |    不查找路由表                                                  |    
        |    MSG_OOB                            |    接受或者发送带外数据                                    |    
        |    MSG_PEEK                          |    查看数据,并不从系统缓冲区移走数据            |    
        |    MSG_WAITALL                    |    等待所有数据                                                  |    
        |--------------------------------------------------------------|  
        */  
                            len = send(sockfd,buf,strlen(buf)-1,0);  
                            if(len < 0)  
                            {  
                                printf("send fail\n");  
                            }  
                              
                              
                          
                    }  
                    else if(FD_ISSET(sockfd, &rfds))  
                    {  
                      
                        bzero(buf,1024);  
                        len = recv(sockfd,buf,strlen(buf)-1,0);  
                        if(len < 0)  
                        {  
                            printf("receive fail\n");  
                        }  
                        if(len == 0)//当服务器终止运行的时候,会不断检测到sockfd的状态变化,但是收到的信息长度为0  
                        {  
                            printf("Server has already exit, will exit\n");  
                            FD_CLR(sockfd, &rfds);   
                            close(sockfd);  
                            break;  
                          
                        }  
                        if (!strncasecmp(buf, "quit", 4))   
                        {  
                            printf("current talk exit\n");  
                            FD_CLR(sockfd, &rfds);   
                            close(sockfd);  
                            break;  
                        }  
                        buf[len+1]='\0';  
                        printf("        Server: %s\n",buf);  
                      
                    }  
                  
                }  
              
          
              
        } 
复制代码










本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7650486.html,如需转载请自行联系原作者

相关文章
|
2月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
147 1
|
2月前
|
网络协议 测试技术 网络安全
Python编程-Socket网络编程
Python编程-Socket网络编程
26 0
|
4月前
|
网络协议 Java
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
这篇文章全面讲解了基于Socket的TCP网络编程,包括Socket基本概念、TCP编程步骤、客户端和服务端的通信过程,并通过具体代码示例展示了客户端与服务端之间的数据通信。同时,还提供了多个案例分析,如客户端发送信息给服务端、客户端发送文件给服务端以及服务端保存文件并返回确认信息给客户端的场景。
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
|
3月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【9月更文挑战第6天】
|
5月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
60 4
|
5月前
|
开发者 Python
Python Socket编程:不只是基础,更有进阶秘籍,让你的网络应用飞起来!
【7月更文挑战第25天】在网络应用蓬勃发展的数字时代,Python凭借其简洁的语法和强大的库支持成为开发高效应用的首选。本文通过实时聊天室案例,介绍了Python Socket编程的基础与进阶技巧,包括服务器与客户端的建立、数据交换等基础篇内容,以及使用多线程和异步IO提升性能的进阶篇。基础示例展示了服务器端监听连接请求、接收转发消息,客户端连接服务器并收发消息的过程。进阶部分讨论了如何利用Python的`threading`模块和`asyncio`库来处理多客户端连接,提高应用的并发处理能力和响应速度。掌握这些技能,能使开发者在网络编程领域更加游刃有余,构建出高性能的应用程序。
37 3
|
5月前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
【7月更文挑战第26天】在网络的数字宇宙中,Python Socket编程是开启网络世界大门的钥匙。本指南将引领你从基础到实战,成为网络世界的建筑师。
67 2
|
5月前
|
网络协议 程序员 视频直播
|
5月前
|
消息中间件 网络协议 网络安全
Python Socket编程:打造你的专属网络通道,基础篇与进阶篇一网打尽!
【7月更文挑战第26天】在网络编程领域,Python以简洁语法和强大库支持成为构建应用的首选。Socket编程为核心,实现计算机间的数据交换。
72 1
|
5月前
|
网络协议 安全 Java
Java中的网络编程:Socket编程详解
Java中的网络编程:Socket编程详解