TCP编程及基础知识

一、端口号

  • 为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区分
  • TCP端口号与UDP端口号独立
  • 端口用两个字节来表示    2byte(65535个)

众所周知端口:1~10231~255之间为众所周知端口,256~1023端口通常由UNIX系统占用)
已登记端口:1024~49151    (选1000以上10000以下) 
动态或私有端口:49152~65535

二、字节序  

   小端序(little-endian)  - 低序字节存储在低地址

   大端序(big-endian)    - 高序字节存储在低地址

网络中传输一字节以上的带类型的数据(比如short、int),必须使用网络字节序,即大端字节序。

查看主机是大端序还是小端序。

网络传输中,需要将每个主机的主机字节序(CPU决定),转换为网络中统一顺序的网络字节序

才能供双方主机去识别

只需要转换IP和port就可以,不需要转换传输的数据包的字节序

因为IP和port为 4个字节和2个字节,  而数据报一般都为char类型, 占一个字节

根据字节序的性质,内存存储大于一个字节类型的数据在内存中的存放顺序

所以char类型并不具有字节序的概念

1.主机字节序到网络字节序 (小端序->大端序)

#include <arpa/inet.h>
u_long htonl (u_long hostlong); //host to internet long
功能:将无符号整数hostlong从主机字节顺序转换为网络字节顺序。
#include <arpa/inet.h>
u_short htons (u_short short);  //掌握这个
功能:将无符号短整数hostshort从主机字节顺序到网络字节顺序。

2.网络字节序到主机字节序(大端序->小端序)

#include <arpa/inet.h>
u_long ntohl (u_long hostlong);
功能:将无符号整数netlong从网络字节顺序转换为主机字节顺序。
#include <arpa/inet.h>
u_short ntohs (u_short short);//端口 2byte
功能:将无符号短整数netshort从网络字节顺序转换为主机字节顺序。的

#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char const *argv[])
{
    int a = 0x12345678;
    //小端转大端
    int b = htonl(a);
    printf("%#x\n", b);
    //大端转小端
    int c = ntohl(b);
    printf("%#x\n", c);
    return 0;
}

三、IP地址转换

1、inet_addr主机字节序转换为网络字节序

#include<sys/socket.h>   
#include<netinet/in.h>               
#include<arpa/inet.h>
in_addr_t  inet_addr(const char *strptr);  //该参数是字符串typedef uint32_t in_addr_t;
struct in_addr 
{
    in_addr_t s_addr;
};
功能:  主机字节序转为网络字节序
参数:  const char *strptr: 字符串
返回值: 返回一个无符号长整型数(无符号32位整数用十六进制表示), 
      否则NULL

2、inet_ntoa 网络字节序转换为主机字节序

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *inet_ntoa(struct in_addr inaddr);
功能:   将网络字节序二进制地址转换成主机字节序。 
参数:  struct in_addr in addr  : 只需传入一个结构体变量
返回值:  返回一个字符指针, 否则NULL;

四、TCP编程

C/S   B/S

client/server              browser/server

客户端/服务器 浏览器/服务器

1、套接字工作流程

客户端:   发送请求

服务器端:  相应请求

服务器:

1.创建流式套接字(socket())------------------------>  有手机

2.指定本地的网络信息(struct sockaddr_in)----------> 有号码

3.绑定套接字(bind())------------------------------>绑定手机

4.监听套接字(listen())---------------------------->待机

5.链接客户端的请求(accept())---------------------->接电话

6.接收/发送数据(recv()/send())-------------------->通话

7.关闭套接字(close())----------------------------->挂机

客户端:

1.创建流式套接字(socket())----------------------->有手机

2.指定服务器的网络信息(struct sockaddr_in)------->有对方号码

3.请求链接服务器(connect())---------------------->打电话

4.发送/接收数据(send()/recv())------------------->通话

5.关闭套接字(close())--------------------------- >挂机

服务器端(server):

1) socket(),创建套接字文件,创建出用于连接的套接字文件

