利用多线程和TCP技术,实现客户端与服务端之间的通信

来源:互联网 发布:js同源策略有哪些 编辑:程序博客网 时间:2024/04/29 01:59

server.c

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include<pthread.h>struct ps{int st;pthread_t *thid;};pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//定义一个锁的变量int status = 0;//定义一个全局变量,用来标记客户端登陆//服务器端//同样有两个线程,一个向client端发送数据,一个向client端读取数据//此线程有一个重要解释://当客户端程序想要连接服务器端时,但它之前有客户端正连接,服务器程序往客户端程序发送了一个关闭客户端消息,客户端也会发送一个消息给服务器端//服务器端收到此消息后,会结束服务端的 recvsocket线程,并将全局标志位置0void * recvsocket(void * arg)//从client端往server端读取数据{struct ps *p = (struct ps *) arg;int st = p ->st;char buf[1024];while(1){memset(buf,0,sizeof(buf));int rc = recv(st,buf,sizeof(buf),0);if(rc <= 0 )break;printf("%s\n",buf);}pthread_mutex_lock(&mutex);//为全局变量加一个互斥锁,防止与线程函数同时读写变量的冲突status = 0;pthread_mutex_unlock(&mutex);pthread_cancel(*(p->thid));pthread_exit(0);}//但客户端退出后,服务端生成的 sendsocket线程,不会退出,会造成资源浪费。需要借助recvsocket线程的pthread_cancel函数将此线程杀死void * sendsocket(void * arg)//向client端写数据{int st = *(int *) arg;char s[1024];while(1){memset(s, 0, sizeof(s));read(STDIN_FILENO, s, sizeof(s));//从键盘读取用户输入信息send(st, s, strlen(s), 0);memset(s, 0, sizeof(s));}pthread_exit(0);}int main(){int st = socket(AF_INET,SOCK_STREAM,0);//初始化socketstruct sockaddr_in addr;//定义一个IP地址的结构addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = htonl(INADDR_ANY);int on = 1;if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){printf("setsockopt failed %s\n", strerror(errno));return EXIT_FAILURE;}//服务器端程序,需要使用bind将IP与server程序绑定if(bind(st,(struct sockaddr *) &addr,sizeof(addr)) == -1){printf("bind failed %s\n", strerror(errno));return 0;}if(listen(st,20) == -1){printf("listen failed %s\n", strerror(errno));return 0;}int client_st = 0;struct sockaddr_in client_addr;pthread_t thid1,thid2;while(1){memset(&client_addr,0,sizeof(client_addr));socklen_t len = sizeof(client_addr);//accept会阻塞,直到有客户端连接过来,accept返回client的socket描述符client_st = accept(st,(struct sockaddr *) &client_addr,&len);//阻塞调用pthread_mutex_lock(&mutex);//为全局变量加一个互斥锁,防止与线程函数同时读写变量的冲突status++;pthread_mutex_unlock(&mutex);if(status > 1)//若status大于1代表这是第二个socket连接,有两个客户端登陆连接了{close(client_st);//将第二个客户端套接口关闭,服务端程序向客户端程序发送一个信号,在客户端程序中recv值为0的消息会,客户端的接收信号的线程结束生命continue;//跳出此循环}if (client_st == -1){printf("accept failed %s\n", strerror(errno));return EXIT_FAILURE;}printf("accept by %s\n",inet_ntoa(client_addr.sin_addr));struct ps ps1;ps1.st = client_st;ps1.thid = &thid2;pthread_create(&thid1,NULL,recvsocket,&ps1);//向recvsocket线程只能传递一个参数,但可以写一个结构,实现了一次可以传多个参数的作用//结构的第一个参数仍为客户端的套接口,第二个参数为sendsocket线程的id号,目的是让recvsocket线程结束自己的生命pthread_create(&thid2,NULL,sendsocket,&client_st);pthread_detach(thid1);pthread_detach(thid2);}close(st);return 0;}

client.c

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include<pthread.h>//本程序实现客户端和服务器端之间相互通信,但服务器一次只能和一个客户端之间连接,若有其他客户端连接会直接挂死//使用多线程技术//客户端程序//出一个解释:当一个客户端想要连接服务器端时,此时在它之前正有一个客户端连接,//此时它就会被服务端程序杀死,并通过服务端send函数发送一个消息,在客户端recvsocket函数中recv一个值为0,并结束此线程void * recvsocket(void * arg)//接收client端socket数据的线程{int st = *(int *) arg;char buf[1024];memset(buf,0,sizeof(buf));while(1){int rc = recv(st,buf,sizeof(buf),0);if(rc <= 0)//如果recv返回小于等于0,代表socket已经关闭或者出错了break;printf("%s\n",buf);memset(buf, 0, sizeof(buf));}pthread_exit(0);}void * sendsocket(void * arg)//向client端socket发送数据的线程{int st = *(int *) arg;char buf[1024];memset(buf,0,sizeof(buf));while(1){read(STDIN_FILENO,buf,sizeof(buf));//从键盘中读取用户输入if(send(st,buf,strlen(buf),0) == -1)//发送buf的数据{printf("send failed %s\n",strerror(errno));return 0;}}pthread_exit(0);}int main(void){//初始化socketint st = socket(AF_INET,SOCK_STREAM,0);//定义一个IP地址的结构struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(8080);addr.sin_addr.s_addr = inet_addr("192.168.1.103");//客户端程序需要使用connect函数连接到结构addr指定的端口和IP地址上if(connect(st,(struct sockaddr * ) &addr,sizeof(addr)) == -1){printf("connect failed %s\n",strerror(errno));return 0;}//连接成功后就可以发送和接收数据了,但需要创建两个线程pthread_t thid1,thid2;pthread_create(&thid1,NULL,recvsocket,&st);pthread_create(&thid2,NULL,sendsocket,&st);pthread_detach(thid2);pthread_join(thid1, NULL);close(st); //关闭socketreturn EXIT_SUCCESS;}


0 0
原创粉丝点击