当前位置:文档之家› linux聊天室程序

linux聊天室程序

Linux C-多线程的聊天室一.多线程与进程:线程是程序中一个单一的顺序控制流程。

进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。

在单个程序中同时运行多个线程完成不同的工作,称为多线程。

每个应用对应一个进程。

而一个进程包含至少一个线程,线程要和cpu进行交互。

多线程并发:“同一时间”执行多件事情,如程序一边执行,一边计时。

多线程并发的本质:并不是cpu“同时”执行多件事情,而是在不停的切换,执行一段时间事件A,切换,执行时间事件B;再切换,再执行,如此往复。

线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针,程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。

多线程是指程序中包含多个程序执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是允许单个程序创建多个并行执行的线程来完成各自的任务。

二.聊天室功能:实现多用户群体聊天功能(此程序最多设定为10人),每个人所发送的消息其他用户均可以收到。

用户可以随意的加入或退出。

三.所用函数:创建int socket(int domain, int type, int protocol);domain:协议域。

协议族决定了socket的地址类型,在通信中必须采用对应的地址。

type:指定Socket类型。

常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。

protocol:指定协议。

绑定int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);参数说明:socket:是一个套接字描述符。

address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。

address_len:确定address缓冲区的长度。

接收int recv(SOCKET socket, char FAR* buf, int len, int flags);socket:一个标识已连接套接口的描述字。

buf:用于接收数据的缓冲区。

len:缓冲区长度。

flags:指定调用方式。

取值:MSG_PEEK 查看当前数据,数据将被复制到缓冲区中,但并不从输入队列中删除;MSG_OOB 处理带外数据。

网络监听int listen(SOCKET sockfd, int backlog);sockfd一个已绑定未被连接的套接字描述符backlog 连接请求队列的最大长度(一般由2到4)接收连接请求int accept( int fd, struct socketaddr* addr, socklen_t* len);fd:套接字描述符。

addr:返回连接着的地址len:接收返回地址的缓冲区长度四.服务器端1.程序结构:总共有三个函数:主函数(main),实现服务器端的初始化,接受连接;消息处理函数(RC_snd),接受某一用户的消息,将其进行简单处理之后发送给其他所有的用户;退出函数(quit),可实现服务器关停。

这三个函数分别从属于三个线程:main函数的作为主线程,又创建了一个退出函数所在的线程,以及每次接受到一个连接之后会新创建一个对此连接的消息进行处理的线程。

2.服务器端代码:#define LISTENQ 5#define MAXLINE 512#define MAXMEM 10#define NAMELEN 20//分别记录服务器端的套接字与连接的多个客户端的套接字int listenfd,connfd[MAXMEM];//服务器关闭函数void quit();//服务器接收并转发消息函数 6void rcv_snd(int n);int main(){pthread_t thread;struct sockaddr_in servaddr,cliaddr;socklen_t len;time_t ticks;char buff[MAXLINE];//调用socket函数创建服务器端的套接字printf("Socket...\n");listenfd=socket(AF_INET,SOCK_STREAM,0);if(listenfd<0){printf("Socket created failed.\n");return -1;}//调用bind函数使得服务器端的套接字与地址实现绑定printf("Bind...\n");servaddr.sin_family=AF_INET;servaddr.sin_port=htons(6666);servaddr.sin_addr="127.0.0.1";if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){printf("Bind failed.\n");return -1;}//调用listen函数,将一个主动连接套接字变为被动的倾听套接字//在此过程中完成tcp的三次握手连接printf("listening...\n");listen(listenfd,LISTENQ);//创建一个线程,对服务器程序进行管理(关闭)pthread_create(&thread,NULL,(void*)(&quit),NULL);//记录空闲的客户端的套接字描述符(-1为空闲)int i=0;for(i=0;i<MAXMEM;i++){connfd[i]=-1;}while(1){len=sizeof(cliaddr);for(i=0;i<MAXMEM;i++){if(connfd[i]==-1){break;}}//调用accept从listen接受的连接队列中取得一个连接connfd[i]=accept(listenfd,(struct sockaddr*)&cliaddr,&len);ticks=time(NULL);sprintf(buff,"% .24s \r \n",ctime(&ticks));printf("%s Connectfrom:%s,port%d\n\n”, ,buff,inet_ntoa(cliaddr.sin_addr.s_addr),ntohs(cliaddr.sin_port));//针对当前套接字创建一个线程,对当前套接字的消息进行处理。

