ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C语言网络编程(1)— UDP通信(这篇写得很详细,也讲了怎么借助网络调试助手)

2021-07-28 13:29:56  阅读:180  来源: 互联网

标签:UDP addr struct 网络 C语言 sockfd include sin socket


这篇写得很详细,也讲了怎么借助网络调试助手,而且从这篇来看,写起来确实挺简单的。

转载自:https://blog.csdn.net/qq_38113006/article/details/105531439

C语言网络编程(1)— UDP通信

Willliam_william 2020-04-15 13:56:37 1465 收藏 22

分类专栏: C语言网络编程

版权

C语言网络编程(1)— UDP通信

一、socket

我们要进行网络通信,那么就要用到socket,socket即网络套接字,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。
在 C语言中,有支持socket 的库,使用库里的socket()函数 就可以创建一个socket对象,socket()函数原型是

int socket(int domain, int type, int protocol);
  1. 其中domain参数是指协议域,又称为协议族(family),常用的协议族有,AF_INET、AF_INET6、…等等,AF_INET指ipv4,AF_INET6即为ipv6;
  2. 然后是type,type指定socket类型,有SOCK_STREAM(流式套接字,主要用于 TCP 协议)和SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)等等;
  3. protocol就是指定的协议,常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议,但是type和proto不可以随意组合,当protocol参数为0时,会自动选择type类型对应的默认协议。

二、UDP发送数据

首先我们添加要使用的头文件

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

创建一个udp套接字,ipv4协议,使用SOCK_DGRAM参数,protocol为0,就会默认自动选择udp协议;

	// 1、使用socket()函数获取一个socket文件描述符
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

然后我们把要接收数据的那一端的ip地址和端口号放在一个结构体里准备好

	// 2. 准备接收方的地址和端口,'192.168.0.107'表示目的ip地址,8080表示目的端口号 
    struct sockaddr_in sock_addr = {0};	
	sock_addr.sin_family = AF_INET;                         // 设置地址族为IPv4
	sock_addr.sin_port = htons(8266);						// 设置地址的端口号信息
	sock_addr.sin_addr.s_addr = inet_addr("192.168.0.107");	// 设置IP地址

准备好后就可以使用sendto函数进行发送了,对于sendto()函数,成功则返回实际传送出去的字符数,失败返回-1,

    // 3. 发送数据到指定的ip和端口
	char sendbuf[]={"hello world."};
	ret = sendto(sockfd, sendbuf, sizeof(sendbuf), 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr));

发送完就可以关闭套接字了

    // 4. 关闭套接字
    close(sockfd);

打开网络调试助手,编译,运行程序,可以看到,数据发送成功,且网络调试助手已经接收到数据了,
注意,如果不是在本机windows系统上运行python程序,在Ubuntu虚拟机或者其他局域网内的机器上运行,要把windows的防火墙关了,重要的事说三遍!!!
注意,如果不是在本机windows系统上运行python程序,在Ubuntu虚拟机或者其他局域网内的机器上运行,要把windows的防火墙关了,重要的事说三遍!!!
注意,如果不是在本机windows系统上运行python程序,在Ubuntu虚拟机或者其他局域网内的机器上运行,要把windows的防火墙关了,重要的事说三遍!!!
在这里插入图片描述
在这里插入图片描述
然后我们让其每隔一秒发送一次,发送10次,发送成功
在这里插入图片描述
贴上完整代码

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

int main(void)
{
	int ret = -1;
	// 1、使用socket()函数获取一个socket文件描述符
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		printf("socket open err.");
		return -1;
	}

	// 2. 准备接收方的地址和端口,'192.168.0.107'表示目的ip地址,8266表示目的端口号 
    struct sockaddr_in sock_addr = {0};	
	sock_addr.sin_family = AF_INET;                         // 设置地址族为IPv4
	sock_addr.sin_port = htons(8266);						// 设置地址的端口号信息
	sock_addr.sin_addr.s_addr = inet_addr("192.168.0.107");	// 设置IP地址

    // 3. 发送数据到指定的ip和端口
	char sendbuf[]={"hello world, I am UDP."};
    int cnt = 10;
    while(cnt--)
    {
        ret = sendto(sockfd, sendbuf, sizeof(sendbuf), 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
	    printf("ret = %d \n",ret);
        sleep(1);
    }

    // 4. 关闭套接字
    close(sockfd);
}

