一、使用原理

TCP网络传输;多线程事件处理;

 

二、功能简介:(服务端)

1、打开服务端,服务端进行初始化,并等待客户端的连接;

2、打开客户端,输入服务端的IP地址与端口号;服务端会产生一个线程与新增的客户端进行通信,并分配客户端的名称,每连接一个客户端,服务端会产生一个线程与客户端进行通信;

3、每新建一个客户端服务端会为客户端分配一个名字,依次为: ‘a’,'b','c',.....

三、使用方法

1、单独发送信息:每个客户端发送的信息会保存在服务端的缓冲区中,例如

客户端a发送:

a hello;

即发送给自己消息:

hello;

客户端a发送:

b hello;

即发送给客户端b消息:

hello;

2、群发消息:

在发送的消息前面加上 ‘x’ ,即触发群发消息的功能;

 

四、编译   

由于使用了线程的功能,所以在编译的时候  gcc server.c -lpthead -o server  加上线程库;

 

五、代码(server.c)     客户端可以先使用      nc 127.0.0.1 9527    (服务端IP加端口号) 进行调试

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
//#include<ctype.h>
#include<unistd.h>
//#include<errno.h>
#include<pthread.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define SERV_PORT 9527          /*服务端的端口号       */
#define ONLINEMAX 10             /*同时在线的最大人数   */

void *ChatOnline(void *arg);    /*线程的聊天,消息处理函数   */
void fuck_filter(const char* buf);
int UserSelect(char name);

/*用于用户信息的临时参数存储 ->  套接字的文件描述符 && IP+PORT  */
struct arg_user
{
        int cfd;
        struct sockaddr_in clit_addr;
};

/*用户信息的存放函数 ->用户名 && 套接字文件描述符    */
struct user
{
        char  name;
        int user_cfd;
}clit_user[ONLINEMAX];

/*报错函数  */
void sys_err(const char *str)
{
        perror(str);
        exit(1);
}

static int user_number = 0;     /* 用户的号数*/

int main()
{
        int sfd = 0,cfd = 0;
        int ret = 0,i = 0,tmp = 0;
        char buf[BUFSIZ];        //BUFSIZ大小为8192字节
        struct sockaddr_in serv_addr,clit_addr;
        struct arg_user *arg;
        socklen_t clit_addr_len;
        pthread_t pthread;

        /*设置 struct sockaddr_in 里面的内容 采用IPv4 端口号为9527 IP号由INADDR_ANY生成的二进制的IP地址   */
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        /*生成套接字,并校验*/
        sfd = socket(AF_INET,SOCK_STREAM,0);
        if(sfd == -1){
                sys_err("socket error");
        }
        printf("socket .....");

        /*绑定端口号+IP地址  */
        bind(sfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
        printf("bind .......");

        /*设置监听的上限        */
        listen(sfd,20);
        printf("listen .....");

        int opt = SO_REUSEADDR;
        setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
      /*setsockopt()用来设置参数s所指定的socket状态。
      参数level代表欲设置的网络层,一般设成SOL_SOCKET以存取socket层。
      参数optname代表欲设置的选项,有下列几种数值:SO_REUSEADDR 允许在bind()过程中本地地址可重复使用
        参数  optval代表欲设置的值,参数optlen则为optval的长度。
      返回值  成功则返回0,若有错误则返回-1,错误原因存于errno。*/
        /*监听,阻塞至有客户端连接,并返回一个新的套接字与客户端建立通讯 */

        clit_addr_len = sizeof(clit_addr);


        /*读取客户端发来的信息,并转化为大写    */
        while(1){
                printf("accept .....\n");
                cfd = accept(sfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
                if(cfd == -1)   sys_err("accpet error");
//              printf("client's IP = %s",inet_ntoa(clit_addr.sin_addr));

/*指向结构体的结构体要先分配空间    将用户信息收集起来 */
                arg = malloc(sizeof(struct arg_user));
                arg->cfd = cfd;
                memcpy((void *)&arg->clit_addr, &clit_addr, sizeof(clit_addr));

                tmp = pthread_create(&pthread,NULL,ChatOnline,(void*)arg);
                if(tmp != 0)    sys_err("pthread_create faile");
        }

        close(sfd);
        close(cfd);
        return 0;
}

void *ChatOnline(void *arg)
{
        int cfd = 0;int ret = 0;
        struct sockaddr_in clit_addr;
        struct arg_user *n_arg;
        char buf[BUFSIZ];
//      char *p=NULL;
        char name;
        struct pid{
                int pid_pid;
                char pid_name;
        }my_pid;

        n_arg = malloc(sizeof(struct arg_user));
        n_arg = arg;
        cfd = n_arg->cfd;
        clit_addr = n_arg->clit_addr;

        clit_user[user_number].name = 'a'+user_number;
        clit_user[user_number].user_cfd = cfd;
        my_pid.pid_name = clit_user[user_number].name;
        if(user_number++ > ONLINEMAX) user_number = 0;

        printf("client's IP = %s...name = %c\n",inet_ntoa(clit_addr.sin_addr),my_pid.pid_name);
        my_pid.pid_pid = pthread_self();

        while(1){
                memset(buf,0,BUFSIZ);
                ret = read(cfd,buf,sizeof(buf));
                if(ret == 0 || ret == -1){
                        perror("client disconnec");
                        close(cfd);
                        break;
                }
                write(STDOUT_FILENO,buf,ret);

                fuck_filter(buf);       //话语过滤
                name = buf[0];

                if(name == 'x'){
                        int i;
                        for(i = 0;i < ONLINEMAX;i++){
                                if(clit_user[i].user_cfd > 0){
        //                              write(clit_user[i].user_cfd,"[THE MESSAGE From]:->",28);
        //                              write(clit_user[i].user_cfd,p,sizeof(p));
                                        write(clit_user[i].user_cfd,buf,ret);

                                }
                        }
                }
                int user_cfd = UserSelect(name);
                if(user_cfd > 0 ){
                        write(user_cfd,buf,ret);

                }
        }
}
int UserSelect(char name)
{
    int ufd = 0;
    int i;
    for(i=0;i<= ONLINEMAX;i++)
    {

        if(name == clit_user[i].name)
            return clit_user[i].user_cfd;
    }

    return ufd;
}


void fuck_filter(const char* buf)
{
    char buf_fuck[] = "fuck";
    char * p = NULL;
    do{
        p = strstr(buf,buf_fuck);
        if(p != 0){
            *p = '*';
            *(++p) = '*';
            *(++p) = '*';
            *(++p) = '*';
        }
    }while(p != 0);
}

 

Logo

更多推荐