Linux下C语言多线程,网络通信简单聊天程序
- 格式:docx
- 大小:26.26 KB
- 文档页数:8
/** server.c** Created on: 2012-6-15* Author: root*/#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <sys/types.h>#include <error.h>#include<netinet/in.h>#define PORT 7999#define MAX_NUM 3 //client连接最大个数#define MAX_CLIENT 15#define MAX_SIZE 1024pthread_rwlock_t idx_lock, wait_lock;//client 信息typedef struct _client {int sockfd;char name[20];pthread_t pid;int flg;} c_client;c_client client[MAX_CLIENT];//定义client;//等待的clientstruct _client_ {int sockfd;char name[20];pthread_t pid;struct _client_ *next;};typedef struct _client_ c_client_c;c_client_c *head = NULL;c_client_c *temp_c1 = NULL, *temp_c2 = NULL;//等待的//初始化client信息void init_client() {int i = 0;for (i = 0; i < MAX_CLIENT; i++) {client[i].sockfd = -1;memset(client[i].name, 0, 20);client[i].pid = -1;client[i].flg = -1;}}//查找结构体数组中sockfd为-1的下标值int find_fd(c_client *client) {int i = 0;while (i < MAX_NUM) {// printf("====%d\n",client[i].sockfd);if (client[i].sockfd == -1)return i;i++;}return -1;}//判断登录格式int logform(char *buf) {char *p = strstr(buf, "LOGIN\r\n");int n = strlen(buf);char *q = p + n - 4;if (p != NULL && p + 7 != q && strcmp(q, "\r\n\r\n") == 0)return 1;elsereturn 0;}int cmpname(char *buf, c_client *p_client) {int i = 0;char *p = strtok(buf + 7, "\r\n\r\n");while (client[i].sockfd != -1 && client[i].sockfd != p_client->sockfd && i < MAX_NUM) {if (strcmp(client[i].name, p) == 0)return 0;i++;}return 1;}//SHOWvoid showuser(c_client *p_client) {int i = 0;char buf[1024] = { 0 };strcpy(buf, "200\r\n");for (i = 0; i < MAX_NUM; i++) {if (client[i].sockfd != -1) {sprintf(buf + strlen(buf), "%s\r\n", client[i].name);}}sprintf(buf + strlen(buf), "\r\n");send(p_client->sockfd, buf, strlen(buf), 0);}//ALLvoid sendto_all(c_client *p_client, char *buf) {int i = 0;char sendbuf[1024] = { 0 };sprintf(sendbuf, "AFROM\r\n%s\r\n%s", p_client->name, buf + 5);for (i = 0; i < MAX_NUM; i++) {if (client[i].sockfd != -1 && client[i].flg != -1)if(send(client[i].sockfd, sendbuf, strlen(sendbuf), 0) <= 0){printf("send errrrrr\n");exit(1);}}}int findname(char *name) {int i = 0;for (i = 0; i < MAX_NUM; i++) {if (client[i].sockfd != -1 && strcmp(client[i].name, name) == 0) return client[i].sockfd;}return 0;}//TOvoid sendto_one(c_client *p_client, char *buf) {int i = 0;char sendbuf[1024] = { 0 };char name[20] = { 0 };char *p = strtok(buf + 4, "\r\n");//TO\r\n:4个字符后取出\r\n前的名字strcpy(name, p);int sock = findname(name);if (!sock) {sprintf(sendbuf, "ERROR2\r\n%s用户不存在\r\n\r\n", name);send(p_client->sockfd, sendbuf, strlen(sendbuf), 0);} else {sprintf(sendbuf, "FROM\r\n%s\r\n%s", p_client->name, buf + 4 + strlen( name) + 2);if(send(sock, sendbuf, strlen(sendbuf), 0)<=0){printf("send errrrrr\n");exit(1);}}}void pthread_fun(void* cclient);//quitvoid quit(c_client *p_client){int i=0;int idx;char buf[1024] = {0};c_client_c *temp;printf("--%s退出聊天室\n",p_client->name);close(p_client->sockfd);p_client->sockfd = -1;p_client->pid = -1;p_client->flg = -1;sprintf(buf,"NOTICE1\r\n%s退出聊天室\r\n\r\n",p_client->name);memset(p_client->name,0,20);for(i=0;i<MAX_NUM;i++){if(client[i].sockfd != -1 && client[i].flg != -1)send(client[i].sockfd,buf,strlen(buf),0);}if(head != NULL && head->next != NULL){memset(buf,0,1024);pthread_rwlock_rdlock(&idx_lock);idx = find_fd(client);pthread_rwlock_unlock(&idx_lock);client[idx].sockfd = head->next->sockfd;pthread_rwlock_wrlock(&wait_lock);temp = head->next;head->next = head->next->next;free(temp);pthread_rwlock_unlock(&wait_lock);sprintf(buf,"NOTICE\r\n您已被唤醒,请继续操作\r\n\r\n");send(client[idx].sockfd,buf,strlen(buf),0);if (pthread_create(&client[idx].pid, NULL, (void *)pthread_fun,(void *) &client[idx]) != 0) {perror("pthread_create");exit(1);}pthread_detach(client[idx].pid);}}void pthread_fun(void* cclient) {c_client *p_client = (c_client *) cclient;char buf[MAX_SIZE] = { 0 };char sendbuf[1024] = { 0 };int i, n;char *p;sprintf(sendbuf, "%s", "NOTICE\r\n通讯通道开启\r\n\r\n");if (send(p_client->sockfd, sendbuf, strlen(sendbuf), 0) <= 0) {printf("send err\n");}memset(sendbuf, 0, 1024);while (1) {memset(buf, 0, MAX_SIZE);n = recv(p_client->sockfd, buf, sizeof(buf) - 1, MSG_NOSIGNAL);if (n <= 0) {close(p_client->sockfd);p_client->sockfd = -1;break;}if (logform(buf)) {if (cmpname(buf, p_client) == 0) {send(p_client->sockfd, "ERROR\r\n用户名重复\r\n\r\n", 26, 0);continue;} else {p_client->flg = 1;p = strtok(buf + 7, "\r\n\r\n");strcpy(p_client->name, p);sprintf(sendbuf, "100\r\n%s\r\n\r\n", p_client->name);send(p_client->sockfd, sendbuf, sizeof(sendbuf), 0);printf("%s进入聊天室\n", p_client->name);for (i = 0; i < MAX_NUM; i++) {if (client[i].sockfd != -1 && client[i].sockfd!= p_client->sockfd && client[i].flg != -1)send(client[i].sockfd, sendbuf, sizeof(sendbuf), 0);}memset(sendbuf, 0, 1024);while (1) {memset(buf, 0, MAX_SIZE);if ((n = recv(p_client->sockfd, buf, MAX_SIZE, 0)) <= 0) {perror("recv err");break;}// printf("recv=%s\n",buf);if ((p = strstr(buf, "\r\n\r\n")) != NULL && *(p + 4)== '\0') {if (!strncmp(buf, "SHOW\r\n\r\n", 8)) {showuser(p_client);//客户端执行show后,发送给客户端已连接的用户continue;}if (!strncmp(buf, "ALL\r\n", 5)) {sendto_all(p_client, buf);continue;}if (!strncmp(buf, "TO\r\n",4)) {sendto_one(p_client, buf);continue;}if (!strncmp(buf, "QUIT\r\n\r\n", 8))quit(p_client);// break;pthread_exit(NULL);} else {send(p_client->sockfd, "ERROR\r\n信息不符合协议要求\r\n\r\n",38, 0);}}}} else {send(p_client->sockfd, "ERROR\r\n未登录,请您登录再进行其他操作\r\n\r\n", 56, 0);}}pthread_exit(NULL);}int main() {int ser_sockfd, clt_sockfd;struct sockaddr_in addr;int idx;char buf[1024] = { 0 };// pthread_rwlock_t idx_lock,wait_lock;pthread_rwlock_init(&idx_lock, NULL);pthread_rwlock_init(&wait_lock, NULL);init_client();//创建服务器sockfdif ((ser_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {perror("socket");exit(1);}//设置服务器网络地址bzero(&addr, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(PORT);addr.sin_addr.s_addr = htonl(INADDR_ANY);//设置端口可重用int opt = 1;setsockopt(ser_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//将套接字绑定到服务器的网络地址上if (bind(ser_sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { perror("bind");exit(1);}printf("bind success\n");//监听连接请求--监听队列长度为10if (listen(ser_sockfd, 10) == -1) {perror("listen");exit(1);}printf("listen success\n");while (1) {if ((clt_sockfd = accept(ser_sockfd, NULL, NULL)) == -1) {perror("accept");exit(1);}pthread_rwlock_rdlock(&idx_lock);idx = find_fd(client);// printf("idx=%d\n",idx);pthread_rwlock_unlock(&idx_lock);if (idx != -1) { //连接末满client[idx].sockfd = clt_sockfd;if (pthread_create(&client[idx].pid, NULL,(void *) pthread_fun,(void *)&client[idx]) != 0) {perror("pthread_create");exit(1);}pthread_detach(client[idx].pid);} else { //连接已满,等待temp_c1 = (c_client_c *) malloc(sizeof(c_client_c));temp_c1->sockfd = clt_sockfd;temp_c1->next = NULL;pthread_rwlock_wrlock(&wait_lock);if (head == NULL) {head = (c_client_c *) malloc(sizeof(c_client_c));head->next = temp_c1;} else {for (temp_c2 = head; temp_c2->next != NULL; temp_c2= temp_c2->next);temp_c2->next = temp_c1;}pthread_rwlock_unlock(&wait_lock);memset(buf, 0, 1024);sprintf(buf, "NOTICE\r\n服务器已满,请等候\r\n\r\n");//客户端接受则等待if (send(temp_c1->sockfd, buf, strlen(buf), 0) <= 0) {printf("sendr err\n");}}}}/** client.c** Created on: 2012-6-18* Author: root*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <unistd.h>#include <sys/types.h>#include <pthread.h>#include <netinet/in.h>#include <error.h>#include <arpa/inet.h>#include <termios.h>#define MAX_SIZE 1024#define PORT 7999static int FLAGE = -1;char name[20] = {0};void fun_show(int sockfd){char sendbuf[256] = {0};sprintf(sendbuf,"SHOW\r\n\r\n");if(send(sockfd,sendbuf,strlen(sendbuf),0)<=0){ printf("send err\n");close(sockfd);exit(1);}}void fun_all(int sockfd){char sendbuf[MAX_SIZE] = {0};sprintf(sendbuf,"ALL\r\n",5);printf("输入发送的内容:\n");scanf("%s",sendbuf+5);sprintf(sendbuf+strlen(sendbuf),"\r\n\r\n");if(send(sockfd,sendbuf,strlen(sendbuf),0) <= 0){ printf("send err\n");close(sockfd);exit(1);}}void fun_one(int sockfd){char sendbuf[MAX_SIZE] = {0};char name3[20] = {0};printf("输入聊天对象:");scanf("%s",name3);sprintf(sendbuf,"TO\r\n%s\r\n",name3);printf("输入聊天内容:\n");scanf("%s",sendbuf+strlen(sendbuf));sprintf(sendbuf+strlen(sendbuf),"\r\n\r\n");if(send(sockfd,sendbuf,strlen(sendbuf),0) <= 0){printf("send err\n");close(sockfd);exit(1);}}void fun_quit(int sockfd){char sendbuf[256] = "QUIT\r\n\r\n";if(send(sockfd,sendbuf,strlen(sendbuf),0) <= 0){printf("send err\n");close(sockfd);exit(1);}}void *pthread_fun(int *sock){int sockfd = *sock;char recvbuf[1024] = {0};int n = 0;char *p,*q;char name2[20] = {0};while(1){memset(recvbuf,0,1024);n = recv(sockfd,recvbuf,sizeof(recvbuf),0);if(n<=0){printf("recv failed\n");exit(1);}if(!strncmp(recvbuf,"NOTICE\r\n通讯通道开启\r\n\r\n",30)){ printf("通讯通道开启\n");FLAGE=1;if (!strncmp(recvbuf, "100\r\n", 5)) {char *p = strtok(recvbuf + 5, "\r\n\r\n");//100\r\n:3个字符后取出\r\n\r\n前的名字strcpy(name2, p);printf("[NOTICE]%s进入聊天室\n", name2);FLAGE = 4;}if (!strncmp(recvbuf, "ERROR\r\n用户名重复\r\n\r\n", 26)) {printf("用户名重复\n");FLAGE = 3;}if (!strncmp(recvbuf, "200\r\n", 5)) {p = strtok(recvbuf+5,"\r\n\r\n");while(p != NULL){printf("%s\n",p);p = strtok(NULL,"\r\n\r\n");}printf("please input con:\n");}if(!strncmp(recvbuf,"AFROM\r\n",7)){// printf("recvbuf=%s\n",recvbuf);p = strtok(recvbuf+6,"\r\n");q = strtok(NULL,"\r\n\r\n");printf("(%s)[群聊]:%s\n",p, q);printf("please input con:\n");}if(!strncmp(recvbuf,"FROM\r\n",6)){p = strtok(recvbuf+6,"\r\n");q = strtok(NULL,"\r\n\r\n");printf("(%s)[私聊](%s):%s\n",p,name,q);printf("please input con:\n");}if(!strncmp(recvbuf,"ERROR2\r\n",8)){p = strtok(recvbuf + 8, "\r\n");printf("%s\n", p);}if(!strncmp(recvbuf,"NOTICE1\r\n",9)){p = strtok(recvbuf + 9, "\r\n");printf("用户%s\n", p);}if(!strncmp(recvbuf,"NOTICE\r\n服务器已满,请等候\r\n\r\n",37)){printf("服务器已满,请等候\n");FLAGE = 0;if(!strncmp(recvbuf,"NOTICE\r\n您已被唤醒,请继续操作\r\n\r\n",45)){ printf("你已经被唤醒,请继续操作\n");FLAGE = 2;}}}int main(int argc,char **argv){if(argc != 2){printf("input server ip:\n");exit(1);}pthread_t pid;int sockfd;struct sockaddr_in addr;char recvbuf[1024] = {0};char sendbuf[1024] = {0};int k=0;int n;char str[6] = {0};char *p,*q;if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){perror("socket");exit(1);}bzero(&addr,sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(PORT);if(inet_pton(AF_INET,argv[1],(void *)&addr.sin_addr) <= 0){perror("inet_pton");exit(1);}// inet_aton(argv[1], &addr.sin_addr);if(connect(sockfd,(struct sockaddr *)&addr,sizeof(addr)) == -1){ perror("connect");exit(1);}printf("connect success\n");// pid = fork();pthread_create(&pid,NULL,(void *)pthread_fun,(void*)&sockfd);pthread_detach(pid);// if(pid > 0){usleep(100);//让子进程先运行while(FLAGE==0){printf("服务器已满,wait...\n");sleep(1);}tcflush(0,TCIFLUSH);while(FLAGE==2){ //当FLAGE=2时正好父进程运行,则等待,FLAGE=1 usleep(100);}if(FLAGE == 1){while(k<3){//fflush(stdin);memset(name,0,20);printf("login:");scanf("%s",name);//fflush(stdin);sprintf(sendbuf,"LOGIN\r\n%s\r\n\r\n",name);send(sockfd,sendbuf,strlen(sendbuf),0);memset(sendbuf,0,1024);for(;;){if(FLAGE==3 || FLAGE==4)break;}if(FLAGE==3){k++;if(k==3){printf("输入过多,退出\n");}else{printf("还有%d次机会登录\n", 3 - k);}FLAGE=1;continue;}if(FLAGE==4){while(1){memset(str,0,6);scanf("%s",str);if(!strcmp(str,"show")){printf("显示在线用户:\n");fun_show(sockfd);continue;}else if(!strcmp(str,"all")){printf("群聊模式");fun_all(sockfd);continue;}else if(!strcmp(str,"to")){printf("私聊模式");fun_one(sockfd);continue;}else if(!strcmp(str,"quit")){printf("退出聊天室\n");fun_quit(sockfd);close(sockfd);return;}else if(!strcmp(str,"help")){printf("all[聊天内容] 群聊\n");printf("to[name][聊天内容] 私聊\n");printf("quit 退出程序\n");printf("help 显示帮助信息\n");printf("please input con:\n");continue;}else{printf("请输入help查看指令:\n");continue;}}}}}// }wait(NULL);}。
C语言实现的聊天室功能随着互联网的普及,聊天室作为一种社交交流方式逐渐受到人们的重视和喜爱。
在计算机编程领域,C语言作为一种广泛应用的编程语言,也能够实现聊天室的功能。
本文将介绍如何用C语言来实现聊天室功能,并分析其实现原理和相关技术。
一、聊天室功能简介聊天室是一种通过计算机网络进行在线沟通交流的工具。
不同于即时通讯软件,聊天室可以容纳更多的用户同时进行交流,形成一个开放的群体。
用户在聊天室中可以发送消息、分享文件、进行语音/视频通话等操作,实现多种形式的交流和互动。
二、C语言实现聊天室的原理实现聊天室功能涉及到网络编程、进程间通信和多线程等技术。
下面是C语言实现聊天室功能的一般步骤:1. 创建服务器端和客户端程序;2. 服务器端程序启动时建立一个监听socket;3. 客户端程序启动时创建一个socket,并向服务器端发送连接请求;4. 服务器端收到请求后,接受连接请求,并创建一个新的线程来处理客户端的请求;5. 客户端和服务器端通过socket实现数据的发送和接收;6. 服务器端可采用多线程的方式实现对多个客户端的并发处理;7. 客户端和服务器端通过消息队列、共享内存或信号量等方式进行进程间通信;8. 聊天室程序运行结束后,关闭socket和释放相关资源。
三、C语言实现聊天室的技术考虑在实现聊天室功能时,需要考虑以下技术问题:1. 网络协议:聊天室可以基于TCP或UDP协议来实现,需要选择合适的协议来保证消息的可靠传输或实现实时性要求。
2. 进程通信:聊天室中的客户端和服务端需要进行进程间通信,可以选择合适的通信方式,如消息队列、共享内存、信号量等。
3. 多线程编程:服务器端需要支持多个客户端的并发连接,可以通过多线程来实现并发处理。
4. 用户注册登录:聊天室需提供用户注册和登录功能,可将用户信息存储在数据库中,并进行身份验证。
5. 数据库管理:聊天室需要管理用户、消息等数据,可以使用关系型数据库或其他形式的数据存储和管理。
Linux网络编程:用C语言实现的聊天程序(同步通信)通过TCP协议,用C语言实现的同步聊天程序,注释写的比较详细,个人觉得对字符串处理比较充分,能够正常编译运行,拿出来和大家分享一下!1、客户端源代码:[cpp]view plaincopyprint?1.#include <stdio.h>2.#include <stdlib.h>3.#include <string.h>4.#include <errno.h>5.#include <sys/socket.h>6.#include <arpa/inet.h>7.#include <netinet/in.h>8.#include <sys/types.h>9.#include <unistd.h>10.11.#define BUFLEN 1012.13.int main(int argc, char **argv)14.{15.int sockfd;16. struct sockaddr_in s_addr;17. socklen_t len;18. unsigned int port;19.char buf[BUFLEN];20.21. /*建立socket*/22. if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){23. perror("socket");24. exit(errno);25. }else26. printf("socket create success!\n");27.28. /*设置服务器端口*/29. if(argv[2])30. port = atoi(argv[2]);31. else32. port = 4567;33. /*设置服务器ip*/34. bzero(&s_addr, sizeof(s_addr));35. s_addr.sin_family = AF_INET;36. s_addr.sin_port = htons(port);37. if (inet_aton(argv[1], (struct in_addr *)&s_addr.sin_addr.s_addr) == 0) {38. perror(argv[1]);39. exit(errno);40. }41. /*开始连接服务器*/42. if(connect(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr)) == -1){43. perror("connect");44. exit(errno);45. }else46. printf("conncet success!\n");47.48. while(1){49. /******接收消息*******/50. bzero(buf,BUFLEN);51. len = recv(sockfd,buf,BUFLEN,0);52. if(len > 0)53. printf("服务器发来的消息是:%s,共有字节数是: %d\n",buf,len);54. else{55. if(len < 0 )56. printf("接受消息失败!\n");57. else58. printf("服务器退出了,聊天终止!\n");59. break;60. }61. _retry:62. /******发送消息*******/63. bzero(buf,BUFLEN);64. printf("请输入发送给对方的消息:");65. /*fgets函数:从流中读取BUFLEN-1个字符*/66. fgets(buf,BUFLEN,stdin);67. /*打印发送的消息*/68. //fputs(buf,stdout);69. if(!strncasecmp(buf,"quit",4)){70. printf("client 请求终止聊天!\n");71. break;72. }73. /*如果输入的字符串只有"\n",即回车,那么请重新输入*/74. if(!strncmp(buf,"\n",1)){75. printf("输入的字符只有回车,这个是不正确的\n");76. goto _retry;77. }78. /*如果buf中含有'\n',那么要用strlen(buf)-1,去掉'\n'*/79. if(strchr(buf,'\n'))80. len = send(sockfd,buf,strlen(buf)-1,0);81. /*如果buf中没有'\n',则用buf的真正长度strlen(buf)*/82. else83. len = send(sockfd,buf,strlen(buf),0);84. if(len > 0)85. printf("消息发送成功,本次共发送的字节数是:%d\n",len);86. else{87. printf("消息发送失败!\n");88. break;89. }90. }91. /*关闭连接*/92. close(sockfd);93.94. return 0;95.}2、服务器源代码:[cpp]view plaincopyprint?1.#include <stdio.h>2.#include <stdlib.h>3.#include <string.h>4.#include <errno.h>5.#include <sys/socket.h>6.#include <arpa/inet.h>7.#include <netinet/in.h>8.#include <sys/types.h>9.#include <unistd.h>10.11.#define BUFLEN 1012.13.int main(int argc, char **argv)14.{15.int sockfd, newfd;16. struct sockaddr_in s_addr, c_addr;17.char buf[BUFLEN];18. socklen_t len;19. unsigned int port, listnum;20.21. /*建立socket*/22. if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){23. perror("socket");24. exit(errno);25. }else26. printf("socket create success!\n");27. /*设置服务器端口*/28. if(argv[2])29. port = atoi(argv[2]);30. else31. port = 4567;32. /*设置侦听队列长度*/33. if(argv[3])34. listnum = atoi(argv[3]);35. else36. listnum = 3;37. /*设置服务器ip*/38. bzero(&s_addr, sizeof(s_addr));39. s_addr.sin_family = AF_INET;40. s_addr.sin_port = htons(port);41. if(argv[1])42. s_addr.sin_addr.s_addr = inet_addr(argv[1]);43. else44. s_addr.sin_addr.s_addr = INADDR_ANY;45. /*把地址和端口帮定到套接字上*/46. if((bind(sockfd, (struct sockaddr*) &s_addr,sizeof(struct sockaddr))) == -1){47. perror("bind");48. exit(errno);49. }else50. printf("bind success!\n");51. /*侦听本地端口*/52. if(listen(sockfd,listnum) == -1){53. perror("listen");54. exit(errno);55. }else56. printf("the server is listening!\n");57. while(1){58. printf("*****************聊天开始***************\n");59. len = sizeof(struct sockaddr);60. if((newfd = accept(sockfd,(struct sockaddr*) &c_addr, &len)) == -1){61. perror("accept");62. exit(errno);63. }else64. printf("正在与您聊天的客户端是:%s: %d\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));65. while(1){66. _retry:67. /******发送消息*******/68. bzero(buf,BUFLEN);69. printf("请输入发送给对方的消息:");70. /*fgets函数:从流中读取BUFLEN-1个字符*/71. fgets(buf,BUFLEN,stdin);72. /*打印发送的消息*/73. //fputs(buf,stdout);74. if(!strncasecmp(buf,"quit",4)){75. printf("server 请求终止聊天!\n");76. break;77. }78. /*如果输入的字符串只有"\n",即回车,那么请重新输入*/79. if(!strncmp(buf,"\n",1)){80. printf("输入的字符只有回车,这个是不正确的\n");81. goto _retry;82. }83. /*如果buf中含有'\n',那么要用strlen(buf)-1,去掉'\n'*/84. if(strchr(buf,'\n'))85. len = send(newfd,buf,strlen(buf)-1,0);86. /*如果buf中没有'\n',则用buf的真正长度strlen(buf)*/87. else88. len = send(newfd,buf,strlen(buf),0);89. if(len > 0)90. printf("消息发送成功,本次共发送的字节数是:%d\n",len);91. else{92. printf("消息发送失败!\n");93. break;94. }95. /******接收消息*******/96. bzero(buf,BUFLEN);97. len = recv(newfd,buf,BUFLEN,0);98. if(len > 0)99. printf("客户端发来的信息是:%s,共有字节数是: %d\n",buf,len); 100. else{101. if(len < 0 )102. printf("接受消息失败!\n");103. else104. printf("客户端退出了,聊天终止!\n");105. break;106. }107. }108. /*关闭聊天的套接字*/109. close(newfd);110. /*是否退出服务器*/111. printf("服务器是否退出程序:y->是;n->否? ");112. bzero(buf, BUFLEN);113. fgets(buf,BUFLEN, stdin);114. if(!strncasecmp(buf,"y",1)){115. printf("server 退出!\n");116. break;117. }118. }119. /*关闭服务器的套接字*/120. close(sockfd);121. return 0;122.}3、编译源代码:new@new-desktop:~/linux/c$ gcc -Wall sync-client.c -o sync-clientnew@new-desktop:~/linux/c$ gcc -Wall sync-server.c -o sync-server4、运行服务器程序:new@new-desktop:~/linux/c$ ./sync-server 127.0.0.1 4567socket create success!bind success!the server is listening!*****************聊天开始***************正在与您聊天的客户端是:127.0.0.1: 48639请输入发送给对方的消息:client消息发送成功,本次共发送的字节数是:6客户端发来的信息是:server,共有字节数是: 6请输入发送给对方的消息:5、运行客户端程序:new@new-desktop:~/linux/c$ ./sync-client 127.0.0.1 4567socket create success!conncet success!服务器发来的消息是:client,共有字节数是: 6请输入发送给对方的消息:server消息发送成功,本次共发送的字节数是:6linux网络编程:用C语言实现的聊天程序(异步通信)本片文章,在上一篇:linux网络编程:用C语言实现的聊天程序(同步通信)的基础上,增加了IO复用的功能,实现了聊天程序的异步通讯!1、使用IO复用可以在等待的时候加入了超时的时间,如果等待的时间没有达到超时时间,那么该情况与阻塞的情况一致。
c语言线程间的通信
在C语言中,线程间的通信可以通过多种方式实现,包括以下几种常用方法:
1. 共享内存:通过在多个线程之间共享一块内存区域来进行数据通信。
可以使用标准库中的`shmget()`函数创建共享内存,然后使用`mmap()`函数将共享内存映射到各个线程的地址空间。
2. 信号量:通过信号量控制多个线程的访问权限,从而实现线程间的同步和互斥。
可以使用`sem_init()`函数初始化一个信号量,然后使用`sem_wait()`和`sem_post()`函数进行等待和释放操作。
3. 互斥锁:通过互斥锁保护共享资源的访问,从而实现线程间的互斥访问。
可以使用`pthread_mutex_init()`函数初始化一个互斥锁,然后使用`pthread_mutex_lock()`和`pthread_mutex_unlock()`函数对互斥锁进行加锁和解锁操作。
4. 条件变量:通过条件变量实现线程间的等待和唤醒操作,从而实现线程间的同步。
可以使用`pthread_cond_init()`函数初始化一个条件变量,然后使用`pthread_cond_wait()`和
`pthread_cond_signal()`函数进行等待和唤醒操作。
5. 管道:通过管道在多个线程之间传输数据。
可以使用
`pipe()`函数创建一个管道,然后使用`read()`和`write()`函数进行读写操作。
以上是常见的几种线程间通信的方法,具体选择哪种方法取决于具体的需求和场景。
用 C 实现多线程 Socket 的通信Socket 是一种基于 IP 协议的网络协议,它提供网络连接和数据传输服务。
在多线程编程中,可以使用 Socket 实现线程之间的通信。
本文将介绍如何用 C 语言实现多线程 Socket 的通信。
首先,需要引入以下头文件:```#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/socket.h>#include <netinet/in.h>#include <pthread.h>```其中,pthread.h 提供了用于多线程编程的函数和数据类型。
下面是一个简单的 Server 实现代码:```cvoid *server_thread(void *arg) {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};char *message = "Hello from server";int port = *(int*)arg;// 创建 Socketif ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("Socket failed");exit(EXIT_FAILURE);}// 设置 Socket 选项if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("setsockopt");exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(port);// 绑定 Socket 到端口号if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}// 监听来自客户端的连接请求if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}// 接受客户端连接if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}// 发送消息给客户端send(new_socket, message, strlen(message), 0);printf("Message sent to client\n");// 接收来自客户端的消息read(new_socket, buffer, 1024);printf("Message from client: %s\n", buffer);close(new_socket);close(server_fd);return NULL;}```上述代码中,server_thread 函数是一个 pthread_create 函数即可创建一个新的线程执行该函数:```cint main() {int port = 8080;pthread_t t_id;if (pthread_create(&t_id, NULL, server_thread, &port) != 0) { perror("pthread_create failed");exit(EXIT_FAILURE);}// 等待子线程结束pthread_join(t_id, NULL);return 0;}```当客户端连接到 Server 后,Server 发送一条消息给客户端,然后等待客户端发送消息过来。
Linux网络编程:用C语言实现的聊天程序(同步通信)通过TCP协议,用C语言实现的同步聊天程序,注释写的比较详细,个人觉得对字符串处理比较充分,能够正常编译运行,拿出来和大家分享一下!1、客户端源代码:[cpp]view plainc opyprint?1.#includ e <stdio.h>2.#includ e <stdlib.h>3.#includ e <string.h>4.#includ e <errno.h>5.#includ e <sys/socket.h>6.#includ e <arpa/inet.h>7.#includ e <netine t/in.h>8.#includ e <sys/types.h>9.#includ e <unistd.h>10.11.#define BUFLEN 1012.13.int main(int argc, char **argv)14.{15.int sockfd;16. struct sockad dr_in s_addr;17. sockle n_t len;18. unsign ed int port;19.char buf[BUFLEN];20.21. /*建立sock et*/22. if((sockfd = socket(AF_INE T, SOCK_S TREAM, 0)) == -1){23. perror("socket");24. exit(errno);25. }else26. printf("socket create succes s!\n");27.28. /*设置服务器端口*/29. if(argv[2])30. port = atoi(argv[2]);31. else32. port = 4567;33. /*设置服务器i p*/34. bzero(&s_addr, sizeof(s_addr));35. s_addr.sin_fa mily= AF_INE T;36. s_addr.sin_po rt = htons(port);37. if (inet_a ton(argv[1], (struct in_add r *)&s_addr.sin_ad dr.s_addr) == 0) {38. perror(argv[1]);39. exit(errno);40. }41. /*开始连接服务器*/42. if(connec t(sockfd,(struct sockad dr*)&s_addr,sizeof(struct sockad dr)) == -1){43. perror("connec t");44. exit(errno);45. }else46. printf("connce t succes s!\n");47.48. while(1){49. /******接收消息*******/50. bzero(buf,BUFLEN);51. len = recv(sockfd,buf,BUFLEN,0);52. if(len > 0)53. printf("服务器发来的消息是:%s,共有字节数是: %d\n",buf,len);54. else{55. if(len < 0 )56. printf("接受消息失败!\n");57. else58. printf("服务器退出了,聊天终止!\n");59. break;60. }61. _retry:62. /******发送消息*******/63. bzero(buf,BUFLEN);64. printf("请输入发送给对方的消息:");65. /*fgets函数:从流中读取B UFLEN-1个字符*/66. fgets(buf,BUFLEN,stdin);67. /*打印发送的消息*/68. //fputs(buf,stdout);69. if(!strnca secmp(buf,"quit",4)){70. printf("client请求终止聊天!\n");71. break;72. }73. /*如果输入的字符串只有"\n",即回车,那么请重新输入*/74. if(!strncm p(buf,"\n",1)){75. printf("输入的字符只有回车,这个是不正确的!!!\n");76. goto _retry;77. }78. /*如果buf中含有'\n',那么要用st rlen(buf)-1,去掉'\n'*/79. if(strchr(buf,'\n'))80. len = send(sockfd,buf,strlen(buf)-1,0);81. /*如果buf中没有'\n',则用buf的真正长度s trlen(buf)*/82. else83. len = send(sockfd,buf,strlen(buf),0);84. if(len > 0)85. printf("消息发送成功,本次共发送的字节数是:%d\n",len);86. else{87. printf("消息发送失败!\n");88. break;89. }90. }91. /*关闭连接*/92. close(sockfd);93.94. return 0;95.}2、服务器源代码:[cpp]view plainc opyprint?1.#includ e <stdio.h>2.#includ e <stdlib.h>3.#includ e <string.h>4.#includ e <errno.h>5.#includ e <sys/socket.h>6.#includ e <arpa/inet.h>7.#includ e <netine t/in.h>8.#includ e <sys/types.h>9.#includ e <unistd.h>10.11.#define BUFLEN 1012.13.int main(int argc, char **argv)14.{15.int sockfd, newfd;16. struct sockad dr_in s_addr, c_addr;17.char buf[BUFLEN];18. sockle n_t len;19. unsign ed int port, listnu m;20.21. /*建立sock et*/22. if((sockfd = socket(AF_INE T, SOCK_S TREAM, 0)) == -1){23. perror("socket");24. exit(errno);25. }else26. printf("socket create succes s!\n");27. /*设置服务器端口*/28. if(argv[2])29. port = atoi(argv[2]);30. else31. port = 4567;32. /*设置侦听队列长度*/33. if(argv[3])34. listnu m = atoi(argv[3]);35. else36. listnu m = 3;37. /*设置服务器i p*/38. bzero(&s_addr, sizeof(s_addr));39. s_addr.sin_fa mily= AF_INE T;40. s_addr.sin_po rt = htons(port);41. if(argv[1])42. s_addr.sin_ad dr.s_addr = inet_a ddr(argv[1]);43. else44. s_addr.sin_ad dr.s_addr = INADDR_ANY;45. /*把地址和端口帮定到套接字上*/46. if((bind(sockfd, (struct sockad dr*) &s_addr,sizeof(struct sockad dr))) == -1){47. perror("bind");48. exit(errno);49. }else50. printf("bind succes s!\n");51. /*侦听本地端口*/52. if(listen(sockfd,listnu m) == -1){53. perror("listen");54. exit(errno);55. }else56. printf("the server is listen ing!\n");57. while(1){58. printf("*****************聊天开始***************\n");59. len = sizeof(struct sockad dr);60. if((newfd= accept(sockfd,(struct sockad dr*) &c_addr, &len)) == -1){61. perror("accept");62. exit(errno);63. }else64. printf("正在与您聊天的客户端是:%s: %d\n",inet_n toa(c_addr.sin_ad dr),ntohs(c_addr.sin_po rt));65. while(1){66. _retry:67. /******发送消息*******/68. bzero(buf,BUFLEN);69. printf("请输入发送给对方的消息:");70. /*fgets函数:从流中读取B UFLEN-1个字符*/71. fgets(buf,BUFLEN,stdin);72. /*打印发送的消息*/73. //fputs(buf,stdout);74. if(!strnca secmp(buf,"quit",4)){75. printf("server请求终止聊天!\n");76. break;77. }78. /*如果输入的字符串只有"\n",即回车,那么请重新输入*/79. if(!strncm p(buf,"\n",1)){80. printf("输入的字符只有回车,这个是不正确的!!!\n");81. goto _retry;82. }83. /*如果buf中含有'\n',那么要用st rlen(buf)-1,去掉'\n'*/84. if(strchr(buf,'\n'))85. len = send(newfd,buf,strlen(buf)-1,0);86. /*如果buf中没有'\n',则用buf的真正长度s trlen(buf)*/87. else88. len = send(newfd,buf,strlen(buf),0);89. if(len > 0)90. printf("消息发送成功,本次共发送的字节数是:%d\n",len);91. else{92. printf("消息发送失败!\n");93. break;94. }95. /******接收消息*******/96. bzero(buf,BUFLEN);97. len = recv(newfd,buf,BUFLEN,0);98. if(len > 0)99. printf("客户端发来的信息是:%s,共有字节数是: %d\n",buf,len); 100. else{101. if(len < 0 )102. printf("接受消息失败!\n");103. else104. printf("客户端退出了,聊天终止!\n");105. break;106. }107. }108. /*关闭聊天的套接字*/109. close(newfd);110. /*是否退出服务器*/111. printf("服务器是否退出程序:y->是;n->否? ");112. bzero(buf, BUFLEN);113. fgets(buf,BUFLEN, stdin);114. if(!strnca secmp(buf,"y",1)){115. printf("server退出!\n");116. break;117. }118. }119. /*关闭服务器的套接字*/120. close(sockfd);121. return 0;122.}3、编译源代码:new@new-deskto p:~/linux/c$ gcc -Wall sync-client.c -o sync-clientnew@new-deskto p:~/linux/c$ gcc -Wall sync-server.c -o sync-server4、运行服务器程序:new@new-deskto p:~/linux/c$ ./sync-server 127.0.0.1 4567socket create succes s!bind success!the server is listening!*****************聊天开始***************正在与您聊天的客户端是:127.0.0.1: 48639请输入发送给对方的消息:client消息发送成功,本次共发送的字节数是:6客户端发来的信息是:server,共有字节数是: 6请输入发送给对方的消息:5、运行客户端程序:new@new-deskto p:~/linux/c$ ./sync-client 127.0.0.1 4567socket create succes s!connce t succes s!服务器发来的消息是:client,共有字节数是: 6请输入发送给对方的消息:server消息发送成功,本次共发送的字节数是:6linux网络编程:用C语言实现的聊天程序(异步通信)本片文章,在上一篇:linux网络编程:用C语言实现的聊天程序(同步通信)的基础上,增加了IO复用的功能,实现了聊天程序的异步通讯!1、使用IO复用可以在等待的时候加入了超时的时间,如果等待的时间没有达到超时时间,那么该情况与阻塞的情况一致。
C++实现简单的⽹络聊天程序1、什么是socket,socket在哪?Socket是应⽤层与 TCP/IP协议族通信的中间软件抽象层,它是⼀组接⼝。
在设计模式中,Socket其实就是⼀个门⾯模式,它把复杂的TCP/IP协议族隐藏在Socket接⼝后⾯,对⽤户来说,⼀组简单的接⼝就是全部,让Socket去组织数据,以符合指定的协议。
socket起源于Unix,⽽Unix/Linux基本哲学之⼀就是“⼀切皆⽂件”,都可以⽤“打开open –> 读写write/read –> 关闭close”模式来操作,Socket就是该模式的⼀个实现,socket即是⼀种特殊的⽂件,⼀些socket函数就是对其进⾏的操作(读/写、打开、关闭),这些函数我们在后⾯进⾏介绍。
有三种不同形式的套接字:流式套接字(SOCK_STREAM),数据包套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)。
基于TCP的Socket使⽤流式套接字,相⽐于使⽤数据包套接字的UDP来讲,TCP可以使程序员不必关⼼数据正确性及顺序正确性,缺点是效率较低。
基于TCP的Socket编程最常见的应⽤场景是在C/S架构下的分布式应⽤,针对客户端和服务器端提供不同的Socket系统调⽤。
2、client/server(CS)模式服务端:服务器端: 初始化 socket套接字----->绑定socket----->对端⼝进⾏监听(listen)----->阻塞(accept)----->等待客户端连接,⾄此程序运⾏到刚启动服务端的状态。
客户端:初始化 socket套接字------>发送连接请求(connect),如果连接成功,客户端发送数据请求,服务器接受请求并处理请求,把回应数据发送给客户端,客户端读取数据,最后关闭连接,完成⼀次交互。
3、实现代码及相关API介绍//服务器#include<iostream>#include<winsock.h>#pragma comment(lib,"ws2_32.lib")using namespace std;void initialization();int main() {//定义长度变量int send_len = 0;int recv_len = 0;int len = 0;//定义发送缓冲区和接受缓冲区char send_buf[100];char recv_buf[100];//定义服务端套接字,接受请求套接字SOCKET s_server;SOCKET s_accept;//服务端地址客户端地址SOCKADDR_IN server_addr;SOCKADDR_IN accept_addr;initialization();//填充服务端信息server_addr.sin_family = AF_INET;server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(1234);//创建套接字s_server = socket(AF_INET, SOCK_STREAM, 0);if (bind(s_server, (SOCKADDR*)& server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {cout << "套接字绑定失败!" << endl;WSACleanup();}else {cout << "套接字绑定成功!" << endl;}//设置套接字为监听状态if (listen(s_server, SOMAXCONN) < 0) {cout << "设置监听状态失败!" << endl;WSACleanup();}else {cout << "设置监听状态成功!" << endl;}cout << "服务端正在监听连接,请稍候...." << endl;//接受连接请求len = sizeof(SOCKADDR);s_accept = accept(s_server, (SOCKADDR*)& accept_addr, &len);if (s_accept == SOCKET_ERROR) {cout << "连接失败!" << endl;WSACleanup();return0;}cout << "连接建⽴,准备接受数据" << endl;//接收数据while (1) {recv_len = recv(s_accept, recv_buf, 100, 0);if (recv_len < 0) {cout << "接受失败!" << endl;break;}else {cout << "客户端信息:" << recv_buf << endl;}cout << "请输⼊回复信息:";cin >> send_buf;send_len = send(s_accept, send_buf, 100, 0);if (send_len < 0) {cout << "发送失败!" << endl;break;}}//关闭套接字closesocket(s_server);closesocket(s_accept);//释放DLL资源WSACleanup();return0;}void initialization() {//初始化套接字库WORD w_req = MAKEWORD(2, 2);//版本号WSADATA wsadata;int err;err = WSAStartup(w_req, &wsadata);if (err != 0) {cout << "初始化套接字库失败!" << endl;}else {cout << "初始化套接字库成功!" << endl;}//检测版本号if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {cout << "套接字库版本号不符!" << endl;WSACleanup();}else {cout << "套接字库版本正确!" << endl;}//填充服务端地址信息}//客户端#include<iostream>#include<winsock.h>#pragma comment(lib,"ws2_32.lib")using namespace std;void initialization();int main() {//定义长度变量int send_len = 0;int recv_len = 0;//定义发送缓冲区和接受缓冲区char send_buf[100];char recv_buf[100];//定义服务端套接字,接受请求套接字SOCKET s_server;//服务端地址客户端地址SOCKADDR_IN server_addr;initialization();//填充服务端信息server_addr.sin_family = AF_INET;server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");server_addr.sin_port = htons(1234);//创建套接字s_server = socket(AF_INET, SOCK_STREAM, 0);if (connect(s_server, (SOCKADDR*)& server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) { cout << "服务器连接失败!" << endl;WSACleanup();}else {cout << "服务器连接成功!" << endl;}//发送,接收数据while (1) {cout << "请输⼊发送信息:";cin >> send_buf;send_len = send(s_server, send_buf, 100, 0);if (send_len < 0) {cout << "发送失败!" << endl;break;}recv_len = recv(s_server, recv_buf, 100, 0);if (recv_len < 0) {cout << "接受失败!" << endl;break;}else {cout << "服务端信息:" << recv_buf << endl;}}//关闭套接字closesocket(s_server);//释放DLL资源WSACleanup();return0;}void initialization() {//初始化套接字库WORD w_req = MAKEWORD(2, 2);//版本号WSADATA wsadata;int err;err = WSAStartup(w_req, &wsadata);if (err != 0) {cout << "初始化套接字库失败!" << endl;}else {cout << "初始化套接字库成功!" << endl;}//检测版本号if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2){cout << "套接字库版本号不符!" << endl;WSACleanup();}else {cout << "套接字库版本正确!" << endl;}//填充服务端地址信息}linux下和c++中相关API介绍:1)socket()函数int socket(int domain, int type, int protocol);domain:即协议域,⼜称为协议族(family)。
Linux下c语⾔多线程编程引⾔ 线程(thread)技术早在60年代就被提出,但真正应⽤多线程到中去,是在80年代中期,solaris是这⽅⾯的佼佼者。
传统的Unix也⽀持线程的概念,但是在⼀个进程(process)中只允许有⼀个线程,这样多线程就意味着多进程。
现在,多 为什么有了进程的概念后,还要再引⼊线程呢?使⽤多线程到底有哪些好处?什么的系统应该选⽤多线程?我们⾸先必须回答这些问题。
使⽤多线程的理由之⼀是和进程相⽐,它是⼀种⾮常"节俭"的多任务操作⽅式。
我们知道,在Linux系统下,启动⼀个新的进程必须分配给它独⽴的地址空间,建⽴众多的数据表来维护它的代码段、堆栈段和数据段,这是⼀种"昂贵"的多任务⼯作⽅式。
⽽运⾏于⼀个进程中的多个线程,它们彼此之间使⽤相同的地址空间,共享⼤部分数据,启动⼀个线程所花费的空间远远⼩于启动⼀个进程所花费的空间,⽽且,线程间彼此切换所需的时间也远远⼩于进程间切换所需要的时间。
使⽤多线程的理由之⼆是线程间⽅便的机制。
对不同进程来说,它们具有独⽴的数据空间,要进⾏数据的传递只能通过通信的⽅式进⾏,这种⽅式不仅费时,⽽且很不⽅便。
线程则不然,由于同⼀进程下的线程之间共享数据空间,所以⼀个线程的数据可以直接为其它线程所⽤,这不仅快捷,⽽且⽅便。
当然,数据的共享也带来其他⼀些问题,有的变量不能同时被两个线程所修改,有的⼦程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地⽅。
除了以上所说的优点外,不和进程⽐较,多线程程序作为⼀种多任务、并发的⼯作⽅式,当然有以下的优点: 1) 提⾼应⽤程序响应。
这对图形界⾯的程序尤其有意义,当⼀个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应、、菜单的操作,⽽使⽤多线程技术,将耗时长的操作(time consuming)置于⼀个新的线程,可以避免这种尴尬的情况。
c语言线程间通信的几种方法C语言是一种广泛应用于系统开发和嵌入式设备的编程语言,线程间通信是多线程编程中非常重要的一个概念。
线程间通信是指多个线程之间通过共享的资源或特定的机制来进行信息交流和同步操作的过程。
在C语言中,有多种方法可以实现线程间通信,下面将介绍几种常见的方法。
1. 互斥锁(Mutex)互斥锁是一种最常用的线程同步机制,用于保护共享资源的访问。
它通过在关键代码段前后加锁和解锁操作,使得同一时间只有一个线程可以访问共享资源,其他线程则需要等待。
互斥锁可以使用pthread库中的pthread_mutex_init、pthread_mutex_lock和pthread_mutex_unlock等函数来实现。
2. 条件变量(Condition Variable)条件变量是一种线程间通信的机制,用于在某个条件满足时唤醒等待的线程。
当某个线程发现自己需要等待某个条件时,它可以使用pthread库中的pthread_cond_wait函数来阻塞自己,并释放互斥锁,当其他线程满足了条件后,可以使用pthread_cond_signal函数来唤醒等待的线程。
3. 信号量(Semaphore)信号量是一种用于控制多个线程对共享资源访问的机制。
它通过一个计数器来表示可用的资源数量,当资源数量不足时,线程需要等待,而当资源数量充足时,线程可以继续执行。
信号量可以使用pthread库中的sem_init、sem_wait和sem_post等函数来实现。
4. 管道(Pipe)管道是一种允许两个线程进行双向通信的机制。
在C语言中,可以使用pipe函数来创建一个管道,并使用read和write函数来进行读取和写入操作。
一个线程可以利用管道将数据发送给另一个线程,并且可以实现双向通信。
5. 共享内存(Shared Memory)共享内存是一种允许多个线程访问同一块内存区域的机制。
多个线程可以通过共享内存来进行数据交换和通信。
功能描述:程序应用多线程技术,可是实现1对N进行网络通信聊天。
但至今没想出合适的退出机制,除了用Ctr+C。
出于演示目的,这里采用UNIX域协议(文件系统套接字),程序分为客户端和服务端。
应用select函数来实现异步的读写操作。
先说一下服务端:首先先创建套接字,然后绑定,接下进入一个无限循环,用accept函数,接受“连接”请求,然后调用创建线程函数,创造新的线程,进入下一个循环。
这样每当有一个新的“连接”被接受都会创建一个新的线程,实现1对N的网络通信。
在服务端程序中线程中用一个buffer读写,为了避免错误,这时就要给关键代码加上互斥锁work_mutex,具体见代码。
服务端代码1#include<stdio.h>2#include<stdlib.h>3#include<string.h>4#include<pthread.h>5#include<sys/socket.h>6#include<sys/un.h>7#include<unistd.h>8#include<semaphore.h> //这里没有用二进制信号量可以删掉910char buffer[1024]; //读写用的区域11sem_t bin_sem; //没用到的二进制信号量,可以删掉12void *pthread_function(void *arg); //线程入口函数声明13pthread_mutex_t work_mutex; //声明互斥锁1415int main(){16int result; //整数变量用来储存调用函数的返回值17struct sockaddr_un server_address, client_address; //UNIX域的套接字,server_address用于服务端的监听,client_addr ess用于客户端连接后的套接字18int client_len; //连接后,accept函数会把客户端的地址的长度储存在这19int server_socketfd, client_socketfd;//服务端和客户端的套接字文件描述符20 pthread_t a_thread; //线程ID标志21 pthread_attr_t thread_attr; //线程的属性,后面可以看的,被我注释掉了,没用到,可以删掉。
2223 result = sem_init(&bin_sem, 0, 1); //初始化二进制信号量,因为用了互斥锁,所以没用到,可以删掉24if(result != 0){25 perror("sem_init");26 exit(EXIT_FAILURE);27 }2829 result = pthread_mutex_init(&work_mutex, NULL);//初始化互斥锁30if(result != 0){31 perror("pthread_mutex_init");32 exit(EXIT_FAILURE);33 }3435 server_socketfd = socket(AF_UNIX, SOCK_STREAM, 0);//创建套接字,用TCP连接方式,出于演示目的只用UNIX域套接字。
3637 server_address.sun_family = AF_UNIX;38 strcpy(server_address.sun_path, "server_socket"); 3940 unlink("server_socket"); //在绑定之前,把以前存在当前目录下的套接字删除4142 result = bind(server_socketfd, (struct sockaddr*)&server_address, sizeof(server_address)); //绑定43if(result != 0){44 perror("bind");45 exit(EXIT_FAILURE);46 }4748 result = listen(server_socketfd, 5);//监听,最多允许5个连接请求49if(result != 0){50 perror("listen");51 exit(EXIT_FAILURE);52 }5354 client_len = sizeof(client_address);55while(1){ //开始进入无限循环56/* printf("If you want to quit, please enter 'qui t'\n");57 printf("Do you want to accept a connectiong\n");58 memset(buffer, '\0', sizeof(buffer));59 fgets(buffer, sizeof(buffer), stdin);60 if((strncmp("quit", buffer, 4))==0) break; */6162 client_socketfd = accept(server_socketfd, (stru ct sockaddr*)&client_address, &client_len); //接受一个连接请求6364/* result = pthread_attr_init(&thread_attr);65 if(result != 0){66 perror("pthread_attr_init");67 exit(EXIT_FAILURE);68 }69 result = pthread_attr_setdetachstate(&thread_at tr, PTHREAD_CREATE_DETACHED);70 if(result != 0){71 perror("pthread_attr_setdetachstate");72 exit(EXIT_FAILURE);73 } */74 result = pthread_create(&a_thread, NULL, pthread _function, (void *)client_socketfd); //成功接受一个请求后,就会创建一个线程,然后主线程又进入accept函数,如果此时没有连接请求,那么主线程会阻塞75if(result != 0){76 perror("pthread_create");77 exit(EXIT_FAILURE);78 }7980 }81}8283void *pthread_function(void *arg){ //线程入口函数,每调用一次pthread_create,都会创建一个新的线程84int fd = (int) arg; //把函数参数,即连接成功后的套接字,赋给fd.85int result;86 fd_set read_fds; //文件描述符集合,用于select函数87int max_fds; //文件描述符集合的最大数8889 printf("%d id has connected!!\n", fd);90while (1){9192 FD_ZERO(&read_fds);//清空集合93 FD_SET(0, &read_fds);//将标准输入放入监听的文件描述符集合,这个用于读取标准输入,即键盘的输入94 FD_SET(fd, &read_fds);//将连接后的客户文件描述符放入监听的文件描述符集合,这个用于向客户端读取数据95 max_fds = fd + 1;9697// sem_wait(&bin_sem);98 pthread_mutex_lock(&work_mutex); //对关键区域上锁99 printf("%d has get the lock\n", fd);100 result = select(max_fds, &read_fds, (fd_set *)NU LL, (fd_set *)NULL, (struct timeval*)NULL); //开始监听那些文件描述符出于可读状态101if(result < 1){102 printf("select");103 }104if(FD_ISSET(0, &read_fds)){ //如果标准输入处于可读状态,说明键盘有所输入,将输入的数据存放在buffer中,然后向客户端写回,如果输入“quit”将会退出一个聊天线程105 memset(buffer, '\0', sizeof(buffer)); //保险起见,清零106 fgets(buffer, sizeof(buffer), stdin);107if((strncmp("quit", buffer, 4))==0){108 printf("You have terminaled the chat\n"); 109// sem_post(&bin_sem);110 pthread_mutex_unlock(&work_mutex);111break;112 }113else{114 result=write(fd, buffer, sizeof(buffer)); 115if(result==-1){116 perror("write");117 exit(EXIT_FAILURE);118 }119 }120 }121if(FD_ISSET(fd, &read_fds)){ //如果客户套接字符可读,那么读取存放在buffer中,然后显示出来,如果对方中断聊天,那么result==0122 memset(buffer, '\0', sizeof(buffer));123 result = read(fd, buffer, sizeof(buffer));124if(result == -1){125 perror("read");126 exit(EXIT_FAILURE);127 }128else if(result == 0){129 printf("The other side has terminal the c hat\n");130// sem_post(&bin_sem);131 pthread_mutex_unlock(&work_mutex);132break;133 }134else{135 printf("receive message: %s", buffer);136 }137 }138 pthread_mutex_unlock(&work_mutex); //解锁139 sleep (1); //如果没有这一行,当前线程会一直占据buffer.让当前线程暂停一秒可以实现1对N的功能。