网络编程TCP与UDP

TCP与UDP

UDP头: 

包括源端口、目的地端口、用户数据包长度,检验和  数据。

typedef struct _UDP_HEADER 
{
 unsigned short m_usSourPort;       // 源端口号16bit
 unsigned short m_usDestPort;       // 目的端口号16bit
 unsigned short m_usLength;        // 数据包长度16bit
 unsigned short m_usCheckSum;      // 校验和16bit
}__attribute__((packed))UDP_HEADER, *PUDP_HEADER;

IP头:

UDP与TCP的区别

TCPUDP
有链接无链接
可靠传输(使用流量控制和拥塞控制)不可靠传输(不使用流量控制和用三个控制)
只能一对一一对一,一对多或者多对多
面向字节流面向报文
首部大小20~60字节首部大小8字节

SOCK_UDP

服务端代码:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);
int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    // man 7 ip 
    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");
    int ret = bind(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    socklen_t len = sizeof(cli);
    while(1)
    {
        char buf[512]={0};
        recvfrom(sockfd,buf,sizeof(buf),0,(SA)&cli,&len);
        time_t tm;
        time(&tm);
        sprintf(buf,"%s %s",buf,ctime(&tm));
        sendto(sockfd,buf,strlen(buf),0,(SA)&cli,len);
    }
    close(sockfd);
    return 0;
}

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
typedef struct sockaddr * (SA);


int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    // 大小端转化 host to net short 
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.203.128");

    while(1)
    {
        char buf[512]="hello,this is udp test";
        sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));
        bzero(buf,sizeof(buf));
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        printf("buf is %s\n",buf);
        sleep(1);
    }
    close(sockfd);
    return 0;
}

网络接口

socket

int socket(int domain, int type , int protocol);

socket作用:创建套接字。

domain: 协议族---> 指定了套接字所使用的协议类型(常用的协议族包括 AF_INET、AF_INET6、AF_UNIX等。其中,AF_INET 表示 IPv4 协议族,AF_INET6 表示 IPv6 协议族,AF_UNIX 表示 Unix 域协议族)

type:套接字类型---> 指定了套接字的数据传输方式(SOCK_STREAM 表示面向连接的流套接字,主要用于可靠传输数据,例如 TCP 协议。SOCK_DGRAM 表示无连接的数据报套接字,主要用于不可靠传输数据,例如 UDP 协议。)

protocol:协议类型--->指定了套接字所使用的具体的协议类型

bind

int bind( (int sockfd, const struct sockaddr *addr, socklen_t addrlen);

bind作用:绑定套接字,将套接字与网络地址绑7定。

sockfd:为套接字描述符。

addr 表示绑定的地址信息。

addrlen 是绑定地址信息的长度。

sendto

发送数据(会阻塞)

int sendto (int s, const void *buf, int len, unsigned int flags, const struct sockaddr *to, int tolen);

sendto作用:把UDP数据包发给指定地址。

s:              socket套接字描述符。
buf:         UDP数据报缓存地址。(把数据放在哪里存储,或者说发送的数据是什么)
len:          UDP数据报长度。(发送长度)
flags:       该参数一般为0。
to:            sendto()函数参数,struct sockaddr_in类型,指明UDP数据发往哪里报。
tolen:      对方地址长度,一般为:sizeof(struct sockaddr_in)。

recvfrom

接收数据(不会阻塞)

int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);

recvfrom()作用:从指定地址接收UDP数据报。

s:              socket套接字描述符。

buf:         UDP数据报缓存地址。(把数据放在哪里存储,或者说发送的数据是什么)

len:          UDP数据报长度。(接收长度)

fromlen:recvfrom()函数参数,struct sockaddr_in类型,指明从哪里接收UDP数据报。

struct sockaddr_in结构体

struct sockaddr_in {
short int sin_family;                      /* Address family */
unsigned short int sin_port;       /* Port number */
struct in_addr sin_addr;              /* Internet address */
unsigned char sin_zero[8];         /* Same size as struct sockaddr */
};

这个结构体里面包括:
sin_family  //代表协议族  在socket只指AF_INET

sin_port     //存储端口号

sin_addr    //存储IP地址

sin_zero    //是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。

listen监听套接字

int listen(int sockfd, int backlog);

功能: 在套接字id上监听等待链接

返回值:成功  0     ;     失败     -1

SOCK_TCP

