Linux网络程序设计

本文详细介绍了TCP/IP协议的组成与应用层、传输层、网络层、网络接口层的功能,以及网络编程中的端口、socket端口、socket套接口的概念与作用,通过实例展示了TCP和UDP编程的基本流程与实现方法,最后探讨了socket通信中的阻塞问题及解决办法,包括使用select函数处理阻塞问题,并提供了聊天程序的示例。
摘要由CSDN通过智能技术生成

1.TCP/IP简介

        TCP/IP协议(Transmission Control Protocol  /  Internet  Protocol)叫做传输控制/网际协议,又叫做网络通信协议。它包含了上百个功能的协议,如ICMP、RIP、TELNET、FTP、SMTP、ARP、TFTP等,这些协议一起被称为TCP/IP协议。下表为协议族中一些常用的协议的英文名和含义。

 

协议名称说明
TCP传输控制协议
IP网际协议
UDP用户数据报协议
ICMP互联网控制协议
SMTP简单邮件传输协议
SNMP简单网络管理协议
FTP文件传输协议
ARP地址解析协议
        TCP/IP从协议分层模型方面可分为四个层次:网络接口层、网络层、传输层、和应用层。

阶层说明
应用层包括网络应用程序和网络进程,是与用户交互地界面,它为用户提供所需的各种服务,包括文件传输、远程登录和电子邮件等
传输层负责相邻计算机之间的通信
网络层用来处理计算机之间的通信问题,它接收传输层请求,传输某个具有目的地址信息的分组
网络接口层这是TCP/IP协议的最底层,负责接收IP数据报和把数据报通过选定的网络发送出去
        TCP和UDP是两种不同的网络传输方式。TCP是一种面向连接的网络传输方式,就像打电话的过程。这种方式是可靠的,缺点是传输过程复杂,需要占用较多的网络资源。UDP是一种不面向连接的传输方式,可以理解为寄信件。这种传输方式是不可靠的,但是有很好的传输效率 .

2.网络编程基础概念介绍

        端口:指计算机中为了标识在计算机中访问网络的不同程序而设的编号。每一个程序在访问网络时都会分配一个标识符,程序在访问网络或接受访问时,会用这个标识符表示这一网络数据属于这个程序。这里的端口是指不同程序的逻辑编号而不是网卡接线的端口。端口号是一个16位的无符号整数,对应的十进制取值范围是0~65535.不同编号范围的端口有不同的作用。低于256的端口是系统保留端口号,主要用于系统进程通信。而不在这一范围的端口号是自由端口号,在编程时可以调用这些端口号。

        socket端口:socket是网络编程的一种接口,它是一种特殊的I/O。在TCP/IP协议中,“IP地址+TCP或UDP端口号”可以唯一的标识网络通信中的一个进程。可以简单的认为:“IP地址+端口号”就称为socket。在TCP协议中,建立连接的两个进程各自有一个socket来标识,这两个socket组成的socket对就唯一的标识一个连接。用socket函数建立一个socket连接,此函数返回一个整形的socket描述符,随后进行数据传输。通常socket分为三种类型:流式socket、数据报socket和原始socket。

        socket套接口:区分不同应用程序进程间的网络通信和连接,主要使用三个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。在编程时,就是使用这三个参数来构成一个套接字。这个套接字相当于一个接口,可以进行不同计算机程序的信息传输。因此,一个IP地址,一个通信端口,就能确定一个通信程序的位置。为此,开发人员专门设计了一个套接结构,就是把网络程序中所用到的网络地址和端口信息放在一个结构体中。一般,套接口地址都以“sockaddr”开头。socket根据所使用的协议的不同,可分为TCP套接口和UDP套接口。

        socket套接口的数据结构: 主要有两种数据结构:sockaddr和sockaddr_in,如下图所示:

                                                                                              

        sockaddr用来保存一个套接字,定义方法如下所示:

       

struct sockaddr
{
    unsigned short int sa_family;
    char sa_data[14];
};

        sa_family:指定通信的地址类型。如果是TCP/IP通信,则该值为AF_INET。

        sa_data:最多使用14个字符长度,用来保存IP地址和端口信息。

        sockaddr_in的功能与sockaddr相同,也是用来保存IP地址和端口信息。不同的是将IP地址与读啊口号分开为不同的成员。这个结构体的定义方法如下所示。

          

