简单的UDP网络程序

sockaddr结构:

struct sockaddr{
    _SOCKADDR_COMMON(sa_);  //Common data:address family and length.
    char sa_data[14];//Address data.
};

sockaddr_in结构:

//Structure describing an Internet socket address.
struct sockaddr_in{
    _SOCKADDR_COMMON(sin_);
    in_port_t sin_port;  //Port number.
    struct in_addr sin_addr;  //Internet address.
    //Pad to size of 'struct sockaddr'.
    unsigned char sin_zero[sizeof(struct sockaddr) - _SOCKADDR_COMMON_SIZE-sizeof(in_port_t) - sizeof(struct in_addr)];
};

虽然socket api的接⼝是sockaddr,但是我们真正在基于 IPv4编程时,使⽤的数据结构是sockaddr_in; 这个结构⾥主要有三部分信息 :地址类型,端⼝号, IP地址。

in_addr结构:

//Internet address.
typedef unit32_t in_addr_t;
struct in_addr{
    in_addr_t s_addr;
};

in_addr⽤来表⽰⼀个IPv4的IP地址.其实就是⼀个32位的整数。

UDP服务器
client.c

//UDP版本的客户端                                                                                                                                                                              
//1.从标准输入,读入一个字符
//2.把这个字符串通过socket发送给服务器
//3.尝试从服务器读取响应数据
//4.把响应结果写到标准输出上。

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

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;


//通过命令行参数,指定客户端向哪个服务器发送请求。
//./client [ip] [port]

int main(int argc, char* argv[]){
    if(argc != 3){
        printf("Usage ./client [ip] [port]\n");
        return 1;
    }
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0){
        perror("socket");
        return 1;
    }

    sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    server_addr.sin_port = htons(atoi(argv[2]));

    while(1){
        //stdin,stdout,stderr
        //1.从标准输入读数据
        printf("> ");
        fflush(stdout);
        char buf[1024] = {0};
        ssize_t read_size = read(0,buf,sizeof(buf) - 1);
        if(read_size < 0){
            perror("read");
            goto END;
        }
        if(read_size == 0){
            printf("read done\n");
            goto END;
        }
        buf[read_size] = '\0';

        //2.把数据发送给服务器
        sendto(sock,buf,strlen(buf),0,(sockaddr*)&server_addr,sizeof(server_addr));

        //3.尝试从服务器读取数据,此时recvfrom不需要知道
        //对端的ip和端口号,因为收到的数据一定是服务器发回来的。
        //而服务器对应的ip 端口号,已知。
        char buf_output[1024] = {0};
        read_size = recvfrom(sock,buf_output,sizeof(buf_output) - 1,0,NULL,NULL);

        //6.把读取到的结果写到标准输出上
        printf("server resp %s\n",buf_output);
    }
END:
    close(sock);
    return 0;
}                                                       

server.c

//UDP服务端                                                                                                                                                                                    
//1.尝试从socket中读取客户端发送的请求(字符串)
//2.读到请求之后,根据请求内容,计算生成响应(字符串)
//3.把响应写回到socket之中,在传递给客户端


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

typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;

//服务器可执行程序 ip地址 端口号
//环回ip
//端口号最好大于1024,1-1023
//并且和当前主机上其他进程使用的端口号不重复
//./server 127.0.0.1  9090


int main (int argc,char* argv[]){
    if(argc != 3){ 
        printf("Usage ./server [ip] [port]\n");
        return 1;
    }   
    //AF_INET 表示ipv4版本的协议
    //SOCK_DGRAM表示创建的socket要使用面向数据报的方式传输数据


    //创建套接字
    int sock = socket(AF_INET,SOCK_DGRAM,0);
    if(sock < 0){ 
    return  1;
    }

    //命名套接字
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    //inet_addr,完成了把一个点分十进制的字符串ip地址转换成网络字节序的数字ip地址
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    addr.sin_port = htons(atoi(argv[2]));

    //绑定端口号
    int ret = bind(sock,(sockaddr*)&addr,sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 1;
    }

    //服务器接收客户信息
    while(1){
        sockaddr_in peer;
        socklen_t len = sizeof(peer);
        char buf[1024] = {0};  //缓冲区
        ssize_t read_size = recvfrom(sock,buf,sizeof(buf) - 1,0,(sockaddr*)&peer,&len);
        if(read_size < 0){
            perror("recvfrom");
            //接受失败之后,如何和处理这个错误合适?
            continue;
        }
        buf[read_size] = '\0';
        printf("[%s:%d] %s\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port),buf);

        //对于一个正常的服务器来说,收到了用户的请求之后
        //就要根据用户请求进行计算,得到结果响应给客户端。
        //由于此处写的是一个回显服务器,此时没有计算的过程                                                                                                                                     
        //直接把结果写给客户端。

        sendto(sock,buf,strlen(buf),0,(sockaddr*)&peer,sizeof(peer));
       }

    //关闭文件描述符
    close(sock);
    return 0;
}                

Makefile

.PHONY:all
all:server client

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

.PHONY:clean
clean:
    rm server client

运行结果:
这里写图片描述
这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值