2) bind(), 绑定,把socket()函数返回的文件描述符和IP、端口号进行绑定;

3) listen(), 监听,将socket()返回的文件描述符,由主动套接字变为被动套接字;

4) accept(), 阻塞函数,阻塞等待客户端的连接请求, 返回一个用于通信的套接字文件;

5) recv(), 接收客户端发来的数据;(read)    

//6) send(), 发送数据;(write)

  1. close(), 关闭文件描述符; 至少要关闭: 连接、通信

客户端(client):

  1. socket(),创建套接字文件,既用于连接,也用于通信; 

    填充结构体:  填充服务器的ip和端口 , 用于connect连接

2) connect(); 用于发起连接请求,阻塞等待连接服务器;

3) send(), 发送数据;

//4) recv(), 接收数据;

5)close(), 关闭文件描述符;

2、函数接口

1)socket 创建套接字

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建套接字
参数:
   domain:协议族
     AF_UNIX, AF_LOCAL  本地通信
     AF_INET            ipv4
     AF_INET6            ipv6
   type:套接字类型
     SOCK_STREAM:流式套接字
     SOCK_DGRAM:数据报套接字
   protocol:协议 - 填0 自动匹配底层 ,根据type系统默认自动帮助匹配对应协议
       传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
       网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
 返回值:
    成功 文件描述符 0 -> 标准输入  1->标准输出  2->标准出错 3->socket
    失败 -1,更新errno

2)bind 绑定   ipv4  ip和端口 

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:绑定   ipv4  ip和端口 
参数
   sockfd:文件描述符
   addr:通用结构体,根据socket第一个参数选择的通信方式最终确定这需要真正填充传递的结构体是那个类型。强转后传参数。
   addrlen:填充的结构体的大小   
返回值:成功0 失败-1、更新errno通用结构体:相当于预留一个空间
struct sockaddr 
{sa_family_t sa_family;char        sa_data[14];
}ipv4的结构体 struct sockaddr_in {sa_family_t    sin_family;  //协议族AF_INETin_port_t      sin_port;  //端口号struct in_addr sin_addr;  //ip地址};struct in_addr 11{uint32_t       s_addr;   //IP地址  }; 本地址通信结构体:struct sockaddr_un {sa_family_t sun_family;  //AF_UNIX  char        sun_path[108]; //在本地创建的套接字文件的路径及名字};ipv6通信结构体:
struct sockaddr_in6 
{sa_family_t     sin6_family;   in_port_t       sin6_port;     uint32_t        sin6_flowinfo; struct in6_addr sin6_addr;     uint32_t        sin6_scope_id; 
};
struct in6_addr 
{unsigned char   s6_addr[16];   
};
//如果绑定使用通用地址可使用 INADDR_ANY宏(结合代码讲的时候再添加)
含义是自动绑定所有本机网卡的地址,

11.2作业

3)listen 监听,将主动套接字变为被动套接字

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同时响应客户端请求链接的最大个数,不能写0.
不同平台可同时链接的数不同,一般写6-8
返回值:成功 0   失败-1,更新errno  

4)accept 阻塞函数

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);accept(sockfd,NULL,NULL);
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
accept()函数返回,返回一个用于通信的套接字文件描述符(4);
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;需要查看链接的客户端ip和端口号
     accept(sockfd,NULL,NULL);需要查看链接的客户端ip和端口号
     accept(sockfd,(struct sockaddr *)&saddr,&len);
  返回值: 
     成功:文件描述符; //用于通信
失败:-1,更新errno
同时返回客户端的ip和端口号。保存到结构体struct sockaddr *addr中。
通过结构体访问到:inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)
打印时记得使用 inet_ntoa()和ntohs()转换为主机字节序

5)recv 接收数据 

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据 
参数: 
    sockfd: acceptfd ;
    buf  存放位置
    len  大小
    flags  一般填0,相当于read()函数
    MSG_DONTWAIT  非阻塞
