当前位置: 首页 > news >正文

建设外贸型网站流程/厦门网站建设公司名单

建设外贸型网站流程,厦门网站建设公司名单,注册电商平台怎么注册,wordpress文章页面模板文章目录 一、引入二、服务端实现2.1 创建套接字socket2.2 绑定bind2.3 设置监听状态listen2.4 获取新链接accept2.5 获取信息与返回信息(文件操作) 三、客户端实现3.1 创建套接字socket3.2 绑定问题3.3 发起链接connect3.4 客户端并行3.4.1 多进程版3.4…

文章目录

  • 一、引入
  • 二、服务端实现
    • 2.1 创建套接字socket
    • 2.2 绑定bind
    • 2.3 设置监听状态listen
    • 2.4 获取新链接accept
    • 2.5 获取信息与返回信息(文件操作)
  • 三、客户端实现
    • 3.1 创建套接字socket
    • 3.2 绑定问题
    • 3.3 发起链接connect
    • 3.4 客户端并行
      • 3.4.1 多进程版
      • 3.4.2 多线程版
      • 3.4.3 线程池版
  • 四、总结

一、引入

UDP和TCP的区别:

对于TCP协议有几个特点:

1️⃣ 传输层协议
2️⃣ 有连接(正式通信前要先建立连接)
3️⃣ 可靠传输(在内部帮我们做可靠传输工作)
4️⃣ 面向字节流

对于UDP协议有几个特点:

1️⃣ 传输层协议
2️⃣ 无连接
3️⃣ 不可靠传输
4️⃣ 面向数据报

可以看到TCP对比UDP会建立链接

其他的接口跟UDP其实没什么区别:【网络编程】demo版UDP网络服务器实现

二、服务端实现

2.1 创建套接字socket

在通信之前要先把网卡文件打开。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);RETURN VALUE
On success, a file descriptor for the new socket is returned.  
On error, -1 is returned, and errno is set appropriately.

这个函数的作用是打开一个文件,把文件和网卡关联起来。

参数介绍:

domain:一个域,标识了这个套接字的通信类型(网络或者本地)。
在这里插入图片描述
只用关注上面两个类,第一个AF_UNIX表示本地通信,而AF_INET表示网络通信。
type:套接字提供服务的类型。
在这里插入图片描述
这一章我们讲的式TCP,所以使用SOCK_STREAM
protocol:想使用的协议,默认为0即可,因为前面的两个参数决定了,就已经决定了是TCP还是UDP协议了。

返回值:

成功则返回打开的文件描述符(指向网卡文件),失败返回-1。

而从这里我们就联想到系统中的文件操作,未来各种操作都要通过这个文件描述符,所以在服务端类中还需要一个成员变量表示文件描述符。

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include "log.hpp"class TCPServer
{
static const uint16_t gport = 8080;
public:TCPServer(cosnt uint16_t& port = gport): _sock(-1), _port(port){}void InitServer(){_sock = socket(AF_INET, SOCK_STREAM, 0);if(_sockfd == -1){std::cerr << "create socket error" << std::endl;exit(1);}std::cout << "create socket success" << std::endl;}void start(){}
private:int _sock;uint16_t _port;
};

2.2 绑定bind

#include <sys/socket.h>int bind(int socket, const struct sockaddr *address,socklen_t address_len);RETURN VALUE
Upon successful completion, bind() shall return 0; 
otherwise, -1 shall be returned and errno set to indicate the error.

参数介绍:

socket:创建套接字的返回值。
address:通用结构体(【网络编程】socket套接字有详细介绍)。
address_len:传入结构体的长度。

所以我们要先定义一个sockaddr_in结构体填充数据,在传递进去。
在这里插入图片描述
然后就是跟UDP一样,先初始化结构体,再处理IP和端口。
要注意IP要绑定任意IP也就是INADDR_ANY
至于为什么再上一章【网络编程】demo版UDP网络服务器实现有过详细讲解。

void InitServer()
{_sock = socket(AF_INET, SOCK_STREAM, 0);if(_sockfd == -1){std::cerr << "create socket error" << std::endl;exit(1);}std::cout << "create socket success" << std::endl;struct sockaddr_in si;// 初始化结构体bzero(&si, sizeof si);si.sin_family = AF_INET;si.sin_port = htons(_port);// 主机转网络序列si.sin_addr.s_addr = INADDR_ANY;if(bind(_sock, (struct sockaddr*)&si, sizeof si) < 0){std::cout << "bind socket error" << std::endl;exit(1);}std::cout << "bind socket success" << std::endl;
}

