基于TCP协议用多线程实现并发服务器,实现思路、算法和demo
2017-07-18 20:46
561 查看
基本的思路:用主线程负责client的连接, 然后当有客户端来连接的时候,创建子进程。在子进程里面实现数据的接收。
先把一些要用的API的头文件都写进来。
[cpp] view
plain copy
#ifndef _MYHEAD_H_
#define _MYHEAD_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#define MYPORT 6667 //1024以下的是保留的端口号,用大于1024的;
//#define MYADDR "192.168.1.102" //把这行注释掉,可以在调用客户端程序时,输入服务器运行的IP来连接服务器 ./client 192.168.1.233
#endif
plain copy
#include "myhead.h"
void *read_msg(void *argc);
int main()
{
int ret = 0;
int socketfd = 0; //局部变量保存在栈空间,而栈空间是脏的==》里面还是保存的是上一次这个区域里面保存的值;
int clientfd = 0;
pid_t pid = 0;
pthread_t th = 0;
struct sockaddr_in sock_server = {0}; //变量类型保存在netinet/in.h里面的;
struct sockaddr_in sock_client = {0}; //保存连接的客户端那边的信息;
socklen_t len = sizeof(struct sockaddr);
//第一步:创建套接字;
socketfd = socket(AF_INET,SOCK_STREAM,0);
if(socketfd == -1) //入口检查
{
perror("socket"); //打印错误信息
return -1;
}
printf("socket success...\n"); //确保前面的代码是运行正确的;
//第二步:给套接字绑定必要的信息;
sock_server.sin_family = AF_INET; //给服务程序绑定地址族;
sock_server.sin_port = htons(MYPORT); //给服务器程序设定个端口号;
// sock_server.sin_addr.s_addr = inet_addr(MYADDR);//给服务程序绑定IP地址;
sock_server.sin_addr.s_addr = htonl(INADDR_ANY);//绑定任意ip地址;这样就能够实现像之前说的./client 192.168.1.233这样使用
ret = bind(socketfd,(struct sockaddr *)&sock_server,sizeof(struct sockaddr));
if(ret == -1)
{
perror("bind");
return -1;
}
printf("bind success..\n");
//第三步:listen监听!
ret = listen(socketfd,10);
if(ret == -1)
{
perror("listen");
return -1;
}
printf("listen success...\n");
//这里的第四步accept放到下面的while(1)里面,有许多client要连接到服务器中来
// clientfd = accept(socketfd,(struct sockaddr *)&sock_client,&len);
// if(clientfd == -1)
// {
// perror("accept");
// return -1;
// }
// printf("accept success...clinet fd = %d\n",clientfd);
while(1)
{
clientfd = accept(socketfd,(struct sockaddr *)&sock_client,&len);
if(clientfd == -1)
{
perror("accept");
return -1;
}
printf("accept success... clientfd = %d\n",clientfd); //客户端连接进来,
ret = pthread_create(&th,NULL,read_msg,&clientfd); //执行read_msg这个函数。传递clientfd,要知道从哪一个客户端来读取数据;
if(ret != 0)
{
perror("pthread_create");
return -1;
}
}
close(socketfd);
return 0;
}
void *read_msg(void *argc)
{
//argc首先是void类型的指针,经过(int *)argc强制转化为int *的指针,接下来要从argc所指向的地址里面取值;
//===>*((int *)argc);===>经过以上的操作就将pthread_create()里面传递的参数,赋值给fd;
int fd = *((int *)argc);
printf("fd = %d\n",fd); //验证fd是否等于main函数里面的cleintfd; 4
char recvbuff[20] = {0}; //用来保存接收的信息
int recvcnt = 0; //用来存recv函数的返回值
while(1)
{
bzero(recvbuff,sizeof(recvbuff)); //先清空recvbuff里面的内容。
recvcnt = read(fd,recvbuff,sizeof(recvbuff)); //从recvbuff中读取,sizeof(recvbuff)大小的内容到为文件描述符fd的文件中
if(recvcnt == -1)
{
perror("recv");
return NULL;
}
else if(recvcnt == 0)
{
printf("The Client is closed!\n");
break;
}
else
{
printf("Recv from Client %d bytes,data:%s\n",recvcnt,recvbuff); //打印接收到的信息
}
if(strcmp(recvbuff,"end") == 0)
{
break;
}
}
close(fd);
return NULL;
}
plain copy
#include "myhead.h"
//int main()
int main(int argc,char **argv)
{
//参数入口检查;
if(argc != 2) //usage: ./client 192.168.1.233
{
perror("argc");
return -1;
}
int socketfd = 0;
int ret = 0;
struct sockaddr_in sock_server = {0};
socketfd = socket(AF_INET,SOCK_STREAM,0); //第一步还是创建套接字
if(-1 == socketfd)
{
perror("socket");
return -1;
}
printf("socket success...\n");
//用sock_server提醒你们这边连接的是服务器端的IP地址和端口号;
sock_server.sin_family = AF_INET;
sock_server.sin_port = htons(MYPORT);
// sock_server.sin_addr.s_addr = inet_addr(MYADDR);
sock_server.sin_addr.s_addr = inet_addr(argv[1]);
ret = connect(socketfd,(struct sockaddr *)&sock_server,sizeof(struct sockaddr)); //连接服务器
if(ret == -1)
{
perror("connect");
return -1;
}
char sendbuff[20] = {0};
int sendcnt = 0;
while(1)
{
//第一步:提示客户输入要发送的数据;
printf("Please input a string:\n");
scanf("%s",sendbuff);
//第二步:调用send向套接字发送数据;
// sendcnt = send(socketfd,sendbuff,strlen(sendbuff),0);
sendcnt = write(socketfd,sendbuff,strlen(sendbuff));
if(sendcnt == -1)
{
perror("send");
return -1;
}
else
{
printf("Send to Server %d bytes,data:%s\n",sendcnt,sendbuff);
}
//第三步:判断发送的数据是否是end,如果是,就结束;
if(strcmp(sendbuff,"end") == 0)
{
close(socketfd);
break;
}
}
return 0;
}
分别编译服务器和客户端程序:
gcc server.c -o server
gcc client.c -o client
然后,先运行服务器./server,再运行客户端./client,客户端发消息给服务器,服务器回复,实现基本的一收一发的功能。
1.myhead.h
先把一些要用的API的头文件都写进来。[cpp] view
plain copy
#ifndef _MYHEAD_H_
#define _MYHEAD_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#define MYPORT 6667 //1024以下的是保留的端口号,用大于1024的;
//#define MYADDR "192.168.1.102" //把这行注释掉,可以在调用客户端程序时,输入服务器运行的IP来连接服务器 ./client 192.168.1.233
#endif
2.server.c
[cpp] viewplain copy
#include "myhead.h"
void *read_msg(void *argc);
int main()
{
int ret = 0;
int socketfd = 0; //局部变量保存在栈空间,而栈空间是脏的==》里面还是保存的是上一次这个区域里面保存的值;
int clientfd = 0;
pid_t pid = 0;
pthread_t th = 0;
struct sockaddr_in sock_server = {0}; //变量类型保存在netinet/in.h里面的;
struct sockaddr_in sock_client = {0}; //保存连接的客户端那边的信息;
socklen_t len = sizeof(struct sockaddr);
//第一步:创建套接字;
socketfd = socket(AF_INET,SOCK_STREAM,0);
if(socketfd == -1) //入口检查
{
perror("socket"); //打印错误信息
return -1;
}
printf("socket success...\n"); //确保前面的代码是运行正确的;
//第二步:给套接字绑定必要的信息;
sock_server.sin_family = AF_INET; //给服务程序绑定地址族;
sock_server.sin_port = htons(MYPORT); //给服务器程序设定个端口号;
// sock_server.sin_addr.s_addr = inet_addr(MYADDR);//给服务程序绑定IP地址;
sock_server.sin_addr.s_addr = htonl(INADDR_ANY);//绑定任意ip地址;这样就能够实现像之前说的./client 192.168.1.233这样使用
ret = bind(socketfd,(struct sockaddr *)&sock_server,sizeof(struct sockaddr));
if(ret == -1)
{
perror("bind");
return -1;
}
printf("bind success..\n");
//第三步:listen监听!
ret = listen(socketfd,10);
if(ret == -1)
{
perror("listen");
return -1;
}
printf("listen success...\n");
//这里的第四步accept放到下面的while(1)里面,有许多client要连接到服务器中来
// clientfd = accept(socketfd,(struct sockaddr *)&sock_client,&len);
// if(clientfd == -1)
// {
// perror("accept");
// return -1;
// }
// printf("accept success...clinet fd = %d\n",clientfd);
while(1)
{
clientfd = accept(socketfd,(struct sockaddr *)&sock_client,&len);
if(clientfd == -1)
{
perror("accept");
return -1;
}
printf("accept success... clientfd = %d\n",clientfd); //客户端连接进来,
ret = pthread_create(&th,NULL,read_msg,&clientfd); //执行read_msg这个函数。传递clientfd,要知道从哪一个客户端来读取数据;
if(ret != 0)
{
perror("pthread_create");
return -1;
}
}
close(socketfd);
return 0;
}
void *read_msg(void *argc)
{
//argc首先是void类型的指针,经过(int *)argc强制转化为int *的指针,接下来要从argc所指向的地址里面取值;
//===>*((int *)argc);===>经过以上的操作就将pthread_create()里面传递的参数,赋值给fd;
int fd = *((int *)argc);
printf("fd = %d\n",fd); //验证fd是否等于main函数里面的cleintfd; 4
char recvbuff[20] = {0}; //用来保存接收的信息
int recvcnt = 0; //用来存recv函数的返回值
while(1)
{
bzero(recvbuff,sizeof(recvbuff)); //先清空recvbuff里面的内容。
recvcnt = read(fd,recvbuff,sizeof(recvbuff)); //从recvbuff中读取,sizeof(recvbuff)大小的内容到为文件描述符fd的文件中
if(recvcnt == -1)
{
perror("recv");
return NULL;
}
else if(recvcnt == 0)
{
printf("The Client is closed!\n");
break;
}
else
{
printf("Recv from Client %d bytes,data:%s\n",recvcnt,recvbuff); //打印接收到的信息
}
if(strcmp(recvbuff,"end") == 0)
{
break;
}
}
close(fd);
return NULL;
}
3.client.c
[cpp] viewplain copy
#include "myhead.h"
//int main()
int main(int argc,char **argv)
{
//参数入口检查;
if(argc != 2) //usage: ./client 192.168.1.233
{
perror("argc");
return -1;
}
int socketfd = 0;
int ret = 0;
struct sockaddr_in sock_server = {0};
socketfd = socket(AF_INET,SOCK_STREAM,0); //第一步还是创建套接字
if(-1 == socketfd)
{
perror("socket");
return -1;
}
printf("socket success...\n");
//用sock_server提醒你们这边连接的是服务器端的IP地址和端口号;
sock_server.sin_family = AF_INET;
sock_server.sin_port = htons(MYPORT);
// sock_server.sin_addr.s_addr = inet_addr(MYADDR);
sock_server.sin_addr.s_addr = inet_addr(argv[1]);
ret = connect(socketfd,(struct sockaddr *)&sock_server,sizeof(struct sockaddr)); //连接服务器
if(ret == -1)
{
perror("connect");
return -1;
}
char sendbuff[20] = {0};
int sendcnt = 0;
while(1)
{
//第一步:提示客户输入要发送的数据;
printf("Please input a string:\n");
scanf("%s",sendbuff);
//第二步:调用send向套接字发送数据;
// sendcnt = send(socketfd,sendbuff,strlen(sendbuff),0);
sendcnt = write(socketfd,sendbuff,strlen(sendbuff));
if(sendcnt == -1)
{
perror("send");
return -1;
}
else
{
printf("Send to Server %d bytes,data:%s\n",sendcnt,sendbuff);
}
//第三步:判断发送的数据是否是end,如果是,就结束;
if(strcmp(sendbuff,"end") == 0)
{
close(socketfd);
break;
}
}
return 0;
}
分别编译服务器和客户端程序:
gcc server.c -o server
gcc client.c -o client
然后,先运行服务器./server,再运行客户端./client,客户端发消息给服务器,服务器回复,实现基本的一收一发的功能。
相关文章推荐
- 转: 构建基于Nginx的文件服务器思路与实现
- 【Java】基于TCP协议多线程服务器-客户端交互控制台聊天室简例
- 第八篇:并发回射服务器的最基本实现思路
- 基于python多线程实现Linux任务并发执行
- JAVA网络编程实现基于TCP协议的时间服务(向服务器查询当前时间)
- Unix C语言编写基于多线程的小型并发服务器
- 基于tcp/udp socket多线程并发实现对二进制协议的压力测试
- c++下基于windows socket的多线程服务器(基于TCP协议)
- QQ群红包的算法实现探讨(基于PHP demo)
- QQ群红包的算法实现探讨(基于PHP demo)
- 基于HTTP模拟实现静态服务器-多任务多线程
- 基于非阻塞socket的多线程服务器的实现------一个服务器如何与多个客户端进行通信?
- 并发回射服务器的最基本实现思路( fork )
- 基于.NET的分词软件设计与实现V1.0--总体思路及算法实现
- mahout demo——本质上是基于Hadoop的分步式算法实现,比如多节点的数据合并,数据排序,网路通信的效率,节点宕机重算,数据分步式存储
- 几种并发服务器模型的实现:多线程,多进程,select,poll,epoll
- 基于redis的高并发秒杀的JAVA-DEMO实现!
- lesson6 基于多线程的tcp并发服务器
- 实现TCP并发服务器之二(多线程)
- Android 网络:基于TCP协议通信,多线程,实现简单的C/S聊天室