三、UDP接收数据

在之前发送数据的时候,我们可以看到,其端口号是一直在变得,那么我们要接收数据,就需要知道其端口号是什么,所以我们要先固定一个端口号,使用bind函数

    // 2、绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号
    struct sockaddr_in local_addr = {0};
    local_addr.sin_family = AF_INET;                                //使用IPv4地址
    local_addr.sin_addr.s_addr = inet_addr("192.168.0.107");        //本机IP地址
    local_addr.sin_port = htons(12341);                             //端口
    bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr));//将套接字和IP、端口绑定

接收数据使用recvfrom函数,第一个参数位socket 文件描述符,第二个参数为接收缓冲区,第三参数为最大接收数据长度,第四个参数一般为零,第五个参数为发送来数据的对方的地址及端口信息;

    // 3、等待接收对方发送的数据
    struct sockaddr_in recv_addr;
    socklen_t addrlen = sizeof(recv_addr);
    char recvbuf[1024] = {0};
    ret = recvfrom(sockfd, recvbuf, 1024, 0,(struct sockaddr*)&recv_addr,&addrlen);  //1024表示本次接收的最大字节数

接收完后将其打印出来:

    printf("[recv from %s:%d]%s \n",inet_ntoa(*(struct in_addr*)&recv_addr.sin_addr.s_addr),ntohs(recv_addr.sin_port),recvbuf);

运行结果:
在这里插入图片描述
贴上完整代码

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

int main(void)
{
	int ret = -1;
	// 1、使用socket()函数获取一个socket文件描述符
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		printf("socket open err.");
		return -1;
	}

    // 2、绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号
    struct sockaddr_in local_addr = {0};
    local_addr.sin_family = AF_INET;                                //使用IPv4地址
    local_addr.sin_addr.s_addr = inet_addr("192.168.0.107");        //本机IP地址
    local_addr.sin_port = htons(12341);                             //端口
    bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr));//将套接字和IP、端口绑定

    // 3、等待接收对方发送的数据
    struct sockaddr_in recv_addr;
    socklen_t addrlen = sizeof(recv_addr);
    char recvbuf[1024] = {0};
    ret = recvfrom(sockfd, recvbuf, 1024, 0,(struct sockaddr*)&recv_addr,&addrlen);  //1024表示本次接收的最大字节数
    printf("[recv from %s:%d]%s \n",inet_ntoa(*(struct in_addr*)&recv_addr.sin_addr.s_addr),ntohs(recv_addr.sin_port),recvbuf);

    // 4. 关闭套接字
    close(sockfd);
}

四、UDP收发数据

实现这样一个功能,通过UDP发送10次消息,然后等待接收,将接收的数据及其来源打印出来:
在这里插入图片描述
在这里插入图片描述
完成代码

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

int main(void)
{
	int ret = -1;
	// 1、使用socket()函数获取一个socket文件描述符
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		printf("socket open err.");
		return -1;
	}

    // 2、绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号
    struct sockaddr_in local_addr = {0};
    local_addr.sin_family = AF_INET;                                //使用IPv4地址
    local_addr.sin_addr.s_addr = inet_addr("192.168.0.107");        //本机IP地址
    local_addr.sin_port = htons(12341);                             //端口
    bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr));//将套接字和IP、端口绑定  

    // 3. 发送数据到指定的ip和端口,'192.168.0.107'表示目的ip地址,8266表示目的端口号 
    struct sockaddr_in sock_addr = {0};	
	sock_addr.sin_family = AF_INET;                         // 设置地址族为IPv4
	sock_addr.sin_port = htons(8266);						// 设置地址的端口号信息
	sock_addr.sin_addr.s_addr = inet_addr("192.168.0.107");	// 设置IP地址
	char sendbuf[]={"hello world, I am a UDP socket."};
    int cnt = 10;
    while(cnt--)
    {
        ret = sendto(sockfd, sendbuf, sizeof(sendbuf)-1, 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
	    //printf("ret = %d \n",ret);
        sleep(1);
    }

    // 4、等待接收对方发送的数据
    struct sockaddr_in recv_addr;
    socklen_t addrlen = sizeof(recv_addr);
    char recvbuf[1024] = {0};
    while(1)
    {
        ret = recvfrom(sockfd, recvbuf, 1024, 0,(struct sockaddr*)&recv_addr,&addrlen);  //1024表示本次接收的最大字节数
        printf("[recv from %s:%d]%s \n",inet_ntoa(*(struct in_addr*)&recv_addr.sin_addr.s_addr),ntohs(recv_addr.sin_port),recvbuf);
    }
    
    // 5. 关闭套接字
    close(sockfd);
}

