简易多线程服务器

概述

  • 用双向链表保存客户信息
  • 每有用户接入创建线程,读取该用户消息,发回给所有用户。
  • 用户退出,摘除节点,线程退出

代码

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

typedef struct {
    int sockfd;
    char nick[30];
} client_t;

typedef struct node_ node_t;
struct node_ {
    client_t c;
    node_t *prev;
    node_t *next;
};

node_t *head = NULL;

void list_init()
{
    head = (node_t*)malloc(sizeof(node_t));
    head->next = head->prev = head;
}

void list_insert(client_t *c)
{
    node_t *p = (node_t*)malloc(sizeof(node_t));
    memset(p, 0x00, sizeof(node_t));
    p->c = *c;

    p->prev = head->prev;
    p->next = head;
    head->prev->next = p;
    head->prev = p;
}

void list_erase( int sfd )
{
    node_t *p = head->next;

    while ( p != head ) {
        if ( p->c.sockfd == sfd ) {
            p->next->prev = p->prev;
            p->prev->next = p->next;
            free(p);
            break;
        }
        p = p->next;
    }
}

int list_size( void )
{
    int i = 0;
    node_t *p;
    for ( p=head->next; p!=head; p=p->next )
        i++;
    return i;
}

void send_all(char *msg)
{
    node_t *p = head->next;

    while ( p != head ) {
        write(p->c.sockfd, msg, strlen(msg));
        p = p->next;
    }
}

int start( void )
{
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if ( lfd == -1 ) perror("socket"),exit(1);

    int op = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &op, sizeof(int));

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    int r = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
    if ( r == -1 ) perror("bind"),exit(1);

    r = listen(lfd, 10);
    if ( r == -1 ) perror("listen"),exit(1);

    return lfd;
}

void *process(void *arg)
{
    pthread_detach(pthread_self());
    int nfd = *(int*)arg;
    free(arg);
    char buf[256] = {};
    sprintf(buf, "welcom to chat room, current online %d\n", list_size());
    write(nfd, buf, strlen(buf));
    sprintf(buf, "input nick name:");
    write(nfd, buf, strlen(buf));
    memset(buf, 0x00, sizeof(buf));
    int r = read(nfd, buf, 256);
    if ( r <= 0 ) pthread_exit(NULL);
    client_t c; 
    strcpy(c.nick, buf);
    char name[30];
    strcpy(name, buf);
    c.sockfd = nfd;

    sprintf(buf, "%s online\n", c.nick);
    send_all(buf);

    list_insert(&c);

    while ( 1 ) {
        memset(buf, 0x00, sizeof(buf));
        char tmp[256] = {};
        r = read(nfd, tmp, 256);
        if ( r <= 0 ) break;
        time_t t = time(NULL);
        struct tm *pt = localtime(&t);;
        sprintf(buf, "%s %02d:%02d:%02d : %s", name, pt->tm_hour, pt->tm_min, pt->tm_sec, tmp);
        send_all(buf);
    }

    list_erase(nfd);
    sprintf(buf, "%s off line\n", name);
    send_all(buf);
    close(nfd);
}

int main( void )
{
    list_init();
    int lfd = start();
    printf("server is started\n");

    for ( ; ; ) {
        int nfd = accept(lfd, NULL, NULL);
        if ( nfd == -1 && errno == EINTR ) continue;
        pthread_t tid;
        int *p = (int*)malloc(sizeof(int));
        *p = nfd;
        pthread_create(&tid, NULL, process, (void*)p);
    }
}
//client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void *get_key(void *arg)
{
    int sfd = *(int*)arg;
    char buf[1024] = {};

    while ( 1 ) {
        memset(buf, 0x00, sizeof(buf));
        if ( read(0, buf, 1024) <= 0 )
            break;
        write(sfd, buf, strlen(buf));
    }
}

void *get_net(void *arg)
{
    int sfd = *(int*)arg;
    char buf[1024] = {};
    while ( 1 ) {
        memset(buf, 0x00, sizeof(buf));
        if ( read(sfd, buf, 1024) <= 0 )
            break;
        write(1, buf, strlen(buf));
    }
}

int main( void )
{
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if ( sfd == -1 ) perror("socket"),exit(1);
    printf("socket ok\n");

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);//9999  端口号 port
    if ( inet_aton("/*server ip address*/", &addr.sin_addr) == -1)
           perror("inet_aton"),exit(1);
    int r = connect(sfd, (struct sockaddr*)&addr, sizeof(addr));
    if ( r == -1 ) perror("connect"),exit(1);

    pthread_t tid1, tid2;

    int *p = (int*)malloc(sizeof(int));
    *p = sfd;
    pthread_create(&tid1, NULL, get_key, (void*)p);
    pthread_create(&tid2, NULL, get_net, (void*)p);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    free(p);

    close(sfd);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值