struct sockaddr_in
{
    unsigned short int sin_family;
    uint16_t sin_port;
    struct in_addr sin_addr;
    unsigned char sin_zero[8];
};
        sin_family:与sockaddr结构体中的sa_family相同。

        sin_port:套接字使用的端口号。

        sin_addr:需要访问的IP地址。

        sin_zero:未使用的字段,填充为0.     

3.TCP编程

        网络上绝大多数的通信服务采用服务器机制,TCP提供的是一种可靠的、面向连接的服务。基于TCP网络编程的函数及功能如下表所示

函数名功能
socket用于建立一个socket连接
bind将socket与本机上的一个端口绑定,随后就可以在该端口监听服务请求
connect面向连接的客户程序使用connect函数来配置socket,并与远程服务器建立一个TCP连接
listenlisten函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们
acceptaccept函数让服务器接收客户的连接请求
close停止在该socket上的任何数据操作
send数据发送函数
recv数据接收函数
        服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态;客户端调用socket()初始化后,调用connect()发出同步信号SYN,并阻塞等待服务器应答,服务器应答一个同步-应答信号SYN-ACK,客户端收到后从connect()返回,同时应答一个ACk,服务器收到后从accept()返回。建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程就是由客户端主动发起请求,服务器被动的处理请求,一问一答的方式。因此,服务器从accept()返回后立即调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,再次期间客户端调用read()阻塞等待服务器端的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。如果客户端没有更多的请求了,就调用close()关闭连接,就像写端关闭的管道一样,服务器的read()返回0,这样服务器就知道了客户端关闭了连接,也调用close()关闭连接。当任何一方调用close()后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown()则连接处于半关闭状态,仍可接收对方发来的数据。

        分别编写服务器端、客户端程序,服务器通过socket连接后,在服务器上显示客户端的IP地址或域名,从客户端读字符,然后将每个字符转换为大写并回送给客户端。客户端发送字符串“连接上了”,客户端把接收到的字符串显示在屏幕上。

        服务器端程序代码如下:

#include<stdio.h>
#include<netdb.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#define SERVPORT 3333
#define BACKLOG 10
int main()
{
   int sockfd,client_fd;
   struct sockaddr_in my_addr;
   struct sockaddr_in remote_addr;
   int sin_size;
   if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
   {  
     perror("socket create fail");
     exit(1);
   }
   my_addr.sin_family = AF_INET;
   my_addr.sin_port = htons(SERVPORT);
   my_addr.sin_addr.s_addr = INADDR_ANY;
   bzero(&(my_addr.sin_zero),8);
   if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)
   {
     perror("bind err!");
     exit(1);
   }
   if(listen(sockfd,BACKLOG) == -1)
   {
     perror("listen err!");
     exit(1);
   }
   while(1)
   {
     sin_size = sizeof(struct sockaddr_in);
     if((client_fd = accept(sockfd,(struct sockaddr *)&remote_addr,&sin_size)) == -1)
     {
       perror("accept error");
       continue;
     }
     printf("receive a connection from :%d\n",inet_ntoa(remote_addr.sin_addr));
     if(!fork())
     {
       if(send(client_fd,"has been connected\n",26,0) == -1)
       perror("send err!");
       close(client_fd);
       exit(0);
     }
   close(client_fd);
   }
}
        与服务器端同位于socket1文件夹的makefile文件如下:

        

CC = gcc
AR = $(CC)ar
EXEC = skts
OBJS = skts.o
all: $(EXEC)
$(EXEC): $(OBJS)
	$(CC) -o $@ $(OBJS) -lm
clean:
	-rm-f $(EXEC) *.elf *.gdb *.o

        在文件夹socket2中创建客户端源程序sktc.c和makefile文件如下所示:

       

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#define SERVPORT 3333
#define MAXDATASIZE 100
int main(int argc,char * argv[])
{
  int sockfd,recvbytes;
  char buf[MAXDATASIZE];
  struct hostent * host;
  struct sockaddr_in serv_addr;
  if(argc < 2)
  {
    fprintf(stderr,"Pleae enter the server's hostname!!\n");
    exit(1);
  }
  if((host = gethostbyname(argv[1])) == NULL)
  {
    perror("gethostyname error!");
    exit(1);
  }
  if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
  {
    perror("socket create error!");
    exit(1);
  }
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(SERVPORT);
  serv_addr.sin_addr = * ((struct in_addr *)host->h_addr);
  bzero(&(serv_addr.sin_zero),8);
  if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) == -1)
  {
    perror("connect error!");
    exit(1);
  }
  if((recvbytes = recv(sockfd,buf,MAXDATASIZE,0)) == -1)
  {
    perror("connect error!");
    exit(1);
  }
  buf[recvbytes] = '\0';
  printf("recv: %s",buf);
  close(sockfd);
}