返回值: < 0  失败出错  更新errno==0  表示客户端退出>0   成功接收的字节个数

发送端协议类型为TCP Client,远程目标ip地址为服务器IP地址

端口号也应该一致

先关闭客户端,再关闭服务器。

6)connect 用于连接服务器;

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
     sockfd:socket函数的返回值
     addr:填充的结构体是服务器端的;
     addrlen:结构体的大小
返回值 -1 失败,更新errno
      正确 0 

7)send 发送数据,用来发送消息到一个套接字中。

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据,用来发送消息到一个套接字中。只能在套接字处于连接状态的时候才能使用。
参数:
    sockfd:socket函数的返回值
    buf:发送内容存放的地址
    len:发送内存的长度
    flags:如果填0,相当于write();
返回值;
    成功 发送的字节数
    失败 -1
可以使用write代替send,  都属于往发送缓存区内写入数据
read代替recv , 都属于往接收缓存区内提取数据

测试注意:

1. 如果使用客户端软件进行连接,必须保证windows和虚拟机在同一个局域网(桥接),并能互相ping通。服务器的IP地址必须指定为虚拟机自己的IP。

2. 必须保证客户端正常退出后在关闭服务器程序,在客户端连接状态情况下强制关闭服务器程序,下次启动服务器程序后会提示bind err。这是因为没有正常释放绑定的端口,等1~2分钟就可以了。

3、代码优化

1。端口和ip地址通过命令行传参到代码中。(如果参数不一致,应该提示并退出)
2。服务器端IP地址可由“0.0.0.0”自动获取,或者由宏定义自动获取

3。客户端发送去掉fgets获取的多余的'\n'.

fgets(实际读到的内容小于等于指定个数-1,自动读到的内容后添加’\0’,会将’\n’也读入)

  if(buf[strlen(buf)-1] == '\n')//去掉fgets获取的'\n'

         buf[strlen(buf)-1] ='\0';

4.设置来电显示功能,获取到请求链接服务器的客户端的ip和端口。

 int acceptfd = accept(sockfd,(struct sockaddr *)&caddr,&len);

打印时记得使用 inet_ntoa()和ntohs()转换为主机字节序

printf("client ip:%s ,port:%d\n", inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));

5、当客户端输入quit的时候,客户端退出

   strncmp

6.实现循环服务器,服务器不退出,当链接服务器的客户端退出,服务器等到下一个客户端链接。

  accept处使用while到接受数据结束。