五、同时收发数据

现在实现这样一个功能,即运行程序,然后将我输入的字符串发送出去的同时,还可以接收数据,我使用多线程来实现这个程序,不过要实现方便接收,我们在程序的开始,将IP地址和端口号打印出来,实现效果如下:
在这里插入图片描述
在这里插入图片描述
实现代码
注意:因为pthread并非Linux系统的默认库,而是POSIX线程库。在Linux中将其作为一个库来使用,因此加上 -lpthread(或-pthread)以显式链接该库。

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

void *recv_thread(void *arg)
{
    int socket_fd = *(int *)arg;
    struct sockaddr_in recv_addr;
    socklen_t addrlen = sizeof(recv_addr);
    char recvbuf[1024] = {0};
    while(1)
    {
        recvfrom(socket_fd, recvbuf, 1024, 0,(struct sockaddr*)&recv_addr,&addrlen);  //1024表示本次接收的最大字节数
        printf("[recv from %s:%d]%s \n",inet_ntoa(*(struct in_addr*)&recv_addr.sin_addr.s_addr),ntohs(recv_addr.sin_port),recvbuf);
    }       
}
int main(void)
{
	int ret = -1;
    pthread_t th = -1;
	// 1、使用socket()函数获取一个socket文件描述符
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		printf("socket open err.");
		return -1;
	}

    // 2、绑定本地的相关信息,如果不绑定,则系统会随机分配一个端口号
    struct sockaddr_in local_addr = {0};
    local_addr.sin_family = AF_INET;                                //使用IPv4地址
    local_addr.sin_addr.s_addr = inet_addr("192.168.0.107");        //本机IP地址
    local_addr.sin_port = htons(12341);                             //端口
    bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr));//将套接字和IP、端口绑定  

    // 3、打印本机ip地址和端口号
    printf("local ipaddr and port->192.168.0.107:12341\n");

    // 4、创建一个线程,用来接收数据
    ret = pthread_create(&th, NULL, recv_thread, &sockfd); 


    // 5、等待输入数据,然后发送出去,直到输入的数据为'quit','192.168.0.107'表示目的ip地址,8266表示目的端口号
    struct sockaddr_in sock_addr = {0};	
	sock_addr.sin_family = AF_INET;                         // 设置地址族为IPv4
	sock_addr.sin_port = htons(8266);						// 设置地址的端口号信息
	sock_addr.sin_addr.s_addr = inet_addr("192.168.0.107");	// 设置IP地址
	char sendbuf[1024]={"hello world, I am a UDP socket."};
    int cnt = 10;
    while(cnt--)
    {
        printf("please input a string.input 'quit' to quit.\n");
        scanf("%s",sendbuf);
        if(strcmp(sendbuf,"quit") == 0)
            break;
        sendto(sockfd, sendbuf, strlen(sendbuf), 0, (struct sockaddr*)&sock_addr, sizeof(sock_addr));
    }    
    
    // 6. 关闭套接字
    close(sockfd);
}


Willliam_william

标签:UDP,addr,struct,网络,C语言,sockfd,include,sin,socket
来源: https://blog.csdn.net/sinat_16643223/article/details/119177283

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有