linux下运行的多线程socket_tcp
多线程服务端:
//本文件是多线程并发服务器的代码 #include <netinet/in.h> // for sockaddr_in #include <sys/types.h> // for socket #include <sys/socket.h> // for socket #include <stdio.h> // for printf #include <stdlib.h> // for exit #include <string.h> // for bzero #include <pthread.h> #include <sys/errno.h> // for errno #include <unistd.h> //define close #define SERVER_IP "10.137.213.68" #define SERVER_PORT 10005 #define LENGTH_OF_LISTEN_QUEUE 20 #define BUFFER_SIZE 1024 #define THREAD_MAX 5 void* talk_to_client(void *data) { // 分离线程, 线程结束后会自动释放资源 pthread_detach(pthread_self()); int new_server_socket = (int)data; char SendBuff[BUFFER_SIZE]; char RecvBuff[BUFFER_SIZE]; while (1) { //接收客户端发送来的信息到buffer中 int length = recv(new_server_socket, RecvBuff, BUFFER_SIZE, 0); if(length < 0) { printf("\nServer Recieve Data Failed!\n"); exit(1); } else { printf("\nReceive from client, Socket Num: %d, message: %s\n",new_server_socket, RecvBuff); } if ((strcmp(RecvBuff, "quit") == 0) || (strcmp(RecvBuff, "Quit") == 0)) { strcpy(SendBuff, "Goodbye,my dear client!"); } else { strcpy(SendBuff, "Hello Client!\n"); } //发送SendBuff中的字符串到new_server_socket,实际是给客户端, 注意字符串结束符 if(-1 == send(new_server_socket, SendBuff, strlen(SendBuff) + sizeof(char), 0)) { printf("Send error!\n"); } else { printf("Send to client:%s\n", SendBuff); } if ((strcmp(RecvBuff, "quit") == 0) || (strcmp(RecvBuff, "Quit") == 0)) { break; } memset(RecvBuff, 0, BUFFER_SIZE); memset(SendBuff, 0, BUFFER_SIZE); } // 关闭与客户端的连接 close(new_server_socket); printf("Thread Exit!"); // 结束线程 pthread_exit(NULL); } int main(int argc, char **argv) { //设置一个socket地址结构serverAddr,代表服务器internet地址, 端口 struct sockaddr_in serverAddr; memset(&serverAddr, 0, sizeof(serverAddr)); //把一段内存区的内容全部设置为0 serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP); serverAddr.sin_port = htons(SERVER_PORT); //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket int serverSocket = socket(AF_INET,SOCK_STREAM,0); if (-1 == serverSocket) { printf("Create Socket Failed!"); exit(1); } printf("socket create successfully.\n"); //把socket和socket地址结构绑定 if(bind(serverSocket,(struct sockaddr*)&serverAddr,sizeof(struct sockaddr))) { printf("Bind error.IP[%s], Port[%d]\n", SERVER_IP, serverAddr.sin_port); exit(1); } printf("Bind successful.IP[%s], Port[%d]\n", SERVER_IP, serverAddr.sin_port); //server_socket用于监听 if (listen(serverSocket, LENGTH_OF_LISTEN_QUEUE) ) { printf("Listen error!\n"); exit(1); } printf("Listening on port[%d]\n", serverAddr.sin_port); while(1) //服务器端要一直运行 { //定义客户端的socket地址结构clientAddr struct sockaddr_in clientAddr; int ilength = sizeof(clientAddr); //接受一个到serverSocket代表的socket的一个连接 //如果没有连接请求,就等待到有连接请求--这是accept函数的特性(阻塞) //accept函数返回一个新的socket,这个socket(new_server_socket)用于同连接到的客户的通信 //new_server_socket代表了服务器和客户端之间的一个通信通道 //accept函数把连接到的客户端信息填写到客户端的socket地址结构clientAddr中 int new_server_socket = accept(serverSocket,(struct sockaddr*)&clientAddr,&ilength); printf("\nNew client touched. Socket Num: %d\n", new_server_socket); if (-1 == new_server_socket) { printf("Server Accept Failed!\n"); break; } void* threadReturn; pthread_t child_thread; // 新建一个线程 // 第二个参数为NULL则默认为PTHREAD_CREATE_JOINABLE状态,需要手动释放线程资源 if(pthread_create(&child_thread, NULL, talk_to_client, (void *)new_server_socket) < 0) { printf("pthread_create Failed : %s\n",strerror(errno)); } printf("Create Thread Success!\n"); } //关闭监听用的socket close(serverSocket); return 0; }
makefile:
multiserver_tcp:multiserver_tcp.o
gcc -o multiserver_tcp multiserver_tcp.o -lpthread
multiserver_tcp.o:multiserver_tcp.c
gcc -c multiserver_tcp.c
clean:
rm -rf multiserver_tcp multiserver_tcp.o
单线程客户端:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <errno.h> #include <string.h> #include <unistd.h> #define CLTIP "10.137.213.68" #define SRVPORT 10005 #define MAX_NUM 100 /************************** 客户端流程: 1.创建clientSocket 2.初始化 serverAddr 3.链接到服务器 connect 4.利用send和recv进行读写操作 5.关闭clientSocket **************************/ int main() { // 延迟1s //sleep(1000); // socket() 为通讯创建一个端点,为套接字返回一个文件描述符 int clientsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(clientsock < 0) { printf("socket creation failed\n"); exit(-1); } printf("socket create successfully.\n"); struct sockaddr_in clientAddr; clientAddr.sin_family = AF_INET; clientAddr.sin_port = htons((u_short)SRVPORT); clientAddr.sin_addr.s_addr = inet_addr(CLTIP); if(connect(clientsock, (struct sockaddr*)&clientAddr, sizeof(struct sockaddr)) < 0) { printf("Connect error.IP[%s], port[%d]\n", CLTIP, clientAddr.sin_port); exit(-1); } printf("Connect to IP[%s], port[%d]\n", CLTIP, clientAddr.sin_port); char sendBuf[MAX_NUM]={0}; char recvBuf[MAX_NUM]={0}; while(gets(sendBuf) != '\0') { if(send(clientsock, sendBuf, strlen(sendBuf) + sizeof(char), 0) == -1) { printf("send error!\n"); } printf("send to server:%s\n", sendBuf); memset(sendBuf, 0, sizeof(sendBuf)); if(-1 == recv(clientsock, recvBuf, MAX_NUM, 0)) { printf("rev error!\n"); } printf("receive from server:%s\n", recvBuf); if(0 == strcmp(recvBuf, "Goodbye,my dear client!")) { break; } memset(recvBuf, 0, sizeof(recvBuf)); } close(clientsock); return 0; }
makefile:
client_tcp:client_tcp.o
gcc client_tcp.o -o client_tcp
client_tcp.o:client_tcp.c
gcc -c client_tcp.c
clean:
rm -rf client_tcp client_tcp.o
线程基本操作:
pthread_create 函数:
所需文件头: |
#include <pthread.h> |
函数原型: |
int pthread_create((pthread_t*thread,pthread_attr_r*attr,void*(*start_routine) |
函数传入值: |
thread:线程标识符 |
|
attr: 线程属性设置 null表示采用默认 |
|
start_roitine : 线程函数的启示地址 |
|
arg :传递给start_routine的参数 |
函数返回值: |
成功:0
|
pthread_exit函数
所需文件头: |
#include <pthread.h> |
函数原型: |
Void pthread_exit(void *retval) |
函数传入值: |
retval:调用者线程的返回值,可由其他函数如pthread_join来检索获取。
|
关于pthread_join():
pthread_join函数
所需文件头: |
#include <pthread.h> |
函数原型: |
int pthread_join ((pthread_t th,void **thread_return)) |
函数传入值: |
th: 等待线程的标识符 |
函数返回值: |
成功:0 |
|
出错:-1
|
在任何一个时间点上,线程是可结合的(joinable),或者是分离的(detached)。一个可结合的线程能够被其他线程收回其资源和杀死;在被其他 线程回收之前,它的存储器资源(如栈)是不释放的。相反,一个分离的线程是不能被其他线程回收或杀死的,它的存储器资源在它终止时由系统自动释放。
线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当 pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的需要,选择适当的分离状态。所以如果我们在创建线程时就知道不需要了解线程的终止状态,则可以pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启动。
设置线程分离状态的函数为pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是 在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
另外一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。
需要注意的是一个线程仅允许唯一的一个线程使用 pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。
相关1:pthread_join是为了防止主线程没有给其他线程执行的时间就返回了而设计的,
pthread_join(thread_t th,void ** thread_return )是使主线程等待th线程运行结束再运行
相关2:有时候主线程创建子线程后,如果不使用pthread_join将自己阻塞,自己会先退出而程序结束,
这样子线程的运行可能无法执行完毕就退出了,这也算是要使用pthread_join的一个场景吧。
相关3:pthread_join应该是用来回收线程资源的,当线程结束时调用,在一个程序中一直创建线程,而在
线程结束时又没有用pthread_join则会造成资源不足,无法继续创建线程的情况.也就是内存泄漏。
解决办法:
1./创建线程前设置 PTHREAD_CREATE_DETACHED 属性
2.当线程为joinable时,使用pthread_join来获取线程返回值,并释放资源。
3.当线程为joinable时,也可在线程中调用 pthread_detach(pthread_self());来分离自己。
相关4:pthread_join回收线程资源,在pthread_create后父进程就可调用此函数,不过会阻塞父进程直到子进程结束。
pthread_join()不会阻塞其他子进程。
可以设置线程属性自动回收资源,就不用调用pthread_join了。
相关5:
示例:
#include<stdlib.h> #include<stdio.h> #include<errno.h> #include<pthread.h> staticvoid pthread_func_1 (void); staticvoid pthread_func_2 (void); int main (int argc,char** argv) { pthread_t pt_1 =0; pthread_t pt_2 =0; pthread_attr_t atrr ={0}; int ret =0; /*初始化属性线程属性*/ pthread_attr_init (&attr); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); ret = pthread_create (&pt_1,&attr, pthread_func_1, NULL); if(ret !=0) { perror ("pthread_1_create"); }
ret = pthread_create (&pt_2, NULL, pthread_func_2, NULL); if(ret !=0) { perror ("pthread_2_create"); } pthread_join (pt_2, NULL); return0; } staticvoid pthread_func_1 (void) { int i =0; for(; i <6; i++) { printf ("This is pthread_1.\n"); if(i ==2) { pthread_exit (0); } } return; } staticvoid pthread_func_2 (void) { int i =0; for(; i <3; i ++) { printf ("This is pthread_2.\n"); } return; }
线程属性相关:
Posix线程中的线程属性pthread_attr_t主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。