概述
- 用双向链表保存客户信息
- 每有用户接入创建线程,读取该用户消息,发回给所有用户。
- 用户退出,摘除节点,线程退出
代码
#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);
}
}
#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);
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);
}