CC = gcc
AR = $(CC)ar
EXEC = sktc
OBJS = sktc.o
all: $(EXEC)
$(EXEC): $(OBJS)
	$(CC) -o $@ $(OBJS) -lm
clean:
	-rm-f $(EXEC) *.elf *.gdb *.o

        分别执行make命令:

                                                     

                                                     

         运行结果如下:

                                                     


        分别编写服务器端和客户端程序。服务器端程序的作用是从客户端读字符,然后将每个字符转换为大写并回送给客户端。客户端程序的作用是从命令行参数中获得一个字符串发送给服务器,然后接收返回的字符串并打印。

        服务器端源程序skts.c如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{
  struct sockaddr_in servaddr,cliaddr;
  socklen_t cliaddr_len;
  int listenfd,connfd;
  char buf[MAXLINE];
  char str[INET_ADDRSTRLEN];
  int i,n;
  listenfd = socket(AF_INET,SOCK_STREAM,0);
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htons(INADDR_ANY);
  servaddr.sin_port = htons(SERV_PORT);
  bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
  listen(listenfd,20); 
  printf("Accepting connections...\n");
  while(1)
  {
    cliaddr_len = sizeof(cliaddr);
    connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddr_len);
    n = read(connfd,buf,MAXLINE);
    printf("received from %d at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
    for(i=0;i<n;i++)
      buf[i] = toupper(buf[i]);
    write(connfd,buf,n);
    close(connfd);
  }
}
        服务器端makefile文件:
CC = gcc
AR = $(CC)ar
EXEC = skts
OBJS = skts.o
all: $(EXEC)
$(EXEC): $(OBJS)
	$(CC) -o $@ $(OBJS) -lm
clean:
	-rm-f $(EXEC) *.elf *.gdb *.o
        客户端源程序sktc.c如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc,char * argv[])
{
  struct sockaddr_in servaddr;
  char buf[MAXLINE];
  int sockfd,n;
  char * str;
  if(argc!=2)
  {
    fputs("usage:./client message\n",stderr);
    exit(1);
  }
  str = argv[1];
  sockfd = socket(AF_INET,SOCK_STREAM,0);
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family = AF_INET;
  inet_pton(AF_INET,"10.0.2.15",&servaddr.sin_addr);
  servaddr.sin_port = htons(SERV_PORT);
  connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
  write(sockfd,str,strlen(str));
  n = read(sockfd,buf,MAXLINE);
  printf("Response from server:\n");
  write(STDOUT_FILENO,buf,n);
  close(sockfd);
  return 0;
}
       客户端makefile文件如下:
CC = gcc
AR = $(CC)ar
EXEC = sktc
OBJS = sktc.o
all: $(EXEC)
$(EXEC): $(OBJS)
	$(CC) -o $@ $(OBJS) -lm
clean:
	-rm-f $(EXEC) *.elf *.gdb *.o

        分别执行make命令:

                                                                    

                                                                    

        程序的运行结果如下:

                                                                    

4.UDP编程

        由于UDP不需要维护连接,程序逻辑简单很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现。下面介绍的是一个基于UDP协议的编程。其最主要的特点是在客户端不需要用函数bind把本地IP与端口号进行绑定也能进行通信。常用的基于UDP网络编程的函数及功能如下表所示:

函数名功能
bind将socket与本机上的一个端口绑定,随后就可以在该端口监听服务请求
close停止在该socket上的任何数据操作
sendto数据发送函数
recvfrom数据接收函数
        下面的程序,服务器端接收客户端发送的字符串。客户端打开wmj文件,读取文件中的3个字符串,传送给服务器端,当传送给服务器端的字符串为“stop”时,终止数据传送并断开连接。

        服务器端程序代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<errno.h>
#include<sys/types.h>
int port = 8888;
int main()
{
  int sockfd;
  int len;
  int z;
  char buf[256];
  struct sockaddr_in adr_inet;
  struct sockaddr_in adr_clnt;
  printf("等待客户端...\n");
  adr_inet.sin_family = AF_INET;
  adr_inet.sin_port = htons(port);
  adr_inet.sin_addr.s_addr = htonl(INADDR_ANY);
  bzero(&(adr_inet.sin_zero),8);
  len = sizeof(adr_clnt);
  sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(sockfd == -1)
  {
    perror("socket error");
    exit(1);
  }
  z = bind(sockfd,(struct sockaddr *)&adr_inet,sizeof(adr_inet));
  if(z == -1)
  {
    perror("bind error");
    exit(1);
  }
  while(1)
  {
    z = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&adr_clnt,&len);
    if(z<0)
    {
      perror("recvfrom error");
      exit(1);
    }
    buf[z] = '\0';
    printf("接收:%s",buf);
    if(strncmp(buf,"stop",4) == 0)
    {
      printf("结束...\n");
      break;
    }
  }
  close(sockfd);
  exit(0);
}
  
        客户端程序代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<errno.h>
