TCP-UDP-IP

本文详细介绍了TCP、UDP和IP协议的基本概念和特性,并通过C语言实现了TCP客户端和服务端的通信。TCP是面向连接、可靠的传输协议,提供应答机制、超时重传和错误校验等功能,适合文件传输;UDP则是无连接、快速的传输协议,适用于音视频流媒体等实时应用。同时,文章展示了如何将服务器改造成多线程模式,以同时处理多个客户端连接。
摘要由CSDN通过智能技术生成

(关于TCP-UDP-IP概述放在本文最后)

1.查看while源代码

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

#define portnumber 3333

int main(int argc, char* argv[]) {
        int local_socket;
        char buffer[1024];
        struct sockaddr_in server_addr;
        struct hostent* host;

        if (argc != 2) {
                fprintf(stderr, "Usage:%s hostname \a\n", argv[0]);
                exit(1);
        }

        /* 使用hostname查询host 名字 */
        if ((host = gethostbyname(argv[1])) == NULL) {
                fprintf(stderr, "ERR gethostbyname\n");
                exit(1);
        }

        /* 客户程序开始建立 local_socket描述符 */
        if ((local_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET:Internet;SOCK_STREAM:TCP
                fprintf(stderr, "ERR socket:%s\a\n", strerror(errno));
                exit(1);
        }

        /* 客户程序填充服务端的资料 */
        bzero(&server_addr, sizeof(server_addr)); // 初始化,置0
        server_addr.sin_family = AF_INET;          // IPV4
        server_addr.sin_port = htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口号
        server_addr.sin_addr = *((struct in_addr*)host->h_addr); // IP地址
        
        /* 客户程序发起连接请求 */
      
        if (connect(local_socket, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) == -1) {
                fprintf(stderr, "ERR connect:%s\a\n", strerror(errno));
                exit(1);
        }

        /* 连接成功了 */
        printf("Please typein a string:\n");

        /* 读取和发送数据 */
        fgets(buffer, 1024, stdin);
        write(local_socket, buffer, strlen(buffer));

        /* 结束通讯 */
        close(local_socket);
        exit(0);
}
                                                                      

2.编译并在Ubuntu上运行(附服务端和客户端代码)

操作截图如下:

 

client代码:

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

#define portnumber 3333

int main(int argc, char* argv[]) {
        int local_socket;
        char buffer[1024];
        struct sockaddr_in server_addr;
        struct hostent* host;

        if (argc != 2) {
                fprintf(stderr, "Usage:%s hostname \a\n", argv[0]);
                exit(1);
        }

        /* 使用hostname查询host 名字 */
        if ((host = gethostbyname(argv[1])) == NULL) {
                fprintf(stderr, "ERR gethostbyname\n");
                exit(1);
        }

        /* 客户程序开始建立 local_socket描述符 */
        if ((local_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET:Internet;SOCK_STREAM:TCP
                fprintf(stderr, "ERR socket:%s\a\n", strerror(errno));
                exit(1);
        }

        /* 客户程序填充服务端的资料 */
        bzero(&server_addr, sizeof(server_addr)); // 初始化,置0
        server_addr.sin_family = AF_INET;          // IPV4
        server_addr.sin_port = htons(portnumber);  // (将本机器上的short数据转化为网络上的short数据)端口
号
        server_addr.sin_addr = *((struct in_addr*)host->h_addr); // IP地址

        /* 客户程序发起连接请求 */
        if (connect(local_socket, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr)) == -1) {
                fprintf(stderr, "ERR connect:%s\a\n", strerror(errno));
                exit(1);
        }

        /* 连接成功了 */
        printf("Please typein a string:\n");

        /* 读取和发送数据 */
        fgets(buffer, 1024, stdin);
        write(local_socket, buffer, strlen(buffer));

        /* 结束通讯 */
        close(local_socket);
        exit(0);
}

serve代码:

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

#define portnumber 3333

int main(int argc, char* argv[]) {
        int local_listen_socket, server_session_socket;
        struct sockaddr_in server_addr_info_struct;
        struct sockaddr_in client_addr_info_struct;
        int size_of_sockaddr_in;
        int read_got_bytes_nr;
        char buffer[1024];


        /* socket: 服务器端开始建立sockfd描述符 */
        if ((local_listen_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) { // AF_INET i.e. IPV4; SOCK_STREAM i.e. TCP
                fprintf(stderr, "Socket error:%s\n\a", strerror(errno));
                exit(1);
        }

        /* 准备 sockaddr结构及其内部IP、端口信息 */
        bzero(&server_addr_info_struct, sizeof(struct sockaddr_in)); // 初始化,置0
        server_addr_info_struct.sin_family = AF_INET;                 // Internet
        server_addr_info_struct.sin_addr.s_addr = htonl(INADDR_ANY);  // 将本机host上的long数据转化为网>络上的long数据,使服务器程序能运行在不同CPU的主机上 

// INADDR_ANY 表示主机监听任意/所有IP地址。
        //server_addr_info_struct.sin_addr.s_addr=inet_addr("192.168.1.1");  //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
        server_addr_info_struct.sin_port = htons(portnumber);         // (将本机器上的short数据转化为网>络上的short数据)端口号

        /* bind: 绑定sockfd描述符 和 IP、端口 */
        if (bind(local_listen_socket, (struct sockaddr*)(&server_addr_info_struct), sizeof(struct sockaddr)) == -1) {
                fprintf(stderr, "ERR bind():%s\n\a", strerror(errno));
                exit(1);
        }

        /* 设置允许连接的最大客户端数 */
        if (listen(local_listen_socket, 5) == -1) {
                fprintf(stderr, "ERR listen():%s\n\a", strerror(errno));
                exit(1);
        }

        while (1) {
                size_of_sockaddr_in = sizeof(struct sockaddr_in);
                fprintf(stderr, "Listening & Accepting...\n");
                if ((server_session_socket = accept(local_listen_socket, (struct sockaddr*)(&client_addr_info_struct), &size_of_sockaddr_in)) == -1) {  // 服务器阻塞, 直到接受到客户连接
                        fprintf(stderr, "ERR accept():%s\n\a", strerror(errno));
                        exit(1);
                }
      }


                fprintf(stderr, "Got connection from %s\n", inet_ntoa(client_addr_info_struct.sin_addr)); // 网络地址 转换成 字符串
                if ((read_got_bytes_nr = read(server_session_socket, buffer, 1024)) == -1) {
                        fprintf(stderr, "ERR read():%s\n", strerror(errno));
                        exit(1);
                }
                buffer[read_got_bytes_nr] = '\0';
                printf("Server received %s\n", buffer); /* 这个对话服务已经结束 */
                close(server_session_socket); /* 下一个 */
        }

        /* 结束通讯 */
        close(local_listen_socket);
        exit(0);
}

3.修改服务器为多线程模式

       意义:服务器改为多线程模式以后就可以同时间监听多个客户端的命令

       在此采用了两台虚拟机,在同一个网络下,用三个客户端对服务器进行通信,并且附操作截图如下:

 

 

 

 4.TCP/UDP/IP

4.1TCP

TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。TCP通信需要经过创建连接、数据传送、终止连接三个步骤。

特点:(1)面向连接,可以间接验证ip地址的有效性
           (2)应答机制: 对方收到消息,底层会回复
           (3)超时重传:对方收到数据没有回复那么会再次给对方发送数据,如果对方一直不回复那么会认为对方掉线
           (4)错误校验: 比如接收的数据的序号和发送时候的数据序号不一致,那么tcp会对数据包进行自动排序, 如果收到重复的数据包会删除
           (5)流量控制: 对方发送大量数据如果接收方网卡缓存区达到一定上限,那么就不让对方发送数据,等接收方把数据处理完以后再发送,保证电脑接收数据不会卡死

4.2UDP

用户数据报协议,属于传输层的协议,无连接,不保证传输的可靠性。对于来自应用层的数据包,直接加上UDP报头然后传送给IP。UDP头部中有一个校验和字段,可用于差错的检测,但是UDP是不提供差错纠正的。此外IPV4不强制这个校验和字段必须使用,但IPV6是强制要求使用的。

特点:(1)开销更小,TCP为了保证其可靠性,首部包含20字节,以及40字节的可选项,UDP首部只有8字节

             (2)速度更快,UDP发送数据之前没有TCP的连接建立过程;而TCP则提供了过多的保护,牺牲了及时性,比如:控制微包(Nagle算法)、延时ACK、流量控制、超时重传等,这些设计严重影响了Tcp的速度和及时性

TCP和UDP的区别:

(1)TCP面向连接, UDP不面向连接
(2)TCP可靠的传输协议,UDP 不可靠
(3)TCP 应答机制, UDP没有
(4)TCP 超时重传,UDP没有
(5)TCP 流量控制, UDP没有
(6)TCP 错误校验, UDP没有
  (7)UDP适合做广播,TCP不适合
  (8)UDP传输速度比TCP传输速度要快, UDP占用的资源要比TCP的占用资源要少
  (9)UDP 每次发送的数据包不能太大,上限是64k, TCP理论上没有限制 —扩展
  (10)TCP适合文件的上传和下载,绝大多数应用都是使用TCP的
  (11)UDP 适合音视频,比如qq,微信的传输,以及飞秋上线广播等等

4.3IP

IP协议:IP协议是TCP/IP协议的核心,所有的TCP,UDP,IMCP,IGMP的数据都以IP数据格式传输。要注意的是,IP不是可靠的协议,这是说,IP协议没有提供一种数据未传达以后的处理机制,这被认为是上层协议:TCP或UDP要做的事情。

IP地址:在数据链路层中我们一般通过MAC地址来识别不同的节点,而在IP层我们也要有一个类似的地址标识,这就是IP地址。32位IP地址分为网络位和地址位,这样做可以减少路由器中路由表记录的数目,有了网络地址,就可以限定拥有相同网络地址的终端都在同一个范围内,那么路由表只需要维护一条这个网络地址的方向,就可以找到相应的这些终端了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值