linux下基于TCP协议的多线程聊天室的搭建
2015-12-11 16:50
731 查看
文章是博主在学习unix网络编程一段时间之后,算是做的一个小的总结吧。希望能够给刚入门unix网络编程的同学学习和参考,当然博主也是学生一枚,更希望有大神批评指点。。。
博主首先先介绍一下多线程的概念:
线程是基于进程来说的,一个进程可以有多个线程,多个线程共享进程的资源。举一个例子,比如我们启动了qq程序,可以说是启动了一个进程,而你打开的多个聊天窗口就是基于qq这个进程的多个线程。多线程和多进程都能够使得代码并行,但是由于多进程开销比较大(fork子进程会复制父进程除了代码区之外的所有区域),而多线程会共享进程的资源,所以多线程在实际应用中很广泛。
下面来介绍几个线程的基本函数:
1、intpthread_create(pthread_t *tid,const pthread_attr_t*attr,void*(*func)(void*),void*arg),我想大家可以看得出这就是线程的创建函数。这个函数包含在头文件#include中,共有四个指针形参,所以线程的创建函数又叫做四针函数,其中第一个参数表示线程ID,第二个是线程属性,很多情况下给0就可以(好像又听说linux下只支持0,但不确定),第三个参数是一个函数指针,用于执行自己的线程函数,传入的就是自定义线程函数的函数指针。第四个参数的作用是我们可以传入一个我们需要的参数给线程函数,后面的程序中大家就会明白。
2、pthread_join(pthread_ttid,void**status),这个函数的作用是让一个线程等待另一个线程的结束。当我们不希望得到线程函数的返回值时,第二个参数给0.例如我们在线程A中写了pthread_join(B,0),就表示A线程要等到b线程的结束。类似于进程中的waitpid()函数。函数的第二个参数用于带出线程函数的返回值,我认为大家可以理解一下为什么是二级指针。当然,第一个参数表示线程ID。
3、pthread_self(),没有形参,函数的作用是取得自己的线程ID。
4、pthread_detach(pthread_ttid),函数的作用是讲一个线程设置为分离线程。分离线程是什么意思呢?由于线程在回收资源上是不确定的,就是说没有固定的说法说他什么时候回收。当一个线程是分离线程的时候,只要他结束,所有的资源都会释放。还有一种线程是pthread_join()过得线程,也会在执行这个函数之后释放所有资源,所以我们最好是把线程设置成这两种类型。形参表示线程ID。
5、pthread_exit(void*status),用于退出线程。
值得注意的是,这一块的函数的返回值基本都是返回错误码,并不是以前的错误返回-1什么的,错误码通过strerror()函数可以解析出错误的信息。
线程这块还有一些知识,比如线程同步,线程尺这些,由于没用到,就不细说了,下面我们先搭建聊天室的服务器端。
博主首先先介绍一下多线程的概念:
线程是基于进程来说的,一个进程可以有多个线程,多个线程共享进程的资源。举一个例子,比如我们启动了qq程序,可以说是启动了一个进程,而你打开的多个聊天窗口就是基于qq这个进程的多个线程。多线程和多进程都能够使得代码并行,但是由于多进程开销比较大(fork子进程会复制父进程除了代码区之外的所有区域),而多线程会共享进程的资源,所以多线程在实际应用中很广泛。
下面来介绍几个线程的基本函数:
1、intpthread_create(pthread_t *tid,const pthread_attr_t*attr,void*(*func)(void*),void*arg),我想大家可以看得出这就是线程的创建函数。这个函数包含在头文件#include中,共有四个指针形参,所以线程的创建函数又叫做四针函数,其中第一个参数表示线程ID,第二个是线程属性,很多情况下给0就可以(好像又听说linux下只支持0,但不确定),第三个参数是一个函数指针,用于执行自己的线程函数,传入的就是自定义线程函数的函数指针。第四个参数的作用是我们可以传入一个我们需要的参数给线程函数,后面的程序中大家就会明白。
2、pthread_join(pthread_ttid,void**status),这个函数的作用是让一个线程等待另一个线程的结束。当我们不希望得到线程函数的返回值时,第二个参数给0.例如我们在线程A中写了pthread_join(B,0),就表示A线程要等到b线程的结束。类似于进程中的waitpid()函数。函数的第二个参数用于带出线程函数的返回值,我认为大家可以理解一下为什么是二级指针。当然,第一个参数表示线程ID。
3、pthread_self(),没有形参,函数的作用是取得自己的线程ID。
4、pthread_detach(pthread_ttid),函数的作用是讲一个线程设置为分离线程。分离线程是什么意思呢?由于线程在回收资源上是不确定的,就是说没有固定的说法说他什么时候回收。当一个线程是分离线程的时候,只要他结束,所有的资源都会释放。还有一种线程是pthread_join()过得线程,也会在执行这个函数之后释放所有资源,所以我们最好是把线程设置成这两种类型。形参表示线程ID。
5、pthread_exit(void*status),用于退出线程。
值得注意的是,这一块的函数的返回值基本都是返回错误码,并不是以前的错误返回-1什么的,错误码通过strerror()函数可以解析出错误的信息。
线程这块还有一些知识,比如线程同步,线程尺这些,由于没用到,就不细说了,下面我们先搭建聊天室的服务器端。
/*基于TCP协议的多线程聊天室服务器端*/ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <signal.h> #include <string.h> #include <pthread.h> #include <arpa/inet.h> char *IP = "192.168.1.100"; //服务器IP号 short PORT = 5555; //通信端口号 int fd; /*客户端信息的结构*/ typedef struct client1{ char name[20]; int sock; } Client; Client client[100]; //能同时容纳100个客户上线 int size = 0; //存放当前在线客户端数目 void fa(int signo){ printf("服务器出现异常,正在退出...\n"); sleep(3); close(fd); printf("服务器已关闭\n"); exit(0); } /*线程函数*/ void* task(void* p); /*数据发送函数*/ void sendmsgtoALL(char* msg); void init(); void start(); void closeServe(); int main(){ signal(SIGINT,fa); init(); start(); closeServe(); } void init(){ printf("服务器正在初始化...\n"); sleep(3); /*创建socket描述符*/ fd = socket(AF_INET,SOCK_STREAM,0); if (fd==-1) perror("socket"),exit(-1); /*准备通信地址*/ struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IP); /*绑定*/ int res = bind(fd,(struct sockaddr*)&addr,sizeof(addr)); if(res==-1) perror("bind"),exit(-1); /*监听*/ listen(fd,100); printf("服务器初始化完毕!\n"); } void start(){ printf("服务器正在启动...\n"); sleep(3); printf("服务器启动成功!\n"); printf("等待客户端的连接....\n"); while(1){ /*创建连接上来的客户端通信地址*/ struct sockaddr_in from; socklen_t len = sizeof(from); int sockfd = accept(fd,(struct sockaddr*)&from,&len); if(sockfd == -1) perror("accept"),exit(-1); /*为连接上来的客户端开辟新的线程*/ client[size].sock = sockfd; pthread_t p_id; pthread_create(&p_id,0,task,(void*)&(client[size].sock)); } } void closeServe(){ printf("服务器正在关闭...\n"); sleep(3); close(fd); printf("服务器关闭成功!\n"); exit(0); } void* task(void* p){ int socktemp = *((int*)p); char name[20] = {}; //用于接收用户名 char buf[100] = {}; int i,temp; if(read(client[size].sock,name,sizeof(name))>0) strcpy(client[size].name,name); temp = size; size++; /*首次进入聊天室打印如下提示*/ char remind[100] = {}; sprintf(remind,"欢迎%s进入本聊天室..",client[size-1].name); sendmsgtoALL(remind); while(1){ if (read(socktemp,buf,sizeof(buf))<=0){ char remind1[100]={}; sprintf(remind1,"%s退出聊天室",client[temp].name); sendmsgtoALL(remind1); for(i=0;i<size;i++) if(client[i].sock == socktemp){ client[i].sock = 0; } return;//结束线程 } /*把消息发送给除了他自己的其他所有在线的客户端*/ char msg[100] = {}; sprintf(msg,"%s:%s",client[temp].name,buf); sendmsgtoALL(msg); memset(buf,0,strlen(buf)); } } /*基于TCP协议的多线程聊天室客户端口*/ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <signal.h> #include <pthread.h> #include <string.h> int fd; char name[50]; char *IP = "192.168.1.100"; short PORT = 5555; void init(); void start(); void* task(void* p); void fa(int signo){ printf("正在退出..."); sleep(1); close(fd); exit(0); } int main(){ signal(SIGINT,fa); //将信号2注册为自定义函数,用于退出 printf("正在启动...\n"); sleep(3); printf("启动成功,请输入用户名:"); scanf("%s",name); init(); write(fd,name,strlen(name)); start(); } void init(){ /*步骤和服务器端差不多*/ fd = socket(AF_INET,SOCK_STREAM,0); if (fd==-1) perror("socket"),exit(-1); /*创建通信地址*/ struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(IP); /*连接服务器*/ int res = connect(fd,(struct sockaddr*)&addr,sizeof(addr)); if(res == -1) perror("connect"),exit(-1); printf("连接服务器成功!\n"); } void start(){ /*客户端除了要给服务器发送数据外,还要接收服务器的数据 而这两个步骤是并行的,所以我们在主线程中写,开辟的线程中读*/ pthread_t pid; pthread_create(&pid,0,task,0); char msg[100] = {}; while(1){ scanf("%s",msg); write(fd,msg,strlen(msg)); /*必须把缓冲区清空*/ memset(msg,0,strlen(msg)); } } /*线程函数,用于读服务器发送过来的数据*/ void* task(void* p){ while(1){ char buf[100] = {}; if(read(fd,buf,sizeof(buf))<=0) return; printf("%s\n",buf); memset(buf,0,sizeof(buf)); } }聊天室搭建完毕,值得注意的是我们的运行平台是linux而不是windows。
相关文章推荐
- Linux socket 初步
- Python3写爬虫(四)多线程实现数据爬取
- 【CF 应用开发大赛】IT Share(IT分享网)
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- 基于 Linux 集群环境上 GPFS 的问题诊断