#include<sys/types.h>
int port = 8888;
int main()
{
  int sockfd;
  int i = 0;
  int z;
  char buf[80],str1[80];
  struct sockaddr_in adr_srvr;
  FILE * fp;
  printf("打开文件......\n");
  fp = fopen("wmj","r");
  if(fp == NULL)
  {
    perror("打开文件失败");;
    exit(1);
  }
  printf("连接服务器...\n");
  adr_srvr.sin_family = AF_INET;
  adr_srvr.sin_port = htons(port);
  adr_srvr.sin_addr.s_addr = htonl(INADDR_ANY);
  bzero(&(adr_srvr.sin_zero),8);
  sockfd = socket(AF_INET,SOCK_DGRAM,0);
  if(sockfd == -1)
  {
    perror("socket error");
    exit(1);
  }
  printf("发送文件...\n");
  for(i=0;i<3;i++)
  {
    fgets(str1,80,fp);
    printf("%d:%s",i,str1);
    sprintf(buf,"%d:%s",i,str1);
    z = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&adr_srvr,sizeof(adr_srvr));
    if(z<0)
    {
      perror("sendto error");
      exit(1);
    }
  }
  printf("发送......\n");
  sprintf(buf,"stop\n");
  z = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&adr_srvr,sizeof(adr_srvr));
  if(z<0)
  {
    perror("sendto error");
    exit(1);
  }
  fclose(fp);
  close(sockfd);
  exit(0);
}
        文件wmj的内容:
hello world
hello linux
hello linux world

        程序的运行结果如下:

                                                                      

5.网络高级编程

        前面介绍了socket通信中常用的函数,利用这些函数能满足基本的socket通信需要;同时也介绍了这些函数用来编写面向连接的网络通信程序和面向无连接的网络程序设计。但在socket应用中,还有一个很重要的问题,那就是如何处理阻塞,解决I/O多路利用问题。在数据通信中,当服务器运行函数accept()时,假设没有客户机连接请求到来,那么服务器就会一直停在accept()语句上,等待客户机连接请求的到来,出现这样的情况就称为阻塞。

        下列程序应用函数select处理阻塞问题,程序运行时,如果在设置的10.5秒内没有输入,程序就会显示“超时”;如果在10.5秒内,有输入并回车,则给出显示“输入了”。

#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
#define STDIN 0
int main()
{
  struct timeval tv;
  fd_set readfds;
  tv.tv_sec = 10;
  tv.tv_usec = 500000;
  FD_ZERO(&readfds);
  FD_SET(STDIN,&readfds);
  select(STDIN + 1,&readfds,NULL,NULL,&tv);
  if(FD_ISSET(STDIN,&readfds))
    printf("输入了\n");
  else
    printf("超时\n");
}

        程序运行结果如下:

                                                                       

        下面编写一个聊天程序

        服务器端源代码如下: 