服务端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <time.h>
typedef struct sockaddr* (SA);
int main(int argc, char *argv[])
{
   
    //监听套接字
    int listfd =  socket(AF_INET,SOCK_STREAM, 0);
    if(-1 == listfd)
    {
        perror("socket");
        exit(1);
    }

    struct sockaddr_in ser,cli;
    bzero(&ser,sizeof(ser));
    bzero(&cli,sizeof(cli));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(50000);
    //host to net long 
    ser.sin_addr.s_addr = htonl(INADDR_ANY);

    int ret = bind(listfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }
    //同一时刻三次握手排队数
    listen(listfd,3);
    socklen_t  len = sizeof(cli);
    //通信套接字 
    int conn = accept(listfd,(SA)&cli,&len);
    if(-1 == conn)
    {
        perror("accept");
        exit(1);
    }

    while(1)
    {
        char buf[512]={0};
        int rd_ret = recv(conn,buf,sizeof(buf),0);
        if(rd_ret<=0)
        {// 0  对方断开连接 -1 错误
            break;
        }
        time_t tm;
        time(&tm);
        sprintf(buf,"%s %s",buf,ctime(&tm));
        send(conn,buf,strlen(buf),0);
        
    }
    close(listfd);
    close(conn);
    return 0;
}

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
typedef struct sockaddr* (SA);

int main(int argc, char *argv[])
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in ser;
    bzero(&ser,sizeof(ser));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(50000);
    //host to net long 
    ser.sin_addr.s_addr = inet_addr("127.0.0.1");

    int ret = connect(sockfd,(SA)&ser,sizeof(ser));
    if(-1 == ret)
    {
        perror("connect");
        exit(1);
    }
    while(1)
    {
        char buf[512]="hello,this is tcp test";
        send(sockfd,buf,strlen(buf),0);
        bzero(buf,sizeof(buf));
        recv(sockfd,buf,sizeof(buf),0);
        printf("buf :%s\n",buf);
        sleep(1);
    }
    close(sockfd);
    return 0;
}

三次握手和四次挥手

TCP建立连接://三次握手

最开始客户端处于closed状态,服务端处于Listen状态。

        第一次握手:客户端发送一个SYN主动打开传输通道,并且说明客户端的初始化序列号ISN,这时客户端处于SYN_SENT状态。

        第二次握手:服务器收到客户端发送的SYN报文之后,会用自己的SYN报文作为应答,也指定了自己的初始化序列号ISN(s),同时还会发送ACK应答给客户端,(ACK = ISN + 1),表示自己收到了客户端的SYN,服务端处于SYN_RCVD状态。

        第三次握手:客户端收到SYN报文之后,会发送一个ACK报文,把服务器的ISN+1作为应答ACK的值,表示接收到了服务端的SYN,此时客户端和服务端都处于ESTABLISHED状态。此时建立起了连接。

三次握手目的

确保客户端和服务端的收发功能都是正常的。

TCP终止连接://四次挥手

客户端和服务端都可以主动发起挥手动作,若客户端先发送关闭请求,则过程如下:

        第一次挥手:客户端发送一个FIN报文,报文指定一个序列号,客户端变成FIN——WAIT1的状态。

        第二次挥手:服务端收到FIN之后,给客户端发送ACK报文,把客户端的序列号+1作为ACK回复,此时服务端进入CLOSE_WAIT状态。

        第三次挥手:服务端也要断开连接时,发送FIN报文+序列号给客户端,此时服务端进入LAST_ACK状态。

        第四次挥手: 服务端收到FIN之后发送ACK =  seq+1回复应答,此时客户端处于TIME_WAIT状态,等一段时间确保服务端收到自己的ACK报文之后才会进入CLOSED状态,而服务端收到应答之后直接进入CLOSED状态。

四次挥手目的

        保证客户端发送的最后一个ACK报文能到达服务端。确保所有的信息都传输完,最后发送结束信号,通知对方信息已经发完了再结束。

查看网络指令(在ubuntu)

1、ifconfig    查看自己的网络配置

2、netstat    -anp       查看当前活动的互联网连接

3、ping   测试网络命令

4、sudo ufw disable/enable             //防火墙关闭/打开

5、sudo apt-get install wireshark    //网络抓包工具   只有网络设备闭上有数据的收发就会放在这。

6、sudo wireshark     //使用抓包工具

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值