2.3 设置监听状态listen

TCP跟UDP的不同在这里就体现了出来。
要把socket套接字的状态设置为listen状态。只有这样才能一直获取新链接,接收新的链接请求。

举个例子:
我们买东西如果出现了问题会去找客服,如果客服不在那么就回复不了,所以规定了客服在工作的时候必须要时刻接收回复消息,这个客服所处的状态就叫做监听状态

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int listen(int sockfd, int backlog);RETURN VALUE
On success, zero is returned.  
On error, -1 is returned, and errno is set appropriately.

关于第二个参数backlog后边讲TCP协议的时候介绍,目前先直接用。

static const int gbacklog = 10;
void InitServer()
{_sock = socket(AF_INET, SOCK_STREAM, 0);if(_sock == -1){std::cerr << "create socket error" << std::endl;exit(1);}std::cout << "create socket success" << std::endl;struct sockaddr_in si;// 初始化结构体bzero(&si, sizeof si);si.sin_family = AF_INET;si.sin_port = htons(_port);// 主机转网络序列si.sin_addr.s_addr = INADDR_ANY;if(bind(_sock, (struct sockaddr*)&si, sizeof si) < 0){std::cout << "bind socket error" << std::endl;exit(1);}std::cout << "bind socket success" << std::endl;// 设置监听状态if(listen(_sock, gbacklog) < 0){std::cout << "listen socket error" << std::endl;exit(1);}std::cout << "listen socket success" << std::endl;
}

2.4 获取新链接accept

上面初始化完毕,现在开始就是要运行服务端,而TCP不能直接发数据,因为它是面向链接的,必须要先建立链接

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);RETURN VALUE
On success, these system calls return a nonnegative integer that is a descriptor for the accepted socket.  
On error, -1 is returned, and errno is set appropriately.

参数介绍:

sockfd文件描述符,找到套接字
addr输入输出型参数,是一个结构体,用来获取客户端的信息。
addrlen输入输出型参数,客户端传过来的结构体大小。

返回值:

成功返回一个文件描述符
失败返回-1

而我们知道sockfd本来就是一个文件描述符,那么这个返回的文件描述符是什么呢?
举个例子:

我们去吃饭的时候会发现每个店铺门口都会有人来招揽顾客,这个人把我们领进去门店后,然后他就会继续站在门口继续招揽顾客,而我们会有里面的服务员来招待我们,给我们提供服务。

这里的揽客的人就是sockfd,而服务员就是返回值的文件描述符。
意思就是sockfd的作用就是把链接从底层获取上来,返回值的作用就是跟客户端通信。

从这里就知道了成员变量中的_sock并不是通信用的套接字,而是获取链接的套接字。为了方便观察,我们可以把所有的_sock换_listensock

void start()
{while(1){// 获取新链接struct sockaddr_in si;socklen_t len = sizeof si;int sock = accept(_listensock, (struct sockaddr*)&si, &len);if(sock < 0){// 获取链接失败无影响,继续获取即可std::cout << "accept error, continue" << std::endl;continue;}std::cout << "accept a new link success" << std::endl;std::cout << "sock: " << sock << std::endl;}
}

2.5 获取信息与返回信息(文件操作)

上面获取到了通信用的套接字sock,而因为TCP通信是面向字节流的,所以后续通信全部是用文件操作(IO),因为文件也是面向字节流的

IO的操作我们可以封装一个函数。

void ServerIO(int sock)
{char buf[1024];// 接收消息       while(1){ssize_t n = read(sock, buf, sizeof buf - 1);if(n > 0){buf[n] = '\0';std::cout << "read a message: " << buf << std::endl;// 把消息发送回去std::string outbuf;outbuf += "Server[echo]#";outbuf += buf;write(sock, outbuf.c_str(), outbuf.size());}else if(n == 0){// 代表客户端退出std::cout << "Client quit" << std::endl;break;}}
}

当IO完后要记得关闭文件描述符sock,不然会导致可用描述符越来越少。
在这里插入图片描述
验证发现可以运行
在这里插入图片描述在这里插入图片描述

三、客户端实现

3.1 创建套接字socket

Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。
所以客户端也需要一个套接字。

void initClient()
{_sock = socket(AF_INET, SOCK_STREAM, 0);if(_sock < 0){std::cout << "create socket error" << std::endl;exit(1);}std::cout << "create socket success" << std::endl;
}

3.2 绑定问题

跟上一章一样要绑定,但是不能显示绑定

3.3 发起链接connect

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);RETURN VALUE
If the connection or binding succeeds, zero is returned.  
On error, -1 is returned, and errno is set appropriately.

