TCP实现网络通信(多进程与多线程版本)
目录
1.TCP实现网络通信的过程
1.1服务端
1.2客户端
2.几个重要函数
2.1 listen()与connect()
2.2 ntohs()与inet_nota
3.基于多进程的TCP通信
3.1 服务端
3.2 客户端
4.基于多线程的TCP通信
4.1 服务端
4.2 客户端
4.3 任务
4.4 线程池
1.TCP实现网络通信的过程
1.1服务端
1.创建网络套接字:socket(),协议族为AF_INET,套接字类型为SOCK_STREAM。
本质上是打开一个套接字文件,目前仅仅有与系统相关的内容(inode和blocks)。该套接字文件主要负责监听。
2.链接网络套接字与本机ip和端口号:bind(),需要建立输出型参数协议地址空间sockaddr_in。
本质上讲本机网络信息内容和套接字文件信息相关联。
3.监听网络连接:listen(),参数为套接字的文件描述符。
本质上是将套接字文件设置为监听状态,允许别人连接。
4.获取新连接到应用层:accept(),返回值为一个新的套接字的文件描述符,参数为监听套接字的文件描述符。
返回的套接字文件主要负责信息的传输。
5.使用read()或者write函数对新套接字文件进行读写,实现网络通信。
6.关闭套接字文件:close()
1.2客户端
1.创建网络套接字:socket(),协议族为AF_INET,套接字类型为SOCK_STREAM。
2.连接服务器:connect()需要建立协议地址空间,并填入客户端的信息。
3.使用read()或者write()对新套接字文件进行读写,实现网络通信。
4.关闭套接字文件close()。
2.几个重要函数
socket等函数以及协议地址空间在udp通信一文中已经说过了。
2.1 listen()与connect()
int listen(int sockfd,int backlog);
第一个参数代表用于监听的套接字文件的文件描述符。监听成功返回0,监听失败返回-1。
int connect(int sockfd,const struct sockaddr*addr,socklen_t addrlen);
第一个参数代表用于监听的套接字文件的文件描述符。第二个参数为服务端的协议地址空间,第三个参数为该协议地址空间的大小。
2.2 ntohs()与inet_nota
uint16_t ntohs(uint16_t netshort);
将一个网络形式的port转化为字符串形式。
char* inet_ntoa(struct in_addr in);
将一个struct in_addr类型的结构体中的s.addr取出,并将该ip转化成点分十进制。
3.基于多进程的TCP通信
3.1 服务端
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include<signal.h>
#include<sys/wait.h>
using namespace std;
void Usage(string proc)
{
cout << proc << "port_in" << endl;
}
void service(int new_sock)
{
while (true)
{
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ssize_t s = read(new_sock, buffer, sizeof(buffer) - 1);
if (s > 0)
{
buffer[s] = 0;
cout << "client#" << buffer << endl;
string echo_string = "server ";
echo_string += buffer;
write(new_sock, echo_string.c_str(), echo_string.size());
}
else if (s == 0)
{
cout << "client quit" << endl;
break;
}
else
{
cerr << "read error" << endl;
break;
}
}
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
return 1;
}
//创建套接字
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0)
{
cerr << "socket error" << errno << endl;
return 2;
}
//绑定套接字
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[1]));
local.sin_addr.s_addr = INADDR_ANY;
if ((bind(listen_sock, (struct sockaddr *)&local, sizeof(local))) < 0)
{
cerr << "bind error" << errno << endl;
return 3;
}
//等待客户端建立链接
const int back_log = 5;
if (listen(listen_sock, back_log) < 0)
{
cerr << "listen error" << endl;
return 4;
}
//signal(SIGCHLD,SIG_IGN);
while (true)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
if (new_sock < 0)
{
continue;
}
cout << "get a new link..." ;
uint16_t client_port=ntohs(peer.sin_port);
string client_ip=inet_ntoa(peer.sin_addr);
cout<< client_ip<<":"<<client_port<<endl;
//提供服务
pid_t id=fork();
if(id<0)
{
continue;
}
else if(id==0)
{
close(listen_sock);
if(fork()>0) exit(0);
service(new_sock);
close(new_sock);
exit(0);
}
else
{
waitpid(id,nullptr,0);
close(new_sock);//do nothing
}
}
}
3.2 客户端
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
using namespace std;
void Usage(string proc)
{
cout<<proc<<"ip_ "<<"port_ "<<endl;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
Usage(argv[0]);
return 1;
}
int sock=socket(AF_INET,SOCK_STREAM,0);
string svr_ip=argv[1];
uint16_t port=atoi(argv[2]);
if(sock<0)
{
cerr<<"socket error"<<endl;
return 2;
}
struct sockaddr_in server;
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr(svr_ip.c_str());
server.sin_port=htons(port);
if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)
{
cout<<"connect fail"<<endl;
return 3;
}
cout<<"connect success"<<endl;
while(true)
{
cout<<"please Enter#"<<endl;
char buffer[1024];
fgets(buffer,sizeof(buffer)-1,stdin);
write(sock,buffer,strlen(buffer));
ssize_t s=read(sock,buffer,sizeof(buffer)-1);
if(s>0)
{
buffer[s]=0;
cout<<"server echo#"<<buffer<<endl;
}
}
}
4.基于多线程的TCP通信
我们使用线程池来完成多线程实现TCP通信:
4.1 服务端
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include"Threadpool.hpp"
using namespace std;
using namespace ns_threadpool;
void Usage(string proc)
{
cout << proc << "port_in" << endl;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
return 1;
}
//创建套接字
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0)
{
cerr << "socket error" << errno << endl;
return 2;
}
//绑定套接字
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[1]));
local.sin_addr.s_addr = INADDR_ANY;
if ((bind(listen_sock, (struct sockaddr *)&local, sizeof(local))) < 0)
{
cerr << "bind error" << errno << endl;
return 3;
}
//等待客户端建立链接
const int back_log = 5;
if (listen(listen_sock, back_log) < 0)
{
cerr << "listen error" << endl;
return 4;
}
//signal(SIGCHLD,SIG_IGN);
while (true)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(listen_sock, (struct sockaddr *)&peer, &len);
if (new_sock < 0)
{
continue;
}
cout << "get a new link..." ;
uint16_t client_port=ntohs(peer.sin_port);
string client_ip=inet_ntoa(peer.sin_addr);
cout<< client_ip<<":"<<client_port<<endl;
//提供服务
Task t(new_sock);
ThreadPool<Task>::getinstance()->Push(t);
}
}
4.2 客户端
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
using namespace std;
void Usage(string proc)
{
cout<<proc<<"ip_ "<<"port_ "<<endl;
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
Usage(argv[0]);
return 1;
}
int sock=socket(AF_INET,SOCK_STREAM,0);
string svr_ip=argv[1];
uint16_t port=atoi(argv[2]);
if(sock<0)
{
cerr<<"socket error"<<endl;
return 2;
}
struct sockaddr_in server;
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr(svr_ip.c_str());
server.sin_port=htons(port);
if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)
{
cout<<"connect fail"<<endl;
return 3;
}
cout<<"connect success"<<endl;
while(true)
{
cout<<"please Enter#"<<endl;
char buffer[1024];
fgets(buffer,sizeof(buffer)-1,stdin);
write(sock,buffer,strlen(buffer));
ssize_t s=read(sock,buffer,sizeof(buffer)-1);
if(s>0)
{
buffer[s]=0;
cout<<"server echo#"<<buffer<<endl;
}
}
}
4.3 任务
#include <iostream>
#include<string.h>
#include<unistd.h>
using namespace std;
namespace ns_task
{
struct Task
{
private:
int sock_;
public:
Task() : sock_(-1) {}
Task(int sock) : sock_(sock) {}
int Run()
{
while (true)
{
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
ssize_t s = read(sock_, buffer, sizeof(buffer) - 1);
if (s > 0)
{
buffer[s] = 0;
cout << "client#" << buffer << endl;
string echo_string = "server ";
echo_string += buffer;
write(sock_, echo_string.c_str(), echo_string.size());
}
else if (s == 0)
{
cout << "client quit" << endl;
break;
}
else
{
cerr << "read error" << endl;
break;
}
}
close(sock_);
}
};
}
4.4 线程池
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>
#include "task.hpp"
using namespace std;
using namespace ns_task;
namespace ns_threadpool
{
template <class T>
class ThreadPool
{
private:
int num_;
pthread_mutex_t mtx;
pthread_cond_t cond;
queue<T> Task;
static ThreadPool<T>* ins;
public:
ThreadPool() : num_(5)
{
pthread_mutex_init(&mtx,nullptr);
pthread_cond_init(&cond,nullptr);
}
void Lock()
{
pthread_mutex_lock(&mtx);
}
void unLock()
{
pthread_mutex_unlock(&mtx);
}
void Wait()
{
pthread_cond_wait(&cond, &mtx);
}
void Wakeup()
{
pthread_cond_signal(&cond);
}
void Push(T &task)
{
Lock();
Task.push(task);
unLock();
Wakeup();
}
void Pop(T *task)
{
*task = Task.front();
Task.pop();
}
bool IsEmpty()
{
return Task.empty();
}
static void *Routine(void *args)
{
pthread_detach(pthread_self());
ThreadPool<T> *tp = (ThreadPool<T> *)args;
while (true)
{
tp->Lock();
if (tp->IsEmpty())
{
tp->Wait();
}
T t;
tp->Pop(&t);
tp->unLock();
t.Run();
}
}
void InitThreadPool()
{
pthread_t tid;
for (int i = 0; i < num_; i++)
{
pthread_create(&tid,nullptr, Routine, (void *)this);
}
}
static ThreadPool<T>* getinstance()
{
if(ins==nullptr)
{
pthread_mutex_t mmtx=PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mmtx);
if(ins==nullptr)
{
ins=new ThreadPool<T>();
ins->InitThreadPool();
}
pthread_mutex_unlock(&mmtx);
}
return ins;
}
~ThreadPool()
{
pthread_mutex_destroy(&mtx);
pthread_cond_destroy(&cond);
}
};
template<class T>
ThreadPool<T>* ThreadPool<T>::ins=nullptr;
}