初探socket(2)

昨天对于那个socket程序进行了小小的修改,使它可以在2个主机之间互相通话,一个很简单的聊天方式。(利用了select)



—————————————————————————————————————————————————————————————————————————————

先上图看看效果吧:







—————————————————————————————————————————————————————————————————————————————

先说一下我的思路:(只是2台主机通信,这个服务器我设置了只能和一台主机通信,与多台有待研究)

服务器端:当accept到一个客户端后获得了客户端的socket  fd在进入while(1)循环,一直用select监听着我的文件描述符,然后检测到底是我输入信息还是接受信息,再去执行具体函数,最后输入quit退出,或者对方输入quit退出。

客户端:跟客户端一样,while(1)循环在connect后面


我先说一下 select这个函数:

    使用select函数的过程一般是:
    先调用宏FD_ZERO将指定的fd_set清零,然后调用宏FD_SET将需要测试的fd加入fd_set,
    接着调用函数select测试fd_set中的所有fd,最后用宏FD_ISSET检查某个fd在函数select调用后,相应位是否仍然为1。

====================================================================================================================================

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
   (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。
   (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
   (3)若再加入fd=2,fd=1,则set变为0001,0011
   (4)执行select(6,&set,0,0,0)阻塞等待
   (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

====================================================================================================================================

我代码中的的运用:

    FD_ZERO(&rfds);
    FD_SET(0,&rfds);
    FD_SET(connfd,&rfds);
  
    ret=select(connfd+1,&rfds,NULL,NULL,NULL);

首先清空rfds,然后将0和connfd加进去,0是stdin标准输入,当我们发送信息的时候就会输入文字在标准输入上(屏幕),然后这个文件描述符里面就有可读内容了select就能监听到,然后调用send函数;connfd就是我们客户端的fd,当它发送信息的时候我们也可以监听到,然后调用recv函数。


这里我想对超时时间timeout说一下,就是select最后一个参数,你可以设置一个超时参数,比如6秒,那么select就会帮你监听这2个文件6秒钟,当有文件可读的时候返回一个大于0的数时候表示有文件可读,再用FD_ISSET这个宏去判断到底是哪些文件可读,在这个程序中就是--到底是向对方发送信息,还是接受对方信息。如果超过6秒,还没有信息,那么返回0,这个时候你可以选择继续往下执行,也可以退出,反正依靠自己功能,不过在这个程序中,我就是继续往下执行。

(我这里设置的是NULL,就是阻塞式的,直到有文件可读,才往下执行。因为程序比较简单,所以这里并没有体现出阻塞和非阻塞的太多区别,设置成0也可以)

if(ret>=0)
    {

    if(FD_ISSET(connfd,&rfds))//当对方有消息的时候往下执行
    {   
        bzero(buff,MAXLINE);           
        n = recv(connfd, buff, MAXLINE, 0);
        buff[n] = '\0'; 
       if(n>0)   
        {    
            printf("recv msg from client: %s\n", buff);   
        }
        else if(n==0)//这里是因为我在客户端那边的操作是,当输入quit的时候就直接退出关闭了套接字,没有执行send函数,所以recv收到的字节数为0
        {
            printf("=================Chat end===============\n");
            break;
        }
    }
    if(FD_ISSET(0,&rfds))//当你想输入消息的时候往下执行
    {
        bzero(buff,MAXLINE);
        fgets(buff,MAXLINE,stdin);
        printf("send mesg to clint.(\"quit\"to end):%s\n",buff);
        if(!strncmp(buff,"quit",4))//先判断我输入的是不是quit,如果是的话,则直接退出了
            break;
       if( send(connfd,buff,strlen(buff),0)<0)
       {
           printf("send msg error:%s(errno:%d)\n",strerror(errno),errno);
           exit(1);
       }
     }
    
	}
	    
	}


—————————————————————————————————————————————————————————————————————————————

OK~以上就是我对代码的理解,下面还是贴出我的代码,然后有时间我再修改一下,实现更多的功能看看。

ps:由于这个程序只是2个主机之间通信。 还想想改成多线程的程序,服务器能同时接受多个服务器传来的消息,明天再上传吧。(mark:continue是结束这一次循环,break是结束整个循环)


服务端:(9月28号又改了一下代码,这个代码的功能是客户端退出后,服务端可以选择不退出,而且服务端在和客户端交互信息的时候可以再连客户端,不过要等上一个客户端退出后信息才会我们才能从屏幕上看到信息)<strncmp可以改成strncasecmp忽略大小写,而且还有一个BUG就是如果你输入quitxx也会退出>

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>

#define MAXLINE 4096

int main(int argc, char** argv)
{
    int    listenfd, connfd;
    struct sockaddr_in     servaddr,c_addr;
    char    buff[MAXLINE];
    int     n;
    int     len;
    fd_set  rfds;
    int ret;
    int max_fd;

    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
	{
    printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
    exit(0);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);

    if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
	{
    printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
    exit(0);
    }

    if( listen(listenfd, 10) == -1)
	{
    printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
    exit(0);
    }

    printf("======waiting for client's request======\n");

	while(1)

 {
    len=sizeof(struct sockaddr_in);
    if( (connfd = accept(listenfd, (struct sockaddr*)&c_addr, &len)) == -1)
    {
        printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
    }
    else 
        printf("=================connect success!================\n");
    printf("the clint port is: %x\n",ntohs(c_addr.sin_port));

    /* ================================================================================= */
    while(1)
    { 
      FD_ZERO(&rfds);
      FD_SET(0,&rfds);
      FD_SET(connfd,&rfds);
      //max_fd=0;
      //if(max_fd<connfd)
      //  max_fd=connfd;//这里是判断最大描述符的,因为我是stdin 默认就是0,所以没必要比较,如果是多个socket的话就需要比较了,因为select第一个参数需要最大描述符   
      ret=select(connfd+1,&rfds,NULL,NULL,NULL);
      if(ret>=0)
     {
       if(FD_ISSET(connfd,&rfds))
      {   
         bzero(buff,MAXLINE);           
         n = recv(connfd, buff, MAXLINE, 0);
         buff[n] = '\0'; 
        if(n>0)   
        {    
            printf("recv msg from client: %s\n", buff);   
        }
        else if(n==0)
        {
            printf("====client quit===== \n do you want to quit?(y)\n");
			break;
		}
      }     
       if(FD_ISSET(0,&rfds))
      {
        bzero(buff,MAXLINE);
        fgets(buff,MAXLINE,stdin);
        printf("send mesg to clint.(\"quit\"to end):%s\n",buff);
        if(!strncmp(buff,"quit",4))
            break;
        if( send(connfd,buff,strlen(buff),0)<0)
        {
		 printf("send msg error:%s(errno:%d)\n",strerror(errno),errno);
         exit(1);        
		}
       }   
	  }	
	}
    close(connfd);
	printf("==============quit server?================\n");
	bzero(buff,MAXLINE);
	fgets(buff,MAXLINE,stdin);
	if(!strncmp(buff,"y",1))
	{
		printf("=========================QUIT=============================\n");
		break;
	}
   }
    close(listenfd);
	return 0;
}


客户端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>

#define MAXLINE 4096

int main(int argc, char** argv)
{
    int    sockfd, n;
    char    recvline[4096], sendline[4096];
    struct sockaddr_in    c_addr;
    fd_set rfds; 
    int ret;
    int max_fd;
    int rcv_len;
    int send_len;

    if( argc != 2){
    printf("usage: ./client <ipaddress>\n");
    exit(0);
    }

    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
    printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
    exit(0);
    }

    memset(&c_addr, 0, sizeof(c_addr));
    c_addr.sin_family = AF_INET;
    c_addr.sin_port = htons(6666);
    if( inet_pton(AF_INET, argv[1], &c_addr.sin_addr) <= 0)
    {
    printf("inet_pton error for %s\n",argv[1]);
    exit(0);
    }

    if( connect(sockfd, (struct sockaddr*)&c_addr, sizeof(c_addr)) < 0)
    {
    printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
    exit(0);
    }
    else
        printf("connect success!\n");
    
 /*================================================================================*/   
    while(1)

    {    
    FD_ZERO(&rfds);
    FD_SET(0,&rfds);//0 stdin
    FD_SET(sockfd,&rfds);
    //max_fd=0;
    //if(max_fd<sockfd)
       // max_fd=sockfd;
    
    ret=select(sockfd+1,&rfds,NULL,NULL,NULL);

    if(ret>=0)
    {
    if(FD_ISSET(0,&rfds))
    {    
    memset(sendline,0,sizeof(sendline));
    
    fgets(sendline, MAXLINE, stdin);
     
    send_len=send(sockfd,sendline,strlen(sendline),0);
    if( send_len<0)
    {
    printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
    exit(1);
    }
    else
    {
        printf("send msg to server(\"quit\" to end):%s\n",sendline);
    
        if(!strncmp(sendline,"quit",4))    
        {
          printf(" End \n");
          break;
    
        }
    
    }
    
    }

/* ========================================================================== */

    if(FD_ISSET(sockfd,&rfds))
    {
        
    memset(recvline,0,sizeof(recvline));
    
    rcv_len=recv(sockfd,recvline,MAXLINE,0);
    if(rcv_len<0)
    
    {    printf("recv msg error:%s(errno:%d)\n",strerror(errno),errno);;
         exit(1);
    }
    else if(rcv_len==0)
    
    { 
        printf("============chat End================\n");
        break;
    }
    else
        printf("recv from server:%s\n",recvline);
    
    }
    
    }
    
    }
    
    close(sockfd);
    
    return 0;
}

—————————————————————————————————————————————————————————————————————————————

帖一下修改后的图:

我们用3个终端测试,一个服务端,2个客户端:




大家可以看出当我第一个客户端退出后第个客户端才能连接,因为我代码中用了2个while循环嘛


第1个客户端退出:


第2个客户端退出:









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值