Linux操作系统网路编程基础——微服务器实现

前言

这算是我自己的一个Linux网络编程学习路上的一个学习笔记,学习的过程中看过一些视频+博客,所以在学习过后根据记录的笔记来完成代码实现的过程中,可能会出现一大段文章内容和别人写的一样或者某些思想也会相同,如有侵权,请联系删除或者添加引用。(本文章不会作为商业用途)

一、TCP协议

(1)三次握手——建立连接
三次握手图
从网络编程程序员的角度来看,我们自己一般不会实现三次握手建立连接的这个步骤,因为对于服务器来说,这个过程封装到了accept()函数当中,对于客户端而言,这个过程封装到了conne()函数中。我们调用这两个函数过后,系统会自动使用三次握手连接对端。

(2)四次挥手——断开连接
四次挥手

二、重要函数介绍

1.网络字节序和本地字节序相互转换

在网络通信中,数据的存储方式为大端法:高位存低地址,低位存高地址。
在计算机上,数据的存储方式为小端法:高位存高地址,低位存低地址。
所以在通信过程中,我们需要进行网络字节序和本地字节序进行相互转换。用到的函数如下:

	   #include <arpa/inet.h>
       uint32_t htonl(uint32_t hostlong);
       //32位的本地字节序转网络字节序,通常用于对IP地址进行转换。
       
       uint16_t htons(uint16_t hostshort);
	   //32位的本地字节序转网络字节序,通常用于对端口号进行转换。
	   
       uint32_t ntohl(uint32_t netlong);
       //网络字节序转本地字节序

       uint16_t ntohs(uint16_t netshort);
       //网络字节序转本地字节序
       
	   int inet_pton(int af, const char *src, void *dst);
	   //将点分十进制的IP地址转换为网络字节序
	   //参1:af:ip协议版本,v4或者;参2:src:字符串首地址,或者点分十进制的IP地址字符串;参3:dst:转换后的结果。
	   //返回值:成功(1);异常(0);失败(-1)。

	   const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
	   //将网络字节序转换为点分十进制的IP地址
	   //参1:IP版本。参2:要转换的网络字节序。参3:转换后的结果。参4:src的字节长度
	   //返回值:成功(dst)。失败(NULL)。

2.网络编程用到的函数

       #include <sys/types.h>          
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
       //创建套接字
       //参1:协议簇,可以选择AF_INET、AF_INET6
       //参2:指定socket通信时的类型SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等,一般选用流式SOCK_STREAM
       //参3:指定网络通信使用的协议,一般传0。


       #include <sys/types.h>          
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
       //绑定服务器的IP地址和端口号(IP+端口号称为地址结构)
       //参1:socket()函数创建的套接字(返回值)
       //参2:服务器的地址结构
       //参3:地址结构的长度
       //IP地址可以确定唯一的一台主机,端口号可以确定一台主机上唯一的一个应用程序。两者结合可以确定网络中唯一一台主机上的唯一一个程序。
       #include <sys/types.h>          
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
       //参2:设置sockfd的监听上限
       #include <sys/types.h>          
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
       //等待客户端申请连接的请求,如果有请求则进行三次握手操作来连接
       //参1:socket()函数创建的套接字(返回值)
       //参2:客户端的地址结构
       //参3:地址结构的长度
       

3.读写函数

       #include <sys/types.h>          
       #include <sys/socket.h>

        ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       //从sockfd的读缓冲区向内存中读取数据
       //参1:socket()函数创建的套接字(返回值)
       //参2:buf用来保存读取到的数据,为传出参数
       //参3:buf的长度
       //参4:一般设置为0,默认读取方式,等同于read()函数

		 ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       //向sockfd的写缓冲区中写入数据
       //参1:socket()函数创建的套接字(返回值)
       //参2:buf表示需要向写缓冲区写入的内容
       //参3:需要写入的字节长度
       //参4:一般设置为0,默认读取方式,等同于write()函数
       

三、TCP微型服务器实现

#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <ctype.h>

#define SERVER_PORT 9999
using namespace std;

int main()
{
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd == -1)
        {
                perror("socket error");
                exit(1);
        }

        struct sockaddr_in server_addr;
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(SERVER_PORT);
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        int ret = bind(sockfd, (sockaddr*)&server_addr, sizeof(server_addr));
        if (ret == -1)
        {
                perror("bind error");
                exit(1);
        }

        ret = listen(sockfd, 64);
        if (ret == -1)
        {
                perror("listen error");
                exit(1);
        }

        struct sockaddr_in client_addr;
        socklen_t client_addr_len = sizeof(client_addr);
        
        int cfd = accept(sockfd, (sockaddr*)&client_addr, &client_addr_len);
        if(cfd == -1)
        {
                perror("accept error");
                exit(1);
        }

        string client_ip;
        const char *p = inet_ntop(AF_INET, &client_addr, &client_ip[0], client_addr_len);
        cout << "p=" << p<<endl;
        cout<<"client_ip="<<client_ip.c_str()<<endl;

        char buff[1024];
        while(1)
        {
                ssize_t ret1 = recv(cfd, buff, sizeof(buff),0);
                cout<<buff<<endl;
                if(ret1 == -1)
                {
                        perror("recv error");
                        exit(1);
                }

                for(int i = 0; i < ret1; i++)
                {
                        buff[i] = toupper(buff[i]);
                }
                write(STDOUT_FILENO, buff, ret1);
                send(cfd, buff, ret1, 0);
        }

        close(sockfd);
        close(cfd);

        return 0;
}

四、实验时遇到的问题

为了更像是个C++程序员而不是C程序员,我本来是想做以下替换:
第一步,把while循环上面的一句 char buff[1024] 改为 string buff。
第二步,while循环中,改为 ssize_t ret1 = recv(cfd, &buff[0], buff.size(),0),并且输出一下,cout<< buff.c_str() <<endl。但是在这一步,就开始无法输出 buff 字符串了。
第三步,while 循环中的 for 循环这样改

       for(string::iteartor it = buff.begin(); it != buff.end(); it++)
          {
              *it = toupper(*it);
          }
       write(STDOUT_FILENO, &buff[0], ret1);
       send(cfd, &buff[0], ret1, 0);

经过第三步这样改了过后,客户端的一方也收不到我发出去的数据。

要是有大佬看到我这篇文章,希望您能指教一下,谢谢!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值