Linux网络聊天室
1 基本原理
当某个客户端需要发送消息是,它将此消息发送给服务器,服务器再将此消息转发给其他客户端,各客户端之间是无连接的,即相互之间不能直接通信。
2 实现方式
一、服务器的多线程实现:
(1)主线程:
建立socket;
bind本机地址信息和socket;
listen客户端;
循环accept客户端,每当成功连接一个客户端后,创建一个与该客户端交互的子线程,并与之剥离。
(2)与客户端交互的子线程:
循环设计:
接收客户端的数据recv()
将该客户端的数据发送给除了该客户端以外的其他在线的客户端。(在线的判断依据:accept成功返回一个新的套接字。通过这个套接字来区分不同的客户端。)send()
注意点:需要建立一个数组,用于保存accept成功的客户端的新的socket,并且客户端退出后,要将相应的数组元素清空,以留给新连接成功的客户端。
二、客户端的多线程实现:
(1)主线程:
建立socket;
connect 服务器;
创建一个专门发送数据给服务器的子线程;
创建一个专门接收来自服务器的数据的子线程;
等待两个子线程的结束
服务器端源代码:
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> #include <pthread.h> #define MAX_CLT 5 int clt_sfd[MAX_CLT] = {0}; char clt_name[MAX_CLT][100] = {0}; void *broadcast_clt(void *arg) { long index = (long)arg; int sfd = clt_sfd[index]; int ret = -1; int i = 0; char buf[100] = {0}; char welcome[100] = {0}; char msg[100] = {0}; while(1) { ret = recv(sfd, buf, 100, 0); if((ret < 0) || (0 == ret)) { clt_sfd[index] = 0; break; }
if(0 == strncmp(buf, "quit", 4)) { send(sfd, "quit", 5, 0); clt_sfd[index] = 0; break; } if(0 == strncmp(buf, "NAME", 4)) { strcpy(clt_name[index], buf + 4); bzero(welcome, 100); sprintf(welcome, "Welcome %s!", clt_name[index]); for(i = 0; i < MAX_CLT; i++) { if(clt_sfd[i] != 0) { send(clt_sfd[i], welcome, 100, 0); } } } else { sprintf(msg, "%s: %s", clt_name[index], buf); for(i = 0; i < MAX_CLT; i++) { if((clt_sfd[i] != 0) && (clt_sfd[i] != sfd)) { send(clt_sfd[i], msg, 100, 0); } } } usleep(1000); }
close(sfd); pthread_exit(NULL); } int main(void) { pthread_t broadcast_thread[MAX_CLT]; long i = 0; int ret = -1; //int socket(int domain, int type, int protocol); int sfd = -1; int new_sfd = -1; sfd = socket(AF_INET, SOCK_STREAM, 0);
//int bind(int sockfd, struct sockaddr *my_addr, int addrlen); struct sockaddr_in srv_addr; bzero(&srv_addr, sizeof(struct sockaddr_in)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = 6666; srv_addr.sin_addr.s_addr = 0; //服务器IP为0,表示本机IP,通用 ret = bind(sfd, (struct sockaddr *)(&srv_addr), sizeof(struct sockaddr)); //int listen(int sockfd, int backlog); ret = listen(sfd, 10); while(1) { //int accept(int sockfd, struct sockaddr *addr, int *addrlen); int clt_addrlen = 0; struct sockaddr_in clt_addr; bzero(&clt_addr, sizeof(struct sockaddr_in)); new_sfd = accept(sfd, (struct sockaddr *)(&clt_addr), &clt_addrlen); if(-1 == new_sfd) { continue; } for(i = 0; i < MAX_CLT; i++) { if(0 == clt_sfd[i]) { clt_sfd[i] = new_sfd; pthread_create(&broadcast_thread[i], NULL, broadcast_clt, (void *)i); pthread_detach(broadcast_thread[i]); break; } } } close(sfd); for(i = 0; i < MAX_CLT; i++) { if(clt_sfd[i] > 0) { close(new_sfd); } } return 0; } |
客户端源代码:
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <unistd.h> #include <stdio.h> #include <string.h> #include <pthread.h> #include <string.h> #include <semaphore.h> char send_buf[100] = {0}; char recv_buf[100] = {0}; void *send_srv(void *arg) { while(1) { bzero(send_buf, sizeof(send_buf)); read(0, send_buf, 100); send_buf[strlen(send_buf) - 1] = '\0'; long tmp = (long)arg; int cfd = (int)tmp; send(cfd, send_buf, 100, 0); if(0 == strncmp(send_buf, "quit", 4)) { break; } } pthread_exit(NULL); } void *recv_srv(void *arg) { int ret = -1; while(1) { long tmp = (long)arg; int cfd = (int)tmp; bzero(recv_buf, sizeof(recv_buf)); ret = recv(cfd, recv_buf, 100, 0); if((ret < 0) || (0 == ret)) { break; } if(0 == strncmp(recv_buf, "quit", 4)) { break; } puts(recv_buf); } pthread_exit(NULL); } int main(int argc, void **argv) { int ret = -1; int cfd = -1; pthread_t recv_thread; pthread_t send_thread; cfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in srv_addr; bzero(&srv_addr, sizeof(struct sockaddr_in)); srv_addr.sin_family = AF_INET; srv_addr.sin_port = 6666; srv_addr.sin_addr.s_addr = inet_addr(argv[1]);
ret = connect(cfd, (struct sockaddr *)(&srv_addr), sizeof(struct sockaddr)); puts("Please input your name:"); char name[100] = {0}; char buf[100] = {0}; read(0, buf, 100); buf[strlen(buf) - 1] = '\0'; sprintf(name, "NAME%s", buf); send(cfd, name, 100, 0); usleep(10000); long send_cfd = (long)cfd; long recv_cfd = (long)cfd; pthread_create(&send_thread, NULL, send_srv, (void *)send_cfd); pthread_create(&recv_thread, NULL, recv_srv, (void *)recv_cfd); pthread_join(send_thread, NULL); pthread_join(recv_thread, NULL); close(cfd); return 0; } |