网络编程(一)网络通信的套路

一、相关函数说明

1、socket函数
函数功能:创建套接字,获取描述符
函数原型:int socket(int domain, int type, int protocol)
    参数:domain----->指明了协议族/域,通常AF_INET、AF_INET6、AF_LOCAL等.
           type----->是套接口类型,主要SOCK_STREAM、SOCK_DGRAM、SOCK_RAW.
           protocol->一般取为0。
  返回值:-1-------->失败
         成功则返回一个描述符

2、bind函数
函数功能:绑定套接字网络属性
函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    参数:sockfd------->socket函数获得的描述符
           addr------->网络属性结构体
        addrlen------->网络属性结构体内存大小
  返回值:0-------->成功
         -1------->失败

3、listen函数
函数功能:设置监听模式,最大可同时接收多少信息
函数原型:int listen(int sockfd, int backlog)
    参数:sockfd----->socket函数获得的描述符
          bocklog--->最大可同时接收多少信息
  返回值:0-------->成功
         -1------->失败

4、accept函数
函数功能:等待客户端信息到来
函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    参数:sockfd----->socket函数获得的描述符
            addr----->网络属性结构体(用于保存客户端网络属性,不要可以写 NULL)
            addrlen-->网络属性结构体内存大小
  返回值:-1------->失败
         成功则返回一个描述符用于通信

5、connect函数
函数功能:连接服务器,以便发送消息
函数原型:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    参数:sockfd----->socket函数获得的描述符
            addr----->服务器网络属性结构体
            addrlen-->服务器网络属性结构体内存大小
  返回值:0-------->成功
         -1------->失败

二、TCP 通信测试代码

代码功能:实现两机通信

2.1、服务器端

// 服务器---->接收端,接收来自客户端来的信息

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#include <sys/stat.h>
#include <sys/socket.h>
    
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> 




int main(int argc, char **argv)
{
	// 一、创建套接字 socket,获取描述符 fd
	int fd = socket(AF_INET, SOCK_STREAM, 0);

	// 二、设置服务器网络属性(未绑定)-->(端口号,IP)---->struct sockaddr_in
	struct sockaddr_in seraddr;
	socklen_t len = sizeof (seraddr);
	bzero(&seraddr, len);

	seraddr.sin_family = AF_INET;
	seraddr.sin_addr.s_addr = inet_addr("192.168.22.161");
	seraddr.sin_port = htons(50001);

	// 三、绑定服务器属性
	bind(fd, (struct sockaddr *)&seraddr, len);

	// 四、设置监听模式(最多可同时接收来自 N+4 客户端的信息),
	listen(fd, 3);

	// 五、等待客户端信息,并保存客户端网络属性(不想保存就写 NULL)
	struct sockaddr_in cliaddr;
	socklen_t clilen = sizeof (cliaddr);
	bzero(&cliaddr, clilen);

	int connfd = accept(fd, (struct sockaddr *)&cliaddr, &clilen);

	printf("from client ip: %s   port: %hu\n", 
							inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));

	// 六、接收信息
	char msg[100];
	while (1)
	{
		bzero(msg, 100);

		// 客户端退出,服务端也退出
		if(!read(connfd, msg, 100))
			break;
		printf("receive msg: %s", msg);
	}
	close(connfd);
	close(fd);
	return 0;
}

2.2、客户端

// 客户端,发送消息给服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#include <sys/stat.h>
#include <sys/socket.h>
    
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h> 

void useage(int argc, char **argv)
{
	if (argc != 3)
	{
		printf("use: %s IP PORT\n", argv[0]);
		exit(0);
	}

}

int main(int argc, char **argv)   
{
	useage(argc, argv);

	// 一、创建套接字,获取描述符 fd
	int fd = socket(AF_INET, SOCK_STREAM, 0);

	// 二、设置服务器(也就是接收端)的网络属性
	struct sockaddr_in seraddr;
	socklen_t serlen = sizeof(seraddr);
	bzero(&seraddr, serlen);

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

	// 三、连接服务器
	connect(fd, (struct sockaddr *)&seraddr, serlen);

	// 四,发送信息给服务器
	char msg[100];
	while (1)
	{
		bzero(msg ,100);
		fgets(msg, 100, stdin);
		write(fd, msg, 100);
	}
	close(fd);

	return 0;
}

三、UDP 通信测试代码

3.1、发送端

// 发送端,消息发送,但udp通信存在数据丢失,对于高质量通信建议不要使用

#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>

#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/fb.h>
#include <linux/un.h>

#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char **argv)
{
    if(argc != 3)
    {
        printf("please use : %s <IP> <PORT>\n", argv[0]);
        return -1;
    }

    // 一、创建 UDP 套接字,因为 UDP 无连接所以设置自己网络属性,只需要接收端网路属性
    int udpfd = socket(AF_INET, SOCK_DGR, 0);
    
    // 二、接收端网络属性设置
    struct sockaddr_in addr;
    socklen_t len = sizeof(addr);
    bzero(&addr, sizeof (addr));

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    addr.sin_port = htons(atoi(argv[2]));
    
    // 三、发送消息给对方
    char msg[100];
    while (1)
    { 
        bzero(msg, 100);
        fgets(msg, 100, stdin);
    
        sendto(udpfd, msg, strlen(msg), 0, (struct sockaddr *)&addr, len);
    }

    return 0;

}

3.2、接收端

// 接收端,接收来自发送端的消息,可能会有数据丢失
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>

#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/fb.h>
#include <linux/un.h>

#include <arpa/inet.h>
#include <netinet/in.h>


int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("please use : %s <PORT>\n");
        return -1;
    }

    // 一、创建 UDP 套接字
    int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
    // 二、 设置本机网络属性
    struct sockaddr_in addr;
    socklen_t len = sizeof (addr);
    bzero(&addr, len);

    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[1]));
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 自动获取本机 IP 

    // 三、套接字绑定
    bind(udpfd, (struct sockaddr *)&addr, len);

    // 四、创建保存对方网络属性的结构体
    struct sockaddr_in cliaddr;
    len = sizeof(cliaddr);

    
    // 五、等待对方消息到来
    char msg[100];
    while (1)
    {
        bzero(msg, 100);
        bzero(&cliaddr, len);

        recvfrom(fd, msg, 100, 0, (struct sockaddr *)cliaddr, &len);
        printf("from IP<%s> PORT<%d> msg: %s\n", inet_ntoa(cliaddr.sin_addr),
                                ntohs(cliaddr.sin_port), msg);
    }     
}

四、小结

        先总结一下,以上展示的是 TCP 通信的基本套路,除了以上套路外,还有一点简单的理解,其实不论是进程通信还是线程通信还是这里的 TCP 通信(网络通信的一个小代表),都必须要有一个在客户和服务端共享的资源。无论是进程通信的信号量,管道,共享内存还是线程通信的处理函数,还是网络通信的套接字。。。这些都是共享资源,可以在多个任务中(进程或者线程)对该共享资源进行相关操作。  

       对于数据的理解------>任何东西都可以表现成数据的形式,意思测试代码中的 msg 可以是一段文字,也可以是一张图片,一部电影,这些都是数据,一个举一反三,扩展已实现功能,扩展到同一类型事物。要求掌握的不仅仅是基本编码,而应该是深层次的东西。

猜你喜欢

转载自blog.csdn.net/qq_41985711/article/details/82732546