pthread_create(malloc(sizeof(pthread_t)),NULL,(void*)(&rcv_snd),(void*)i);}return 0;}void quit(){char msg[10];while(1){scanf("%s",msg);if(strcmp("quit",msg)==0){printf("Byebye...\n");close(listenfd);exit(0);}}}//消息处理函数void rcv_snd(int n){char* ask="Your name please锛?;char buff[MAXLINE];char buff1[MAXLINE];char buff2[MAXLINE];char name[NAMELEN];time_t ticks;int i=0;int retval;//获取此进程对应的套接字用户的名字write(connfd[n],ask,strlen(ask));int len;len=read(connfd[n],name,NAMELEN);if(len>0){name[len]=0;}//把当前用户的加入告知所有用户strcpy(buff,name);strcat(buff,"\tjoin in\0");for(i=0;i<MAXMEM;i++){if(connfd[i]!=-1){write(connfd[i],buff,strlen(buff));}}//接受当前用户的信息并将其转发给所有的用户while(1){if((len=read(connfd[n],buff1,MAXLINE))>0){buff1[len]=0;//当当前用户的输入信息为“bye”时,当前用户退出if(strcmp("bye",buff)==0){close(connfd[n]);connfd[n]=-1;pthread_exit(&retval);}ticks=time(NULL);sprintf(buff2,"%.24s\r\n",ctime(&ticks));strcpy(buff,name);strcat(buff,"\t");strcat(buff,buff2);strcat(buff,buff1);for(i=0;i<MAXMEM;i++){if(connfd[i]!=-1){write(connfd[i],buff,strlen(buff));}}}}}四.客户端1.程序结构:总共有两个函数:一个主函数(main),实现客户端套接字的初始化,与服务器建立连接,并且接收消息再本地显示;一个发送函数(snd),从控制台读入信息然后发送到服务器。

这两个函数分别在两个线程上运行,一个是主函数所在的线程,另一个是在主函数里创建的发送函数所在的线程。

2.客户端代码讲解:#define MAXLINE 512#define NAMELEN 20#define PORT 6666#define HOST_ADDR "127.0.0.1"int sockfd;void snd();int main(){pthread_t thread;struct sockaddr_in servaddr;//调用socket函数创建客户端的套接字sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){printf("Socket create failed\n");return -1;}//初始化服务器端的地址servaddr.sin_family=AF_INET;servaddr.sin_port=htons(PORT);if(inet_aton(HOST_ADDR,&servaddr.sin_addr)<0){printf("inet_aton error.\n");return -1;}//调用connect函数实现与服务器端建立连接printf("Connecting...\n");if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){printf("Connect server failed.\n");return -1;}//从此处开始程序分做两个线程//创建发送消息的线程,调用了发送消息的函数snd pthread_create(&thread,NULL,(void*)(&snd),NULL);//从此处开始向下为接收消息的线程char buff[MAXLINE];int len;while(1){if((len=read(sockfd,buff,MAXLINE))>0){buff[len]=0;printf("\n%s\n\n",buff);}}return 0;}//发送消息的函数sndvoid snd(){char name[NAMELEN];char buff[MAXLINE];gets(name);write(sockfd,name,strlen(name));while(1){gets(buff);write(sockfd,buff,strlen(buff));if(strcmp("bye",buff)==0){exit(0);}}}。

相关主题