/*服务器创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        printf("plase input <ip><port>\n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    //文件描述符 0 -> 标准输入  1->标准输出  2->标准出错  3->socket
    printf("sockfd:%d\n", sockfd);    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;            //协议族ipv4
    saddr.sin_port = htons(atoi(argv[1])); //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
//atoi(),字符串转整型
//saddr.sin_addr.s_addr = inet_addr(argv[1]);//ip地址,转化为16进制表示
#if 0
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//ip地址,转化为16进制表示
#else
    saddr.sin_addr.s_addr = INADDR_ANY;
#endif
    socklen_t len = sizeof(saddr); //结构体大小
    //bind绑定ip和端口
    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("bind err");
        return -1;
    }
    printf("bind success\n");
    //3.启动监听,把主动套接子变为被动套接字
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    printf("listen success\n");
    //4.阻塞等待客户端的链接请求
    int acceptfd;
    // acceptfd = accept(sockfd,NULL,NULL);
    while (1)
    {
        acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
        //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
        if (acceptfd < 0)
        {
            perror("accept err");
            return -1;
        }
        printf("client ip:%s ,port:%d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
        printf("connect success\n");
    //5.接收数据
    char buf[64];
    int ret;
    while (1)
    {
        ret = recv(acceptfd, buf, sizeof(buf), 0);
        if (strncmp(buf, "quit", 4) == 0) //接收到quit退出
        {
            break;
        }
        if (ret < 0)
        {
            perror("recv err.");
            return -1;
        }
        else if (ret == 0) //客户端退出
        {
            printf("client exit\n");
            break;
        }
        else
        {
            printf("buf:%s\n", buf);
        }
    }
    }
    close(sockfd);
    close(acceptfd);
    return 0;
}

/*客户端创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    if (argc < 3)
    {
        printf("plase input <ip><port>\n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    //文件描述符 0 -> 标准输入  1->标准输出  2->标准出错  3->socket
    printf("sockfd:%d\n", sockfd);
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;                 //协议族ipv4
    saddr.sin_port = htons(atoi(argv[2]));      //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    saddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址,转化为16进制表示
    socklen_t len = sizeof(saddr);              //结构体大小
    //3用于连接服务器;
    if (connect(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("connect err");
        return -1;
    }
    //4发送信息
    char buf[64] = {0};
    while (1)
    {
        fgets(buf, sizeof(buf), stdin);   //从终端获取内容存放到数组中
        if (strncmp(buf, "quit", 4) == 0) //输入quit退出客户端
        {
            break;
        }
        if (buf[strlen(buf)] == '\0')
        {
            buf[strlen(buf) - 1] = '\0';
        }                                  //将fgets自动补的\n去掉
        send(sockfd, buf, sizeof(buf), 0); //将数组中的内容发送到主机端
    }
    close(sockfd);
    // close()
    return 0;
}

ftp文件传输协议

tcp实现ftp功能:

模拟FTP核心原理:客户端连接服务器后,向服务器发送一个文件。文件名可以通过参数指定,服务器端接收客户端传来的文件(文件名随意),如果文件不存在自动创建文件,如果文件存在,那么清空文件然后写入。

项目功能介绍:

均有服务器和客户端代码,基于TCP写的。

在同一路径下,将客户端可执行代码复制到其他的路径下,接下来再不同的路径下运行服务器和客户端。

相当于另外一台电脑在访问服务器。

客户端和服务器链接成功后出现以下提示:四个功能

***************list**************//列出服务器所在目录下的文件名(除目录不显示)

***********put filename**********//客户端给服务器上传一个文件

***********get filename**********//客户端从服务器所在路径下载文件

**************quit***************//退出(可只退出客户端,服务器等待下一个客户端链接)

//读文件,读到最后一次,最后再发送end

//粘包:1.延时 2.读多少发多少

服务器

  1. 搭建tcp框架
  2. 服务器接收消息

//list 列出服务器所在目录下的文件名 目录操作 文件属性获取--发送给客户端

//put filename //接收文件 recv --》新建文件---》 写文件 

//get filename //读文件--》发送给客户端

//quit  while(1)accpet;

客户端

  1. 搭建tcp框架
  2. 列出功能
  3. 给服务器发消息

//list 接收--》列出服务器所在目录下的文件名

//put filename 找到文件--》读文件   -》发送

//get filename //接收文件 recv --》新建文件---》 写文件 

//quit   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/170960.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

macOS使用conda初体会

最近在扫盲测序的一些知识 其中需要安装一些软件进行练习&#xff0c;如质控的fastqc&#xff0c;然后需要用conda来配置环境变量和安装软件。记录一下方便后续查阅学习 1.安装miniconda 由于我的电脑之前已经安装了brew&#xff0c;所以我就直接用brew安装了 brew install …

优秀智慧园区案例 - 珠海华发智慧园区,万字长文解析先进智慧园区建设方案经验

一、项目背景 珠海华发产业园运营管理有限公司&#xff08;简称“产业园公司”&#xff09;是2016年起连续五年跻身“中国企业500强”、国务院国企改革“双百企业”的珠海华发集团旗下的实体产业发展载体运营平台&#xff0c;依托“四园一基地”&#xff1a;中以国际产业园、信…

如何用postman+jmeter实现接口实例

