1.如果只调用一次accept,就只能接受一个客户端的连接
2.如果将accept放到while循环内部,则进行一次收发数据的时候,需要阻塞等待新连接的到来
这两种情况都无法处理多个客户端的情况
原则:
多进程:
父进程进行获取连接
子进程进行收发数据,每来一个新链接创建一个新的进程处理
多线程
主线程进行获取连接
每来一个新连接,则创建一个新的线程进行处理
多进程版本
//tcpsvr.hpp
#pragma once
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
class TcpSvr
{
public:
TcpSvr()
{
sockfd_ = -1;
}
~TcpSvr()
{
}
//创建套接字
bool CreateSocket()
{
sockfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd_ < 0)
{
perror("socket");
return false;
}
return true;
}
//绑定地址信息
bool Bind(std::string& ip, uint16_t port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0)
{
perror("bind");
return false;
}
return true;
}
//侦听
bool Listen(int backlog = 5)
{
int ret = listen(sockfd_, backlog);
if(ret < 0)
{
perror("listen");
return false;
}
return true;
}
//获取连接
//bool Accept(struct sockaddr_in* peeraddr, int* sockfd)
//peeraddr:出参,返回客户端的地址信息
//ts:出参,返回一个TcpSvr类的实例化指针,在这个类的实例化指针当中保存新创建出来的套接字描述符,上层调用者可以使用返回的类的实例化指针和客户端进行通信
bool Accept(struct sockaddr_in* peeraddr, TcpSvr* ts)
{
socklen_t addrlen = sizeof(struct sockaddr_in);
int serverfd = accept(sockfd_, (struct sockaddr*)peeraddr, &addrlen);
if(serverfd < 0)
{
perror("accept");
return false;
}
ts->sockfd_ = serverfd;
return true;
}
//发起连接(client)
bool Connect(std::string& ip, uint16_t port)
{
struct sockaddr_in destaddr;
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(port);
destaddr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = connect(sockfd_, (struct sockaddr*)&destaddr, sizeof(destaddr));
if(ret < 0)
{
perror("connect");
return false;
}
return true;
}
//发送数据
bool Send(std::string& data)
{
int sendsize = send(sockfd_, data.c_str(), data.size(), 0);
if(sendsize < 0)
{
perror("send");
return false;
}
return true;
}
//接收数据
//data:是一个出参,将接收到的数据返回给调用者
bool Recv(std::string* data)
{
char buf[1024] = {0};
int recvsize = recv(sockfd_, buf, sizeof(buf) - 1, 0);
if(recvsize < 0)
{
perror("recv");
return false;
}
else if(recvsize == 0)
{
printf("peer shutdown connect\n");
return false;
}
(*data).assign(buf, recvsize);
return true;
}
//关闭套接字
void Close()
{
close(sockfd_);
sockfd_ = -1;
}
private:
int sockfd_;
};
//svr.cpp
#include "tcpsvr.hpp"
#include <signal.h>
#include <sys/wait.h>
//自定义信号处理函数,子进程退出回收资源
void sigcb(int signo)
{
(void)signo;
while(1)
{
waitpid(-1, NULL, WNOHANG);
}
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("start server: ./svr [ip] [port]\n");
return 0;
}
signal(SIGCHLD, sigcb);
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if(!ts.CreateSocket())
{
return 0;
}
if(!ts.Bind(ip, port))
{
return 0;
}
if(!ts.Listen())
{
return 0;
}
while(1)
{
TcpSvr newts;
struct sockaddr_in peeraddr;
if(!ts.Accept(&peeraddr, &newts))
{
continue;
}
printf("Have a new connection ip=%s, port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
int pid = fork();
if(pid < 0)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
//child
while(1)
{
std::string buf;
newts.Recv(&buf);
printf("client did say: %s\n", buf.c_str());
printf("please return msg to client:");
fflush(stdout);
std::cin >> buf;
newts.Send(buf);
}
newts.Close();
exit(1);
}
else
{
//father
newts.Close();
}
}
return 0;
}
多线程版本
//tcpsvr.hpp
#pragma once
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
class TcpSvr
{
public:
TcpSvr()
{
sockfd_ = -1;
}
~TcpSvr()
{
}
//创建套接字
bool CreateSocket()
{
sockfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sockfd_ < 0)
{
perror("socket");
return false;
}
return true;
}
//绑定地址信息
bool Bind(std::string& ip, uint16_t port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr));
if(ret < 0)
{
perror("bind");
return false;
}
return true;
}
//侦听
bool Listen(int backlog = 5)
{
int ret = listen(sockfd_, backlog);
if(ret < 0)
{
perror("listen");
return false;
}
return true;
}
//获取连接
//bool Accept(struct sockaddr_in* peeraddr, int* sockfd)
//peeraddr:出参,返回客户端的地址信息
//ts:出参,返回一个TcpSvr类的实例化指针,在这个类的实例化指针当中保存新创建出来的套接字描述符,上层调用者可以使用返回的类的实例化指针和客户端进行通信
bool Accept(struct sockaddr_in* peeraddr, TcpSvr* ts)
{
socklen_t addrlen = sizeof(struct sockaddr_in);
int serverfd = accept(sockfd_, (struct sockaddr*)peeraddr, &addrlen);
if(serverfd < 0)
{
perror("accept");
return false;
}
ts->sockfd_ = serverfd;
return true;
}
//发起连接(client)
bool Connect(std::string& ip, uint16_t port)
{
struct sockaddr_in destaddr;
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(port);
destaddr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = connect(sockfd_, (struct sockaddr*)&destaddr, sizeof(destaddr));
if(ret < 0)
{
perror("connect");
return false;
}
return true;
}
//发送数据
bool Send(std::string& data)
{
int sendsize = send(sockfd_, data.c_str(), data.size(), 0);
if(sendsize < 0)
{
perror("send");
return false;
}
return true;
}
//接收数据
//data:是一个出参,将接收到的数据返回给调用者
bool Recv(std::string* data)
{
char buf[1024] = {0};
int recvsize = recv(sockfd_, buf, sizeof(buf) - 1, 0);
if(recvsize < 0)
{
perror("recv");
return false;
}
else if(recvsize == 0)
{
printf("peer shutdown connect\n");
return false;
}
(*data).assign(buf, recvsize);
return true;
}
//关闭套接字
void Close()
{
close(sockfd_);
sockfd_ = -1;
}
private:
int sockfd_;
};
//svr.cpp
#include "tcpsvr.hpp"
#include <pthread.h>
void* ThreadStart(void* arg)//线程入口函数
{
pthread_detach(pthread_self());
TcpSvr* ts = (TcpSvr*)arg;
while(1)
{
std::string buf;
ts->Recv(&buf);
printf("client did say: %s\n", buf.c_str());
printf("please return msg to client: ");
fflush(stdout);
std::cin >> buf;
ts->Send(buf);
}
ts->Close();
delete ts;
}
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("start server: ./svr [ip] [port]\n");
return 0;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if(!ts.CreateSocket())
{
return 0;
}
if(!ts.Bind(ip, port))
{
return 0;
}
if(!ts.Listen())
{
return 0;
}
while(1)
{
TcpSvr* newts = new TcpSvr();
struct sockaddr_in peeraddr;
if(!ts.Accept(&peeraddr, newts))
{
continue;
}
printf("Have a new connection ip=%s, port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
pthread_t tid;
int ret = pthread_create(&tid, NULL, ThreadStart, (void*)newts);
if(ret < 0)
{
perror("pthread_create");
return 0;
}
}
return 0;
}