打开方式:
在Linux上(同一台设备同时运行多台客户端和服务器也行,或者同一局域网下的设备),选一台设备编译运行服务器(serve.cpp(创建一个把代码丢进去)),记得链接lpthread
在终端里
编译 : g++ ./serve.cpp -lpthread
运行:./a.out
在另(一/多)台设备编译运行客户端即可
编译 : g++ ./client.cpp -lpthread
运行:./a.out
源码
客户端:
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
using namespace std;
int main()
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(fd == -1)
{
perror("socket is failed to initialize!");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(9999);//端口同服务器中的9999
inet_pton(AF_INET,"192.168.46.128",&saddr.sin_addr.s_addr);//在服务器运行的linux终端ipconfig获取服务器当前ip填入
int ret = connect(fd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret == -1)
{
perror("connect Error!");
return -1;
}
int number = 0;
while(1)
{
char buf[1024];
sprintf(buf,"hello,world,%d...\n",number++);
send(fd,buf,strlen(buf)+1,0);
memset(buf,0,sizeof(buf));
int len = recv(fd,buf,sizeof(buf),0);
if(len>0)
{
cout<<"ServeSay:"<<buf<<endl;
}
else if(len == 0)
{
cout<<"Serve is out of line"<<endl;
break;
}
else
{
perror("receive Error");
break;
}
sleep(1);
}
close(fd);
return 0;
}
服务器:
#include<iostream>
#include<string>
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<thread>
#include<vector>
#include<queue>
#include<mutex>
#include<functional>
#include<condition_variable>
using namespace std;
//线程池
class ThreadPool
{
public:
ThreadPool(int ThreadNum):stop(false)
{
V_threads.emplace_back([this]{
while(true)
{
unique_lock<mutex> u_lock(mtx);
cv.wait(u_lock,[this]{
return stop || !tasks.empty();
});//没任务线程就睡觉等
if(stop&&tasks.empty())
return;
function<void()> task(move(tasks.front()));//把队列中第一个任务内容转交给task留下空壳
tasks.pop();//已经被task取走任务队列中第一个空白任务弹出销毁
u_lock.unlock();
task();//就是后面传进去的working和参数
}
});
}
~ThreadPool()
{
{
unique_lock<mutex> u_lock(mtx);//新建作用域({})把stop锁住
stop = true;
}
cv.notify_all();//唤醒所有线程检查有无任务未完成
for(auto &t : V_threads)
{
t.join();//遍历线程池,等待所有线程结束
}
}
template<class F,class ...Args>
void enqueue(F&& f,Args && ...args)//定义线程池自用函数enqueue接受任何函数和任何类型数量的参数
{
function<void()> task = bind(std::forward<F>(f), forward<Args>(args)...);
{
unique_lock<mutex> u_lock(mtx);
tasks.emplace(move(task));//把enqueue接收到的任务加到线程池里
}
cv.notify_one();//随机唤醒一个沉睡线程
}
private:
bool stop;
vector<thread> V_threads;
queue<function<void()>> tasks;
condition_variable cv;
mutex mtx;
};
//和客户端建立连接后收发
void* working(int cfd,struct sockaddr_in caddr,socklen_t addrlen)
{
char ip[32];
printf("ClientIP:%s,Port:%d\n",
inet_ntop(AF_INET,&caddr.sin_addr.s_addr,ip,sizeof(ip)),
ntohs(caddr.sin_port));
while(1)
{
char buf[1024];
int len = recv(cfd,buf,sizeof(buf),0);
if(len>0)
{
cout<<"ClientSay:"<<buf<<endl;
send(cfd,buf,len,0);
}
else if(len == 0)
{
cout<<"Client is out of line"<<endl;
break;
}
else
{
perror("receive Error");
break;
}
}
}
int main()
{
int fd = socket(AF_INET,SOCK_STREAM,0);//创建一个名为fd的套接字(ipv4,流式传输,0)
if(fd == -1)
{
perror("socket is failed to initialize!");//print error
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;//ipv4
saddr.sin_port = htons(9999);//9999端口号从host转成net
saddr.sin_addr.s_addr = INADDR_ANY;//获取本地ip
int ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));//把sockaddr_in类型强转为sockaddr后和监听套接字进行绑定
if(ret == -1)
{
perror("bind is failed to initialize!");
return -1;
}
ret = listen(fd,128);//开始监听fd套接字,最大队列128,监听返回值丢给ret
if(ret == -1)
{
perror("listen if failed to initialize!");
return -1;
}
struct sockaddr_in caddr;//客户端地址结构体创建(里面啥也没有)
socklen_t addrlen = sizeof(caddr);
ThreadPool pool(4);//创建一个包含4个线程的线程池
while(1)
{
int cfd = accept(fd,(struct sockaddr*)&caddr,&addrlen);//把客户端地址结构体传入,如果接到了客户端的通信请求会把客户端的ip和端口放进caddr里
if(cfd == -1)
{
perror("accept error");
return -1;
}
pool.enqueue(working,cfd,caddr,addrlen);//把working(收发信息)丢给线程池处理
}
close(fd);
return 0;
}