网络编程 (传输层 -- UDP协议)

本文借鉴【Linux】网络编程套接字——UDP协议
sockaddr结构体:

socket API是一层抽象的网络编程接口,适用于底层各种网络协议,IPv4、IPv6等,而每一种网络协议的地址格式并不相同。
IPv4地址用sockaddr_in结构体表示,包括16位地址类型,16位端口号和32位IP地址。IPv4、IPv6的地址类型为AF_INET、AF_INET6,这样只要得到某种sockaddr结构体的首地址,不需要知道是具体哪种类型的sockade结构一条,就可以根据地址类型字段确定结构体中的内容。
socket API中的参数都是struct sockaddr*类型,在使用时需要强制类型转换成sockaddr_in;这样的好处是程序的通用性。

函数原型:int socket(int domain, int type, int protocol);
在参数表中,domain指定使用何种的地址类型,比较常用的有:
PF_INET, AF_INET: Ipv4网络协议;
PF_INET6, AF_INET6: Ipv6网络协议。
type参数的作用是设置通信的协议类型,可能的取值如下所示:
SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议。
OOB: 在所有数据传送前必须使用connect()来建立连接状态。
SOCK_DGRAM: 使用不连续不可靠的数据包连接,即UDP协议。
SOCK_SEQPACKET: 提供连续可靠的数据包连接。
SOCK_RAW: 提供原始网络协议存取。
SOCK_RDM: 提供可靠的数据包连接。
SOCK_PACKET: 与网络驱动程序直接通信。
参数protocol用来指定socket所使用的传输协议编号。这一参数通常不具体设置,一般设置为0即可

socket编程接口:(创建通信端点,并返回描述符)
创建 socket ⽂件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);

参数:domain表示域,当前要使用的协议簇,type(类型)表示使用哪种服务protocol(协议)设置为0
返回值:调用成功返回文件描述符,失败返回-1

bind (绑定名称到套接字)
int bind(in sockfd, const struct sockaddr *addr(地址),  socklen_t addrlen(地址长度))

recvfrom(从套接字接收数据)
ssize_t recvfrom(int sockfd,  void* buf,  size_t len,  int flags, \
struct sockaddr* src_addr,  socklen_t* addrlen);

sendto(在套接字中发送信息)
ssize_t sendto(int sockfd,  const void* buf,  size_t len, int flags, \
struct sockaddr* src_addr,  socklen_t* addrlen);

UDP通信:无连接的数据报传输协议。
特点:不可靠,不稳定,容易丢包,但传输速度快。(支持一对一,一对多,多对一和多对多的交互通信)
应用场合:周期性状态信息,图片和视频数据传输;区域网内数据传输。

这段程序是这样的:从客户端上sendto(发送)一条消息到服务器端,然后服务器端将收到的消息回显到客户端。

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>


int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("error: 需要 %s port端口 ip地址 \n", argv[0]);
        return 1;
    }
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        perror("socket error");
        return 2;
    }
    struct sockaddr_in peer;
    peer.sin_family = AF_INET;
    peer.sin_port = htons(atoi(argv[1]));//prot端口例:8080 端口号是一个2字节16位的整数
    peer.sin_addr.s_addr = inet_addr(argv[2]);//ip地址例:127.0.0.1

    if(bind(sock, (struct sockaddr*)&peer, sizeof(peer)) < 0)
    {
        perror("bind");
        return 3;
    }

    char buf[1024];
    struct sockaddr_in server;
    while(1)
    {
        socklen_t len = sizeof(server);
        printf("Please Enter# ");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s > 0)
        {
            buf[s-1] = 0;
            sendto(sock, buf, sizeof(buf), 0, (struct sockaddr*)&peer, sizeof(peer));
            ssize_t _s = recvfrom(sock, buf, sizeof(buf)-1, 0, (struct sockaddr*)&server, &len);
            if(_s > 0)
            {
                buf[_s] = 0;
                printf("server echo: %s\n", buf);
            }
        }
    }
    return 0;

}

服务端:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>

int createsock(int port, const char* ip)
{
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if(sock < 0)
    {
        perror("socket");
        return 2;
    }
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);//prot端口例:8080 端口号是一个2字节16位的整数
    server.sin_addr.s_addr = inet_addr(ip);//127.0.0.1

    if(bind(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
    {
        perror("bind");
        return 3;
    }
    return sock;
}

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("error:需要%s port端口 ip地址\n", argv[0]);
        return 1;
    }
    int sock = createsock(atoi(argv[1]), argv[2]);
    char buf[1024];
    struct sockaddr_in local;
    while(1)
    {
        socklen_t len = sizeof(local);
        ssize_t s = recvfrom(sock, buf, sizeof(buf)-1, 0, (struct sockaddr*)&local, &len);
        if(s > 0)
        {
            buf[s] = 0;
            printf("[%s:%d]: %s\n", inet_ntoa(local.sin_addr), ntohs(local.sin_port), buf);
            sendto(sock, buf, sizeof(buf), 0, (struct sockaddr*)&local, len);
        }
    }
    return 0;
}

.PHONY:all  //伪目标 .PHONY
all:client server

$@//表示规则中目标
$^//规则中的所有依赖条件
client:client.c  
	gcc -o $@ $^

server:server.c
	gcc  -o $@ $^

.PHONY:
clean:
	rm -f client  server
/* 当工程中的文件名和makefile中的目标重名时,就会有伪目标。
执行make命令时会发现提示目标文件已经是最新的了,将不被不执行!
如果我想让makefile中某个命令永远被执行。
可以在makefile目标前加上.PHONY:'目标名'*/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值