红联Linux门户
Linux帮助

linux下实现一个群聊天室,采用多线程技术

发布时间:2016-11-03 15:16:31来源:linux网站作者:蜜獾哥
一个服务器程序,一个客户端程序。
服务器运行后,监听,如果有新的连接,则会开一个线程负责接收数据。接收到的数据会发给所有已有的连接,最多支持10个连接,即聊天室最多10个人。如果有新的连接,则会连不上。
客户端连接后会开启两个线程,一个负责发送,一个负责接收。客户端仅和服务器通信,相当于数据由服务器转发。代码如下。
 
服务器程序:
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<errno.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
#include<pthread.h>
#include<signal.h>
#define MAX_CONNECT 2
#define MAXLEN 1024
#define DEFAULT_PORT 9012//有一个默认端口  
int  socket_fd, connect_fd;
struct sockaddr_in  servaddr;
char  buff[1024],sendline[1024];
int n,port;//让用户可以输入端口号
void *retval=0;
int sfd[MAX_CONNECT];//最多同时10个连接
/*void *send_fun(void *p)/这部分代码可以实现在服务器端发给所有人
{
int i;
while(1){
fgets(sendline, 1024, stdin);
for(i=0;i<10;i++){
if(sfd[i]!=-1){
if(send(sfd[i], sendline, strlen(sendline), 0) < 0)
{
printf("发送消息错误\n");
close(sfd[i]);
sfd[i]=-1;
//pthread_exit(&retval);
}
}
}
}
}*/
void *rev_fun(void *p)
{
int fd=*(int *)p;
int i;
while(1){
n = recv(fd, buff, MAXLEN,0);//这里肯定会阻塞,没有问题
if(n<=0){
printf("接收消息错误\n");
close(fd);
for(i=0;i<MAX_CONNECT;i++){
if(sfd[i]==fd){
sfd[i]=-1;
}
}
pthread_exit(&retval);
}
if(n==1)
continue;//回车符不打印
buff[n]='\0';
printf("%d:%s",fd,buff);
for(i=0;i<MAX_CONNECT;i++){//发给其他的人
if(sfd[i]!=-1){
if(sfd[i]!=fd){
send(sfd[i],buff, strlen(buff), 0);
}
}
}
}
}
/*信号服务函数,用来监听键盘,服务器关了之后,关掉所有连接*/
void sig_usr(int signo)
{
int i;
printf("\n");//先打一个换行,更好看
for(i=0;i<MAX_CONNECT;i++)
{
if(sfd[i]!=-1){
close(sfd[i]);
}
}
close(socket_fd);
exit(0);
}
void main()  
{
int i;
for(i=0;i<MAX_CONNECT;i++){
sfd[i]=-1;
}
signal(SIGINT,sig_usr);//在这里注册一个信号,用来接收键盘信号
printf("输入端口:");
scanf("%d",&port);
if((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("创建socket错误\n");
return;
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //
if(port<1024){
servaddr.sin_port = htons(DEFAULT_PORT);
}else{
servaddr.sin_port = htons(port);
}
if(bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("绑定socket错误\n");
return;
}
if(listen(socket_fd, 5) == -1){  //最多有5个连接在队列中,但并不代表最大支持10个连接
printf("监听socket错误\n");
return;
}
pthread_t rev_id;
//pthread_create(&send_id, NULL, send_fun, 0);
printf("等待连接\n");
while(1){
//connect_fd只是个临时变量,socket的套接字存放在各线程当中
if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("阻塞socket错误\n");
exit(0);
}
printf("新连接%d\n",connect_fd);
for(i=0;i<MAX_CONNECT;i++){
if(sfd[i]==-1){
break;
}
}
if(i<MAX_CONNECT){//代表还有位置
sfd[i]=connect_fd;
pthread_create(&rev_id, NULL, rev_fun, &sfd[i]);
}else if(i==MAX_CONNECT){//代表聊天室已经满了
printf("聊天室满了\n");
close(connect_fd);
}
}
close(socket_fd);
}
 
客户端程序:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<errno.h> 
#include<sys/types.h>
#include<netinet/in.h>  
#include<pthread.h>
#define MAXLEN 1024
int i,len1;
int port;
char strip[15];
pid_t rev_pid;
int  sockfd, n,rec_len;  
char recvline[1024], sendline[1024];  
char buf[MAXLEN];  
struct sockaddr_in  servaddr;  
pthread_t send_id,rev_id;
void *retval=0;
void *send_fun()
{
while(1){
fgets(sendline, 1024, stdin);
if(send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("发送消息错误\n");
close(sockfd);
exit(-1);//如果仅仅结束自己,则另一个线程无法结束,不如直接结束进程
//pthread_exit(&retval);
}
}
}
void *rev_fun()
{
while(1){
rec_len = recv(sockfd, buf, MAXLEN,0);
if(rec_len<=0){
printf("接收消息错误\n");
close(sockfd);
exit(-1);
//pthread_exit(&retval);
}
if(rec_len==1)
continue;
buf[rec_len]='\0';
printf("%d:%s",sockfd,buf);
}
}
void main()
{   
while(1){//用while1防止错误后得重新启动程序
printf("输入IP地址\n");
scanf("%s",strip);
len1=strlen(strip);
char ip[len1+1];
for(i=0;i<len1;i++)
{
ip[i]=strip[i];
}
ip[len1]='\0';
printf("输入端口\n");
scanf("%d",&port);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("创建socket错误\n");
continue;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
if(inet_pton(AF_INET, ip, &servaddr.sin_addr)<= 0){
printf("转换IP地址错误%s\n",ip);
close(sockfd);
continue;
}
if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("连接错误\n");
close(sockfd);
continue;
}
printf("连接成功\n");
pthread_create(&send_id, NULL, send_fun, 0);
pthread_create(&rev_id, NULL, rev_fun, 0);
/* 等待两个线程结束*/
pthread_join(send_id, &retval);
pthread_join(rev_id, &retval);
break;
}
}
 
可能还有很多漏洞,但基本的功能可以实现。
 
本文永久更新地址:http://www.linuxdiyf.com/linux/25696.html