一、接口基础 为什么要单独测试接口&#xff1f; 1. 程序是分开开发的&#xff0c;前端还没有开发&#xff0c;后端已经开发完了&#xff0c;可以提前进入测试 2. 接口直接返回的数据------越底层发现bug&#xff0c;修复成本是越低的 3. 接口测试能模拟功能测试不能测到的异…

【Java 进阶篇】JQuery DOM操作:通用属性操作的绝妙魔法

在前端的舞台上&#xff0c;JQuery犹如一位魔法师&#xff0c;为我们展现了操纵HTML元素的奇妙技巧。而在这个技巧的精妙组成中&#xff0c;通用属性操作是一门绝妙的魔法。在本篇博客中&#xff0c;我们将深入研究JQuery DOM操作中的通用属性操作&#xff0c;揭示这段魔法的神…

C++——gcc、clang和cmake以及make

文章目录 1. CMake和make1.1 生成内容区别1.2 CMakeLists.txt和Makefile内容比较2. clang, gcc和make2.1 基本概念2.2 改进历史(gcc,make,cmake,Ninja)1. CMake和make 1.1 生成内容区别 环境CMake生成最终生成WindowsXXX.slnVisual Studio(MSBuild)处理.sln生成.exe可执行二进…

【架构】后端项目经典分层架构介绍

文章目录 前言分层架构项目实践示例项目结构 其他知识 前言 开发后端项目时&#xff0c;我们最常见的一种架构模式就是分层架构 。 所谓的分层架构&#xff0c;就是把系统自上而下分为多个不同的层&#xff0c;每一层都有特定的功能和职责&#xff0c;且只和自己的直接上层与…

如何使用CORS和CSP保护前端应用程序安全

前端应用在提供无缝用户体验方面起着核心作用。在当今互联网的环境中&#xff0c;第三方集成和API的普及使得确保强大的安全性至关重要。安全漏洞可能导致数据盗窃、未经授权访问以及品牌声誉受损。本文将向您展示如何使用CORS和CSP为您的网页增加安全性。 嗨&#xff0c;大家好…

国际化:i18n

什么是国际化&#xff1f; 国际化也称作i18n&#xff0c;其来源是英文单词 internationalization的首末字符和n&#xff0c;18为中间的字符数。由于软件发行可能面向多个国家&#xff0c;对于不同国家的用户&#xff0c;软件显示不同语言的过程就是国际化。通常来讲&#xff0…

企业微信开发教程一:添加企微应用流程图解以及常见问题图文说明

最近在前辈的基础上新添加了一个企微应用&#xff0c;过程中遇到了一些卡点&#xff0c;这里一一通过图片标注与注释的方式记录一下&#xff0c;希望能给后来人提供一些清晰明了的帮助&#xff0c;话不多说&#xff0c;大家直接看图吧。 &#xff08;文中包括一些本项目独有的配…

Labview利用声卡捕获波形

一般的计算机上自带的声卡&#xff0c;均既有A/D功能&#xff0c;又有D/A功能&#xff0c;就是一款具备基本配置的数据采集卡&#xff0c;并且技术成熟&#xff0c;性能稳定。 后台如下&#xff1a;

2023.11-9 hive数据仓库,概念,架构,元数据管理模式

目录 0.数据仓库和数据库 数据仓库和数据库的区别 数据仓库基础三层架构 一.HDFS、HBase、Hive的区别 二.大数据相关软件 三. Hive 的优缺点 1&#xff09;优点 2&#xff09;缺点 四. Hive 和数据库比较 1&#xff09;查询语言 2&#xff09;数据更新 3&#xff09;…

DevChat:开发者专属的基于IDE插件化编程协助工具

DevChat&#xff1a;开发者专属的基于IDE插件化编程协助工具 一、DevChat 的介绍1.1 DevChat 简介1.2 DevChat 优势 二、DevChat 在 VSCode 上的使用2.1 安装 DevChat2.2 注册 DevChat2.3 使用 DevChat 三、DevChat 的实战四、总结 一、DevChat 的介绍 在AI浪潮的席卷下&#x…