#include<stdlib.h>
#include<stdio.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fcntl.h>
#define MAXDATASIZE 256
#define SERVPORT 4444
#define BACKLOG 10
#define STDIN 0
int main(void)
{
  FILE * fp;
  int sockfd,client_fd;
  int sin_size;
  struct sockaddr_in my_addr,remote_addr;
  char buf[256];
  char buff[256];
  char send_str[256];
  int recvbytes;
  fd_set rfd_set,wfd_set,efd_set;
  struct timeval timeout;
  int ret;
  if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
  {
    perror("socket");
    exit(1);
  }
  bzero(&my_addr,sizeof(struct sockaddr_in));
  my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons(SERVPORT);
  inet_aton("127.0.0.1",&my_addr.sin_addr);
  if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)
  {
    perror("bind error");
    exit(1);
  }
  if(listen(sockfd,BACKLOG) == -1)
  {
    perror("listen error");
    exit(1);
  }
  sin_size = sizeof(struct sockaddr_in);
  if((client_fd = accept(sockfd,(struct sockaddr *)&remote_addr,&sin_size)) == -1)
  {
    perror("accpet error");
    exit(1);
  }
  fcntl(client_fd,F_SETFD,O_NONBLOCK);
  recvbytes = recv(client_fd,buff,MAXDATASIZE,0);
  buff[recvbytes] = '\0';
  fflush(stdout);
  if((fp = fopen("name.txt","a+")) == NULL)
  {
    printf("can not open file,exit...\n");
    return -1;
  }
  fprintf(fp,"%s\n",buff);
  while(1)
  {
    FD_ZERO(&rfd_set);
    FD_ZERO(&wfd_set);
    FD_ZERO(&efd_set);
    FD_SET(STDIN,&rfd_set);
    FD_SET(client_fd,&rfd_set);
    FD_SET(client_fd,&wfd_set);
    FD_SET(client_fd,&efd_set);
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;
    ret = select(client_fd + 1,&rfd_set,&wfd_set,&efd_set,&timeout);
    if(ret == 0)
    {
      continue;
    }
    if(ret < 0)
    {
      perror("select error:");
      exit(-1);
    }
    if(FD_ISSET(STDIN,&rfd_set))
    {
      fgets(send_str,256,stdin);
      send_str[strlen(send_str)-1] = '\0';
      if(strncmp("quit",send_str,4) == 0)
      {
        close(client_fd);
        close(sockfd);
        exit(0);
      }
      send(client_fd,send_str,strlen(send_str),0);
    }
    if(FD_ISSET(client_fd,&rfd_set))
    {
      recvbytes = recv(client_fd,buf,MAXDATASIZE,0);
      if(recvbytes == 0)
      {
        close(client_fd);
        close(sockfd);
        exit(0);
      }
      buf[recvbytes] = '\0';
      printf("%s:%s\n",buff,buf);
      printf("Server: ");
      fflush(stdout);
    }
    if(FD_ISSET(client_fd,&efd_set))
    {
      close(client_fd);
      exit(0);
    }
  }
}
        客户端源程序如下:
#include<stdlib.h>
#include<stdio.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fcntl.h>
#define SERVPORT 4444
#define MAXDATASIZE 256
#define STDIN 0
int main(void)
{
  int sockfd;
  int recvbytes;
  char buf[MAXDATASIZE];
  char * str;
  char name[MAXDATASIZE];
  char send_str[MAXDATASIZE];
  struct sockaddr_in serv_addr;
  fd_set rfd_set,wfd_set,efd_set;
  struct timeval timeout;
  int ret;
  if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
  {
    perror("socket error");
    exit(1);
  }
  bzero(&serv_addr,sizeof(struct sockaddr_in));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(SERVPORT);
  inet_aton("127.0.0.1",&serv_addr.sin_addr);
  if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) == -1)
  {
    perror("connect error");
    exit(1);
  }
  fcntl(sockfd,F_SETFD,O_NONBLOCK);
  printf("要聊天首先输入你的名字:");
  scanf("%s",name);
  name[strlen(name)] = '\0';
  printf("%s: ",name);
  fflush(stdout);
  send(sockfd,name,strlen(name),0);
  while(1)
  {
    FD_ZERO(&rfd_set);
    FD_ZERO(&wfd_set);
    FD_ZERO(&efd_set);
    FD_SET(STDIN,&rfd_set);
    FD_SET(sockfd,&rfd_set);
    FD_SET(sockfd,&efd_set);
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;
    ret = select(sockfd+1,&rfd_set,&wfd_set,&efd_set,&timeout);
    if(ret == 0)
    {
      continue;
    }
    if(ret < 0)
    {
      perror("select error: ");
      exit(-1);
    }
    if(FD_ISSET(STDIN,&rfd_set))
    {
      fgets(send_str,256,stdin);
      send_str[strlen(send_str)-1] = '\0';
      if(strncmp("quit",send_str,4) == 0)
      {
        close(sockfd);
        exit(0);
      }
    send(sockfd,send_str,strlen(send_str),0);
    }
    if(FD_ISSET(sockfd,&rfd_set))
    {
      recvbytes = recv(sockfd,buf,MAXDATASIZE,0);
      if(recvbytes == 0)
      {
        close(sockfd);
        exit(0);
      }
      buf[recvbytes] = '\0';
      printf("Server: %s\n",buf);
      printf("%s: ",name);
      fflush(stdout);
    }
    if(FD_ISSET(sockfd,&efd_set))
    {
      close(sockfd);
      exit(0);
    }
  }
}

        运行结果如下:

                                                                            

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值