zoukankan      html  css  js  c++  java
  • 【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)

    RT,Linux下使用c实现的多线程服务器。这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍。(>﹏<)

    本学期Linux、unix网络编程的第四个作业。

    先上实验要求:

    【实验目的】

    1、熟练掌握线程的创建与终止方法;

    2、熟练掌握线程间通信同步方法;

    3、应用套接字函数完成多线程服务器,实现服务器与客户端的信息交互。

    【实验内容】

     通过一个服务器实现最多5个客户之间的信息群发。

    服务器显示客户的登录与退出;

    客户连接后首先发送客户名称,之后发送群聊信息;

    客户输入bye代表退出,在线客户能显示其他客户的登录于退出。

    实现提示:

    1、服务器端:

    主线程:

    定义一个全局客户信息表ent,每个元素对应一个客户,存储:socket描述符、客户名、客户IP、客户端口、状态(初值为0)。

    主线程循环接收客户连接请求,在ent中查询状态为0的元素,

        如果不存在状态为0的元素(即连接数超过最大连接数),向客户发送EXIT标志;

        否则,修改客户信息表中该元素的socket描述符、客户IP、客户端口号,状态为1(表示socket可用);

    同时创建一个通信线程并将客户索引号index传递给通信线程。

    通信线程:

    首先向客户端发送OK标志

    循环接收客户发来信息,若信息长度为0,表示客户端已关闭,向所有在线客户发送该用户退出;

    若信息为用户名,修改全局客户信息表ent中index客户的用户名name,并显示该用户登录;

    若信息为退出,修改全局客户信息表ent中index客户状态为0,并显示该用户退出,终止线程;

    同时查询全局客户信息表ent,向状态为1的客户发送接收的信息。

    2、客户端:

    根据用户从终端输入的服务器IP地址及端口号连接到相应的服务器;

    连接成功后,接收服务端发来的信息,若为EXIT,则达到最大用户量,退出;

    若为OK,可以通讯,首先先发送客户名称;

    主进程循环从终端输入信息,并将信息发送给服务器;

    当发送给服务器为bye后,程序退出。

    同时创建一个线程负责接收服务器发来的信息,并显示,当接收的长度小于等于0时终止线程;

    有了上一次多进程服务器的编写经验以后,写起多线程就简单多了。

    照例还是绘制一下流程图,以方便我们理清思路。

    好啦,现在可以开始撸代码了。

    先实现一下用于通信的结构体clientmsg.h:(和多进程服务器是一样的)

     1 //CLIENTMSG between server and client
     2 #ifndef _clientmsg
     3 #define _clientmsg
     4 
     5 //USER MSG EXIT for OP of CLIENTMSG
     6 #define EXIT -1
     7 #define USER 1
     8 #define MSG 2
     9 #define OK 3
    10 
    11 #ifndef CMSGLEN
    12 #define CMSGLEN 100
    13 #endif
    14 
    15 struct CLIENTMSG{
    16     int OP;
    17     char username[20];
    18     char buf[CMSGLEN];
    19 };
    20 
    21 #endif

    客户端程序看起来比较简单,咱们把它实现了,client.c:

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <sys/socket.h>
      4 #include <netinet/in.h>
      5 #include <stdlib.h>
      6 #include <sys/types.h>
      7 #include <sys/wait.h>
      8 #include <signal.h>
      9 #include <unistd.h>
     10 #include <pthread.h>
     11 #include "clientmsg.h"
     12 
     13 struct ARG{
     14     int sockfd;
     15     struct CLIENTMSG clientMsg;
     16 };
     17 
     18 void *func(void *arg);
     19 void process_cli(int sockfd,struct CLIENTMSG clientMsg);
     20 int main(){
     21     int sockfd;
     22     char ip[20];
     23     int port;
     24     pthread_t tid;
     25     struct sockaddr_in server;
     26     struct CLIENTMSG clientMsgSend;
     27     struct ARG *arg;
     28     /*---------------------socket---------------------*/
     29     if((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1){
     30         perror("socket error
    ");
     31         exit(1);
     32     }
     33 
     34     /*---------------------connect--------------------*/
     35     printf("Please input the ip:
    ");
     36     scanf("%s",ip);
     37     printf("Please input the port:
    ");
     38     scanf("%d",&port);
     39     bzero(&server,sizeof(server));
     40     server.sin_family = AF_INET;
     41     server.sin_port = htons(port);
     42     inet_aton(ip,&server.sin_addr);
     43     if(connect(sockfd,(struct sockaddr *)&server,sizeof(server))== -1){
     44         perror("connect() error
    ");
     45         exit(1);
     46     }
     47     recv(sockfd,&clientMsgSend,sizeof(clientMsgSend),0);
     48     if(clientMsgSend.OP == OK){
     49         //创建一个线程
     50         arg = (struct ARG *)malloc(sizeof(struct ARG));
     51         arg->sockfd = sockfd;
     52         pthread_create(&tid,NULL,func,(void *)arg);
     53         //主线程
     54         printf("Please input the username:
    ");
     55         scanf("%s",clientMsgSend.username);
     56         clientMsgSend.OP = USER;
     57         send(sockfd,&clientMsgSend,sizeof(clientMsgSend),0);
     58         while(1){
     59             clientMsgSend.OP = MSG;
     60             scanf("%s",clientMsgSend.buf);
     61             if(strcmp("bye",clientMsgSend.buf) == 0){
     62                 clientMsgSend.OP = EXIT;
     63                 send(sockfd,&clientMsgSend,sizeof(clientMsgSend),0);
     64                 break;
     65             }
     66             send(sockfd,&clientMsgSend,sizeof(clientMsgSend),0);
     67         }
     68         pthread_cancel(tid);
     69     } 
     70     else{
     71         printf("以达到最大连接数!
    ");
     72     }
     73     /*------------------------close--------------------------*/
     74     close(sockfd);
     75 
     76     return 0;
     77 }
     78 
     79 
     80 void *func(void *arg){
     81     struct ARG  *info;
     82     info = (struct ARG *)arg;
     83     process_cli(info->sockfd,info->clientMsg);
     84     free(arg);
     85     pthread_exit(NULL);
     86 }
     87 void process_cli(int sockfd,struct CLIENTMSG clientMsg){
     88     int len;
     89     while(1){
     90                 bzero(&clientMsg,sizeof(clientMsg));
     91                 len =recv(sockfd,&clientMsg,sizeof(clientMsg),0);
     92                 if(len > 0){
     93                     if(clientMsg.OP ==USER){
     94                         printf("the user %s is login.
    ",clientMsg.username );
     95                     }
     96                     else if(clientMsg.OP == EXIT){
     97                         printf("the user %s is logout.
    ",clientMsg.username);
     98                     }
     99                     else if(clientMsg.OP == MSG){
    100                         printf("%s: %s
    ",clientMsg.username,clientMsg.buf );
    101                     }
    102                 }    
    103     }
    104 }

    然后是服务器端的实现,server.c:

      1 #include <stdio.h>
      2 #include <string.h>
      3 #include <sys/socket.h>
      4 #include <netinet/in.h>
      5 #include <stdlib.h>
      6 #include <sys/types.h>
      7 #include <sys/wait.h>
      8 #include <sys/stat.h>
      9 #include <unistd.h>
     10 #include <fcntl.h>
     11 #include <sys/ipc.h>
     12 #include <pthread.h>
     13 #include "clientmsg.h"
     14 
     15 struct Entity{
     16     int sockfd;
     17     char username[20];
     18     char buf[CMSGLEN];
     19     struct sockaddr_in client;
     20     int stat;
     21 };
     22 
     23 void *func(void *arg);
     24 void communicate_process(int index);
     25 struct Entity ent[5];
     26 
     27 int main(){
     28 
     29     struct sockaddr_in server;
     30     struct sockaddr_in client;
     31     int listenfd,connetfd;
     32     char ip[20];
     33     int port;
     34     int addrlen;
     35     struct CLIENTMSG clientMsg;
     36     pthread_t tid;
     37     int *arg;
     38     /*---------------------socket-------------------*/
     39     if((listenfd = socket(AF_INET,SOCK_STREAM,0))== -1){
     40         perror("socket() error
    ");
     41         exit(1);
     42     }
     43 
     44     /*----------------------IO-----------------------*/
     45     printf("Please input the ip:
    ");
     46     scanf("%s",ip);
     47     printf("Please input the port:
    ");
     48     scanf("%d",&port);
     49 
     50     /*---------------------bind----------------------*/
     51     bzero(&server,sizeof(server));
     52     server.sin_family = AF_INET;
     53     server.sin_port = htons(port);
     54     server.sin_addr.s_addr = inet_addr(ip);
     55     if(bind(listenfd,(struct sockaddr *)&server,sizeof(server))== -1){
     56         perror("bind() error
    ");
     57         exit(1);
     58     }
     59 
     60     /*----------------------listen-------------------*/
     61     if (listen(listenfd,5)== -1){
     62         perror("listen() error
    ");
     63         exit(1);
     64     }
     65     int i;
     66     for(i=0;i<5;i++){
     67         ent[i].stat = 0;
     68     }
     69     while(1){
     70         addrlen = sizeof(client);
     71         if((connetfd = accept(listenfd,(struct sockaddr *)&client,&addrlen))== -1){
     72             perror("accept() error
    ");
     73             exit(1);
     74         }
     75         int index = 5;
     76         for(i=0;i<5;i++){
     77             if(ent[i].stat == 0){
     78                 index = i;
     79                 break;
     80             }
     81         }
     82         if(index <= 4){    
     83             printf("connetfd:%d
    ",connetfd );
     84             ent[index].client = client;
     85             ent[index].sockfd = connetfd;
     86             ent[index].stat = 1;
     87             arg = malloc(sizeof(int));
     88             *arg = index;
     89             pthread_create(&tid,NULL,func,(void *)arg);
     90 
     91         }
     92         else{
     93             bzero(&clientMsg,sizeof(clientMsg));
     94             clientMsg.OP = EXIT;
     95             send(connetfd,&clientMsg,sizeof(clientMsg),0);
     96             close(connetfd);
     97         }
     98 
     99     }
    100 
    101     /*----------------------close-------------------*/
    102     
    103     close(listenfd);
    104 
    105     return 0;
    106 }
    107 
    108 
    109 /*----------------------------函数实现区----------------------------*/
    110 void *func(void *arg){
    111     int *info ;
    112     info = (int *)arg;
    113     communicate_process( *info);
    114     pthread_exit(NULL);
    115 }
    116 void communicate_process(int index){
    117     struct CLIENTMSG sendMsg;
    118     struct CLIENTMSG recvMsg;
    119     printf("sockfd:%d
    ",ent[index].sockfd );
    120     sendMsg.OP = OK;
    121     send(ent[index].sockfd,&sendMsg,sizeof(sendMsg),0);
    122 
    123     while(1){
    124         bzero(&sendMsg,sizeof(sendMsg));
    125         bzero(&recvMsg,sizeof(recvMsg));
    126         int len =recv(ent[index].sockfd,&recvMsg,sizeof(recvMsg),0);
    127         if(len > 0){
    128             if(recvMsg.OP == USER){
    129                 printf("user %s login from ip:%s,port:%d
    ",recvMsg.username,inet_ntoa(ent[index].client.sin_addr),ntohs(ent[index].client.sin_port) );
    130                 bcopy(recvMsg.username,ent[index].username,strlen(recvMsg.username));
    131                 sendMsg.OP = USER;
    132             }
    133             else if(recvMsg.OP == EXIT){
    134                 printf("user %s is logout
    ",recvMsg.username );
    135                 sendMsg.OP = EXIT;
    136                 ent[index].stat = 0;
    137                 int i;
    138                 for(i=0;i<5;i++){
    139                      if(ent[i].stat == 1){
    140                          
    141                          send(ent[i].sockfd,&sendMsg,sizeof(sendMsg),0);
    142                      }
    143                  }
    144                 break;
    145             }
    146             else if(recvMsg.OP == MSG){
    147                 sendMsg.OP = MSG;
    148             }
    149             bcopy(recvMsg.username,sendMsg.username,strlen(recvMsg.username));
    150             bcopy(recvMsg.buf,sendMsg.buf,strlen(recvMsg.buf));
    151             int i;
    152             for(i=0;i<5;i++){
    153                  if(ent[i].stat == 1){
    154                      printf("stat 1...
    ");
    155                      send(ent[i].sockfd,&sendMsg,sizeof(sendMsg),0);
    156                  }
    157              }
    158         }
    159         else{
    160             continue;
    161         }
    162     }
    163 }

    最后是makefile文件:

    main:server.o client.o 
        gcc server.o -oserver -lpthread
        gcc client.o -oclient -lpthread
    server.o:server.c clientmsg.h
        gcc -c server.c
    client.o:client.c clientmsg.h
        gcc -c client.c

    如果程序中引入了#include <pthread.h>,要记得在编译的时候 加上 -lpthread。

    下面上一下演示过程:(测试环境,Red Hat Enterprise Linux 6 + centos系Linux,ubuntu下可能会有些问题。)

    首先先把服务端启动开来,为了方便测试,这里直接使用的是127.0.0.1的localhost。

    然后启动两个客户端用来测试,在用户登录的时候客户端会有消息提醒。服务端会有日志打印输出客户端的名字和登录ip、端口。

    客户可以发送消息了,如图发送与接收均正常。这里为了简单演示只是启动了2个。

    输入bye以后即可退出聊天并下线。当有客户下线的时候,在线的客户端会收到下线提醒,客户端会有日志打印输出。

  • 相关阅读:
    java笔记前端部分
    java笔记数据库部分
    java笔记javaweb部分
    java笔记java部分
    JSON常用方法
    MYSQL索引
    Mysql查看执行计划参数解析(转载)
    MySQL中使用SHOW PROFILE命令分析性能的用法
    MySql查询
    DBeaver下载
  • 原文地址:https://www.cnblogs.com/msxh/p/4989883.html
Copyright © 2011-2022 走看看