Linux系统下TCP迭代服务器的实现

TCP/IP协议栈

在写代码之前我们先了解什么是协议栈。

根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字。TCP套接字是面向连接的,因此又称基于流(stream)的套接字。TCP是 Trasmission Control Protocol (传输控制协议)的简写,意为“对数据传输过程的控制”。

在这里插入图片描述
协议栈一般分为4个层次:

第一层次:数据链路层

链路层是物理链接领域标准化的结果,也是最基本的领域,专门定义LAN、WAN、MAN 等网络标准。

第二层次:IP层

IP是面向消息的、不可靠的协议。每次传输数据时会帮我们选择路径,但并不一致。如果传输中发生路径错误,则选择其他路径;但如果发生数据丢失或错误,则无法解决。换言之,IP协议是无法应对数据错误的。

第三层次:TCP/UDP 层

IP层解决数据传输中的路径选择问题,只需照此路径传输数据即可。

TCP和UDP 层以 IP层提供的路径信息为基础完成实际的数据传输,故该层又称传输层(Transport)。

第四层次:应用层

前面三个层次,套接字通信过程中都是自动处理的。为了”使程序员从这些细节中解放出来“。选择数据传输路径、数据确认过程都被隐藏到套接字内部。前面三个层次都是为了给应用层提供服务的。

简单了解了一下TCP的底层原理我们就开始写一个简单的迭代服务器来进一步理解TCP

首先我们先来编写服务端代码

1. 第一步准备好需要使用的头文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

2. 第二步开始创建套接字

void sokc_ls()
{
//创建套接字(socket)
int serv_sock;
//这是一个基于IPv4的TCP套接字
serv_sock = socket(PF_INET,SOCK_STREAM,0);
if(serv_sock < 0)//判断套接字(socket)是否创建成功
{
    //在一般的编程中防御性编程是十分重要的
    std::cout<<"creat sock failed!"<<std::endl;
    return;
}
}

3. 分配套接字地址

struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));//将servaddr清0  预防申请的变量内有残留信息存在造成错误

//配置地址
servaddr.sin_family = AF_INET;//IPv4协议族   
servaddr.sin_addr.s_addr =  htonl(INADDR_ANY);//相当于”0.0.0.0“  监听所有地址
servaddr.sin_port = htons(9527);//分配端口   

//配置完地址 就可以开始分配了
int ret = bind(serv_sock,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(ret == -1)//成功时返回0 失败时返回-1
{
   std::cout<<"bind failed!"<<std::endl;
   close(server);
   return;
}

4. 等待连接请求(监听)

int ret = listen(serv_sock,3);//第二个参数表示可监听的数量
if(ret == -1)//成功返回0 失败返回-1
{
   std::cout<<"listen failed!"<<std::endl;
   close(server);
   return;
}

5. 创建分机 ,并与客户端连接

int client;
struct sockaddr_in cliaddr;
socklen_t cliaddrlen;
char buffer[1024];
while(1)
{
  memset(buffer,0,sizeof(buffer));//将buffer清0 防止有残留数据存在
  client = accept(serv_sock,(struct sockaddr*)&cliaddr,&cliaddrlen);
  if(client == -1)//成功时返回创建的套接字文件描述符,失败时返回-1
  {
	std::cout << "accept failed !" << std::endl;		
	close(server);		
	return;		
  }
     //读取数据
    read(client,buffer,sizeof(buffer));
    //写入数据  
    ssize_t len = write(client,buffer,stelen(buffer));
    if(len != (ssize_t)strlen(buffer))//返回的长度与发送的长度不一致
    {
       std::cout<<"write failed!"<<std::endl;
       close(serv_sock);
       return;
    }
    //关闭套接字
    close(client);//这步可不执行
}
close(serv_sock);//当服务端关闭时,客户端也会同时关闭。

服务端代码写完,我们再来写客户端代码

void client_ls()
{
   //创建套接字
   int client;
   struct sockaddr_in servaddr;
   client = socket(PF_INET,SOCK_STREAM.0);
   if(serv_sock < 0)//判断套接字(socket)是否创建成功
   {
      std::cout<<"creat sock failed!"<<std::endl;
      return;
   }
   memset(servaddr,0,sizeof(servaddr));//清0
   //配置地址
   servaddr.sin_family = AF_INET;
   servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//”127.0.0.1“为回环地址   一般在本机调试时使用
   servaddr.sin_port = htons(9527);
   //与服务端连接
   int ret = connect(client,(struct sockaddr*)&servaddr,sizeof(servaddr));
   if(ret==0)//成功时返回0 失败时返回-1
   {
       char buffer[256] = "hello,here is client!";
       //往服务端写入数据
       write(client,buffer,strlen(buffer));
       memset(buffer,0,sizeof(buffer));//清0
       //读取从服务端返回的数据
       read(client,buffer,sizeof(buffer));
       std::cout<<buffer;//输出读取的数据
   }
   else
   {
      printf("%s(%d):%s %d\n", __FILE__, __LINE__, __FUNCTION__, ret);
   }
   //关闭套接字
   close(client);
   std::cout<<"client done !"<<std::endl;
}

服务端与客户端的代码都写完了,我们来测试一下

int main(int argc,char* argv[])
{
    //使用创建子进程的方法来进行测试
    pid_t pid = fork();
    if(pid == 0)//当pid等于0 时,代表的是子进程 所以我们调用客户端函数
    {
       sleep(1);//暂停一秒 防止第一次连接失败
       client_ls();
       client_ls();
    }
    else if(pid > 0)//当pid大于0时,代表的是父进程,所以我们调用服务端函数
    {
       sokc_ls();
       int status = 0;
       writ(&status);//为防止僵尸进程的出现  使用fork函数时,推荐将writ函数与fork函数同时写入,在进行内容编辑
    }
    else
    {
      std::cout << "fork failed!" << pid << std::endl;//进程创建失败
    }
}

在这里插入图片描述
因为没有设置退出机制所以会在俩次客户端执行结束后卡住,可使用Ctrl+C进行退出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值