这是一个通过多线程来实现可以接受多个客户端的TCP聊天程序。
//这是一个实现多线程TCP的聊天程序服务端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<sys/socket.h>
#include<pthread.h>
#include<netinet/in.h>
#include<arpa/inet.h>
void *thr_start(void *arg)
{
int clifd=(int)arg;
while(1)
{
char buff[1024]={0};
ssize_t ret=recv(clifd,buff,1024,0);
if(ret<0)
{
perror("recv error\n");
close(clifd);
break;
}
else if(ret==0)
{
printf("client shutdown\n");
}
printf("client say:%s\n",buff);
send(clifd,"over",4,0);
}
return NULL;
}
int create_worker(int clifd)
{
pthread_t tid;//创建一个线程来单独处理与某个客户端的通信
int ret=pthread_create(&tid,NULL,thr_start,(void*)clifd);
//pthread_create函数用来创建线程
//第一个参数是指向线程标识符的指针
//第二个参数是用来设置线程的属性默认为NULL
//第三个参数为创建出来的线程从哪里开始执行,我们这里创造出来的线程就直接去执行thr_start函数了
//第四个参数是运行第三个函数的参数
if(ret!=0)
{
printf("pthread_create error\n");
return -1;
}
return 0;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
printf("please input\n");
return -1;
}
int lstfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(lstfd<0)
{
printf("socket error\n");
return -1;
}
struct sockaddr_in lst_addr;
struct sockaddr_in cli_addr;
lst_addr.sin_family=AF_INET;
lst_addr.sin_port=htons(atoi(argv[2]));
lst_addr.sin_addr.s_addr=inet_addr(argv[1]);
socklen_t len=sizeof(struct sockaddr_in);
int ret=bind(lstfd,(struct sockaddr*)&lst_addr,len);
if(ret<0)
{
perror("bind error\n");
return -1;
}
if(listen(lstfd,5)<0)
{
perror("listen error\n");
return -1;
}
while(1)
{
int clifd=accept(lstfd,(struct sockaddr*)&cli_addr,&len);
if(clifd<0)
{
perror("accept error\n");
continue;
}
create_worker(clifd);
//接受到了socket就通过创建线程来实现
}
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/a1d756dcde3f78a170739203d0e1fb01.png)
![](https://i-blog.csdnimg.cn/blog_migrate/19e829922361e7774b2600041dcac79a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7531cba732512d937d527a82136c64f9.png)
使用起来、在客户端看来,多进程和多线程是没有什么区别的。甚至两个过程都是一样的, 以前我们调用accept函数之后,就开始操作accept返回的socket,而多进程中和多线程中不同的是,当我们接收到新的socket操作符之后,我们并不是直接操作而是调用创建进程或者创建线程的函数来说操作。多进程里调用fork函数然后通过判断返回值来让子进程执行接收消息的语句,多线程里边就不需要判断了,直接让创建出来的线程从pth_start函数的地址开始执行即可。大同小异。