参数说明:

这里的addraddrlen填入的是服务端信息。

在UDP通信中,客户端在sendto的时候会自动绑定IP和port,TCP这里就是在connect的时候绑定。

void start()
{struct sockaddr_in si;bzero(&si, sizeof si);si.sin_family = AF_INET;si.sin_port = htons(_serverport);si.sin_addr.s_addr = inet_addr(_serverip.c_str());if(connect(_sock, (struct sockaddr*)&si, sizeof si) < 0){std::cout << "connect socket error" << std::endl;}else{std::string msg;while(1){std::cout << "Please Enter#";std::getline(std::cin, msg);write(_sock, msg.c_str(), msg.size());// 从服务端读取数据char buf[1024];int n = read(_sock, buf, sizeof buf - 1);if(n > 0){buf[n] = '\0';std::cout << buf << std::endl;}else{break;}}}
}

最后在析构的时候要关掉文件描述符。

~TCPClient()
{if(_sock >= 0){close(_sock);}  
}

服务端:
在这里插入图片描述
客户端:
在这里插入图片描述
但此时如果我们在用一个客户端链接,会发现无法通信,除非第一个客户端退出。
在这里插入图片描述
这是因为获取新链接后会进入ServerIo中死循环。只要这个客户端不退出,就会一直给这个客户端提供服务。

那怎么保证多个客户端并行呢?

3.4 客户端并行

3.4.1 多进程版

因为fork后子进程会复制父进程的文件描述符。
这里注意子进程并不需要_listensock文件描述符,所以最好关闭。

pid_t id = fork();
if(id == 0)// child
{close(_listensock);ServerIO(sock);close(sock);exit(1);
}

接下来父进程怎么办呢?是等待吗?
如果父进程等待的话又会导致上面的情况,子进程不退出父进程就一直等待。

子进程退出时,会给父进程发送一个SIGCHLD,17号信号。所以有一种解决办法就是用signal函数,在回调函数中把waitpid的参数设置为-1(等待任意进程),就可以回收。

现在我们不用这种办法,我们可以这么写:

pid_t id = fork();
if(id == 0)// child
{close(_listensock);if(fork() > 0) exit(1);ServerIO(sock);close(sock);exit(1);
}
// father
pid_t ret = waitpid(id, nullptr, 0);
if(ret > 0)
{std::cout << "wait success" << ret << std::endl;
}

这里的意思就是创建孙子进程,父进程直接退出,让孙子进程执行ServerIO,此时孙子进程就会被操作系统收养,不用我们管,而父进程退出,外边的父进程也等待成功了。

结果演示:
客户端:
在这里插入图片描述
在这里插入图片描述
服务端:
在这里插入图片描述
其实下面的等待可以不用等待,因为SIGCHLD信号默认的处理方式是忽略。
在这里插入图片描述
在这里插入图片描述
这里看到客户端退出了但是文件描述符并没有被回收。
这里的原因是我们只关闭了子进程的文件描述符,没有关闭父进程:
在这里插入图片描述

3.4.2 多线程版

struct ThreadData
{TCPServer* _self;int _sock;
};void start()
{while(1){// 获取新链接struct sockaddr_in si;socklen_t len = sizeof si;int sock = accept(_listensock, (struct sockaddr*)&si, &len);if(sock < 0){// 获取链接失败无影响,继续获取即可std::cout << "accept error, continue" << std::endl;continue;}std::cout << "accept a new link success" << std::endl;std::cout << "sock: " << sock << std::endl;// 多线程pthread_t tid;ThreadData* td = new ThreadData({this, sock});pthread_create(&tid, nullptr, thread_start, td);}
}static void* thread_start(void* args)
{// 线程分离pthread_detach(pthread_self());ThreadData* tp = static_cast<ThreadData*>(args);tp->_self->ServerIO(tp->_sock);close(tp->_sock);delete tp;
}

3.4.3 线程池版

前面我们写过线程池【linux】基于单例模式实现线程池,这里直接拿来用即可。
这里就要修改一下代码,因为ServerIO可以不属于类,所以可以把ServerIO放在任务Task.hpp中。

// Task.hpp
#pragma once#include <iostream>
#include <string>
#include <functional>
#include <cstdio>void ServerIO(int sock)
{char buf[1024];// 接收消息       while(1){ssize_t n = read(sock, buf, sizeof buf - 1);if(n > 0){buf[n] = '\0';std::cout << "read a message: " << buf << std::endl;// 把消息发送回去std::string outbuf;outbuf += "Server[echo]#";outbuf += buf;write(sock, outbuf.c_str(), outbuf.size());}else if(n == 0){// 代表客户端退出std::cout << "Client quit" << std::endl;break;}}
}class Task
{typedef std::function<void(int)> func_t;
public:Task(){}Task(int sock, func_t func): _sock(sock), _func(func){}void operator()(){_func(_sock);}std::string tostringTask(){char buf[64];snprintf(buf, sizeof buf, "%d %c %d = ?", _x, _op, _y);return buf;}
private:int _sock;func_t _func;
};// ThreadPool.hpp
#pragma once#include <vector>
#include <queue>
#include <mutex>
#include "mythread.hpp"
#include "mymutex.hpp"
#include "Task.hpp"using std::cout;
using std::endl;const int N = 5;template <class T>
class ThreadPool;template <class T>
struct ThreadData
{ThreadPool<T>* _tp;std::string _name;ThreadData(ThreadPool<T>* tp, const std::string& name): _tp(tp), _name(name){}
};template <class T>
class ThreadPool
{
private:static void* handlerTask(void* args){ThreadData<T>* tdp = static_cast<ThreadData<T>*>(args);while(true){tdp->_tp->lockqueue();while(tdp->_tp->isqueueempty()){tdp->_tp->threadwait();}T t = tdp->_tp->pop();tdp->_tp->unlockqueue();t();}delete tdp;}void lockqueue() volatile{pthread_mutex_lock(&_mutex);}void unlockqueue() volatile{pthread_mutex_unlock(&_mutex);}bool isqueueempty() volatile{return _tasks.empty();}void threadwait() volatile{ pthread_cond_wait(&_cond, &_mutex);}T pop() volatile{T res = _tasks.front();_tasks.pop();return res;}ThreadPool(int num = 5): _num(num){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);// 创建线程for(int i = 0; i < _num; i++){_threads.push_back(new Thread());}}ThreadPool(const ThreadPool<T>& ) = delete;ThreadPool<T> operator=(const ThreadPool<T>&) = delete;public:void start() volatile{for(auto& t : _threads){ThreadData<T>* td = new ThreadData<T>(this, t->GetName());t->start(handlerTask, td);}}void push(const T& in) volatile{LockAuto lock(&_mutex);_tasks.push(in);// 唤醒池中的一个线程pthread_cond_signal(&_cond);}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);for(auto & e : _threads){delete e;}}volatile static ThreadPool<T>* GetSingle(){if(_tp == nullptr){_singlelock.lock();if(_tp == nullptr){_tp = new ThreadPool<T>();}_singlelock.unlock();}return _tp;}private:int _num;// 线程数量std::vector<Thread*> _threads;std::queue<T> _tasks;// 任务队列pthread_mutex_t _mutex;// 保护任务队列pthread_cond_t _cond;volatile static ThreadPool<T>* _tp;static std::mutex _singlelock;
};template <class T>
volatile ThreadPool<T>* ThreadPool<T>::_tp = nullptr;template <class T>
std::mutex ThreadPool<T>::_singlelock;// TCPServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <cstdlib>
#include <sys/wait.h>
#include <pthread.h>
#include "ThreadPool.hpp"
#include "log.hpp"class TCPServer;
struct ThreadData
{TCPServer* _self;int _sock;
};class TCPServer
{
static const uint16_t gport = 8080;
static const int gbacklog = 10;
public:TCPServer(const uint16_t& port = gport): _listensock(-1), _port(port){}void InitServer(){_listensock = socket(AF_INET, SOCK_STREAM, 0);if(_listensock == -1){std::cerr << "create socket error" << std::endl;exit(1);}std::cout << "create socket success" << std::endl;struct sockaddr_in si;// 初始化结构体bzero(&si, sizeof si);si.sin_family = AF_INET;si.sin_port = htons(_port);// 主机转网络序列si.sin_addr.s_addr = INADDR_ANY;if(bind(_listensock, (struct sockaddr*)&si, sizeof si) < 0){std::cout << "bind socket error" << std::endl;exit(1);}std::cout << "bind socket success" << std::endl;// 设置监听状态if(listen(_listensock, gbacklog) < 0){std::cout << "listen socket error" << std::endl;exit(1);}std::cout << "listen socket success" << std::endl;}void start(){// 线程池初始化ThreadPool<Task>::GetSingle()->start();while(1){// 获取新链接struct sockaddr_in si;socklen_t len = sizeof si;int sock = accept(_listensock, (struct sockaddr*)&si, &len);if(sock < 0){// 获取链接失败无影响,继续获取即可std::cout << "accept error, continue" << std::endl;continue;}std::cout << "accept a new link success" << std::endl;std::cout << "sock: " << sock << std::endl;// 线程池ThreadPool<Task>::GetSingle()->push(Task(sock, ServerIO));// 多线程// pthread_t tid;// ThreadData* td = new ThreadData({this, sock});// pthread_create(&tid, nullptr, thread_start, td);// 多进程// pid_t id = fork();// if(id == 0)// child// {//     close(_listensock);//     if(fork() > 0) exit(1);//     ServerIO(sock);//     close(sock);//     exit(1);// }// close(sock);// father// pid_t ret = waitpid(id, nullptr, 0);// if(ret > 0)// {//     std::cout << "wait success " << ret << std::endl;// }// ServerIO(sock);// // 关闭使用完的文件描述符// close(sock);}}static void* thread_start(void* args){// 线程分离pthread_detach(pthread_self());ThreadData* tp = static_cast<ThreadData*>(args);tp->_self->ServerIO(tp->_sock);close(tp->_sock);delete tp;}private:int _listensock;uint16_t _port;
};

四、总结

对比UDP服务器,TCP服务器多了获取新链接和监听的操作,而因为TCP是面向字节流的,所以接收和发送数据都是IO操作,也就是文件操作。

http://www.jmfq.cn/news/4747591.html

相关文章:

  • 网站空间2G一年多少钱/手机打开国外网站app
  • 网站做任务佣金/今天的热点新闻
  • 网站开发设计文案/关键词搜索神器
  • 住房建设部投诉网站/免费的外链网站
  • 电子工程建设/搜索引擎优化关键词选择的方法有哪些
  • wordpress信用卡收款/手机优化
  • 独立建站什么意思/网络推广自学
  • 网站备案证书下载密码忘了/今日资讯最新消息
  • 网站建设引擎/百度资源搜索
  • 活动发布类网站开发/营销软文范例500
  • 济南做网站/品牌网络推广
  • 在centos上做网站/谷歌推广方案
  • 建设网站企业邮箱/百度企业网盘
  • wordpress开通多站点好处/电子商务网站设计方案
  • 地方新闻网站好坏/怎样免费给自己的公司做网站
  • 常宁做网站/nba篮网最新消息
  • php网站开发价格/网络广告案例以及分析
  • wordpress客服插件/杭州专业seo
  • 永春网站建设/软文推广哪个平台好
  • 安阳做网站电话/怎么样做一个自己的网站
  • 沈阳网络关键词排名/windows优化大师的优点
  • 制作网站需要学什么软件/seo外包 杭州
  • 做论坛网站看什么书/备案查询官网
  • 建设部招标网 官方网站/百度投诉中心24人工客服电话
  • 网站被百度蜘蛛爬死了/nba在线直播免费观看直播
  • 企业为什么要自助建站/知乎推广渠道
  • 网上兼职做效果图网站/网站关键词提升
  • 网站建设国内外研究进展/网上推广培训
  • wordpress主题 大/seo网站平台
  • 源代码代做网站/百度搜索词热度查询