linux基础操作之8.套接字网络通讯tcp/udp

导:网络套接字编程-TCP/UDP开发实例

百科资料:http://www.baike.com/wiki/%E5%A5%97%E6%8E%A5%E5%AD%97

 

1.TCP通讯实例=====================================================================

    TCP 是一种面向连接的、可靠的、基于 IP 的传输层协议。通过 TCP 可以保证我们传送的数据的正确性。

    (1)通讯流程梳理

Linux 下网络通信程序基本上都是采用 socket 的方式。socket 起源于 Unix,而Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开 open->读写 read/write->关闭 close”模式来操作。Socket 就是该模式的一个实现,socket 即是一种特殊的文件,一些socket 函数就是对其进行的操作(开/关/读/写/建)。说白了 socket 是应用程序与 TCP/IP协议族通信的中间软件抽象层,它是一组接口。    

    (2)代码分析

                              服务器端:SBLARWRC

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
	int sfp, nfp, num = 0;
	struct sockaddr_in s_add,c_add;
	int sin_size;
	unsigned short portnum=0x8888;//端口1对1
	char buffer[100] = {0};
	printf("Hello,welcome to my server !\r\n");

	sfp = socket(AF_INET, SOCK_STREAM, 0);		//1.s-调用 socket 函数创建一个套接字
	if(-1 == sfp)
	{
			printf("socket fail ! \r\n");
			return -1;
	}
	printf("socket ok !\r\n");
	bzero(&s_add,sizeof(struct sockaddr_in));
	s_add.sin_family=AF_INET;使用 IPv4 协议
	s_add.sin_addr.s_addr=htonl(INADDR_ANY);允许任何地址
	s_add.sin_port=htons(portnum);设置端口号
							//2.b-用bind 绑定函数,使用的是 IPv4 协议族
	if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
	{
			printf("bind fail !\r\n");
			return -1;
	}
	printf("bind ok !\r\n");
	if(-1 == listen(sfp,5))				//3.l-调用 listen 监听函数,监听用户的连接请求
	{
			printf("listen fail !\r\n");
			return -1;
	}
	printf("listen ok\r\n");
			sin_size = sizeof(struct sockaddr_in);
							//4.a-在监听到用户的请求后调用 accept 函数接受请求(R)
			nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
			if(-1 == nfp)
			{
					printf("accept fail !\r\n");
					return -1;
			}

			printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n", 
									ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
			while(1)	                //567.while(1)中执行 W-R操作
			{
					memset(buffer, 0, 100);
					sprintf(buffer, "hello,welcome to my server(%d) \r\n", num++);
					send(nfp, buffer, strlen(buffer), 0);//发生函数
					usleep(500000);
			}
			close(nfp);
	close(sfp);
	return 0;
}//1.s-调用 socket 函数创建一个套接字
	if(-1 == sfp)
	{
			printf("socket fail ! \r\n");
			return -1;
	}
	printf("socket ok !\r\n");
	bzero(&s_add,sizeof(struct sockaddr_in));
	s_add.sin_family=AF_INET;使用 IPv4 协议
	s_add.sin_addr.s_addr=htonl(INADDR_ANY);允许任何地址
	s_add.sin_port=htons(portnum);设置端口号
							//2.b-用bind 绑定函数,使用的是 IPv4 协议族
	if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
	{
			printf("bind fail !\r\n");
			return -1;
	}
	printf("bind ok !\r\n");
	if(-1 == listen(sfp,5))				//3.l-调用 listen 监听函数,监听用户的连接请求
	{
			printf("listen fail !\r\n");
			return -1;
	}
	printf("listen ok\r\n");
			sin_size = sizeof(struct sockaddr_in);
							//4.a-在监听到用户的请求后调用 accept 函数接受请求(R)
			nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
			if(-1 == nfp)
			{
					printf("accept fail !\r\n");
					return -1;
			}

			printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n", 
									ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
			while(1)	                //567.while(1)中执行 W-R操作
			{
					memset(buffer, 0, 100);
					sprintf(buffer, "hello,welcome to my server(%d) \r\n", num++);
					send(nfp, buffer, strlen(buffer), 0);//发生函数
					usleep(500000);
			}
			close(nfp);
	close(sfp);
	return 0;
}

                客户端:S C W RC

#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(int argc, char **argv)
{
	int cfd;
	int recbyte;
	int sin_size;
	char buffer[1024] = {0};

	struct sockaddr_in s_add, c_add;
	unsigned short portnum = 0x8888;
	printf("Hello,welcome to client!\r\n");
	if(argc != 2)
	{
			printf("usage: echo ip\n");
			return -1;
	}
	cfd = socket(AF_INET, SOCK_STREAM, 0);	//1.s-调用 socket 函数创建一个套接字
	if(-1 == cfd)
	{
			printf("socket fail ! \r\n");
			return -1;
	}
	printf("socket ok !\r\n");

	bzero(&s_add,sizeof(struct sockaddr_in));
	s_add.sin_family=AF_INET;					//ipv4
	s_add.sin_addr.s_addr= inet_addr(argv[1]);	//地址输入参数-要链接的IP地址
	s_add.sin_port=htons(portnum);				//端口号,1对1
	printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);
						//2.C-客户端直接链接地址(W)
	if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
	{
			printf("connect fail !\r\n");
			return -1;
	}

	printf("connect ok !\r\n");

	while(1)
	{					//3.R-读出来自服务器的数据
			if(-1 == (recbyte = read(cfd, buffer, 1024)))
			{
					printf("read data fail !\r\n");
					return -1;
			}

			printf("read ok\r\nREC:\r\n");
			buffer[recbyte]='\0';
			printf("%s\r\n",buffer);
	}

	close(cfd);

	return 0;

}//1.s-调用 socket 函数创建一个套接字
	if(-1 == cfd)
	{
			printf("socket fail ! \r\n");
			return -1;
	}
	printf("socket ok !\r\n");

	bzero(&s_add,sizeof(struct sockaddr_in));
	s_add.sin_family=AF_INET;					//ipv4
	s_add.sin_addr.s_addr= inet_addr(argv[1]);	//地址输入参数-要链接的IP地址
	s_add.sin_port=htons(portnum);				//端口号,1对1
	printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);
						//2.C-客户端直接链接地址(W)
	if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
	{
			printf("connect fail !\r\n");
			return -1;
	}

	printf("connect ok !\r\n");

	while(1)
	{					//3.R-读出来自服务器的数据
			if(-1 == (recbyte = read(cfd, buffer, 1024)))
			{
					printf("read data fail !\r\n");
					return -1;
			}

			printf("read ok\r\nREC:\r\n");
			buffer[recbyte]='\0';
			printf("%s\r\n",buffer);
	}

	close(cfd);

	return 0;

}

(3)概念.什么是 TCP? 什么是 IP?
TCP/IP 是一类协议系统,它是用于网络通信的一套协议集合.TCP/IP 中文名为传输控制协议/因特网互联协议,又名网络通讯协议,是 Internet 最基本的协议、Internet 国际互联网络的基础,由网络层的 IP 协议和传输层的 TCP 协议组成。TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准;
TCP/IP 协议不是 TCP 和 IP 这两个协议的合称,而是指因特网整个 TCP/IP 协议族。从协议分层模型方面来讲,TCP/IP 由四个层次组成:网络接口层、网络层、传输层、应用层。OSI 是传统的开放式系统互连参考模型,该模型将 TCP/IP 分为七层: 物理层、数据链路层(网络接口层)、网络层(网络层)、传输层(传输层)、会话层、表示层和应用层(应用层);

1) 网络接口层:
主要是指物理层次的一些接口,比如电缆等.

2) 网络层:
提供独立于硬件的逻辑寻址,实现物理地址与逻辑地址的转换

在 TCP / IP 协议族中,网络层协议包括 IP 协议(网际协议),ICMP 协议( Internet 互联网控制报文协议),以及 IGMP 协议( Internet 组管理协议).

3) 传输层:
为网络提供了流量控制,错误控制和确认服务.

在TCP / IP 协议族中有两个互不相同的传输协议: TCP(传输控制协议)和 UDP(用户数据报协议).

4) 应用层:
为网络排错,文件传输,远程控制和 Internet 操作提供具体的应用程序

(4)数据包==>套字节的形象定义
在 TCP / IP 协议中数据先由上往下将数据装包,然后由下往上拆包

装包的时候,每一层都会增加一些信息用于传输,这部分信息就叫报头,当上层的数据到达本层的时候,会将数据加上本层的报头打包在一起,继续往下传递

拆包的时候,每一层将本层需要的报头读取后,就将剩下的数据往上传.

这个过程有点像俄罗斯套娃,所以有时候人们也会用俄罗斯套娃来形容这个过程.

2.upd-网络通讯实例===============================================

 

    基于UDP的socket编程:

 

    UDP 是用户数据报协议,它是一种无连接的传输层协议,提供面向事物的简单不可靠信息传送服务,所以在一些网络质量不满意的环境下,UDP 协议数据包丢失会比较严重,会造成数据的丢失。

    UDP 的特点是他不属于连接型协议,所以资源消耗小处理速度快的优点,所以通常音频,视频和普通数据在传送时使用 UDP 较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

    

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <pthread.h>
//网口1的UDP服务器、客户端代码
void init_udp1()
{
    pthread_attr_t attr1={0};
    pthread_attr_init(&attr1);
    pthread_attr_setdetachstate(&attr1,PTHREAD_CREATE_DETACHED);
    if(pthread_create(&udp1_thread_id,NULL,udp1_server_process,0) !=0 )
    {
        printf("UserTimer thread (create) error\n");
        //while(1);
    }
    printf("\n=======udp1 server thread ok======\n");
    pthread_attr_t attr1_c={0};
    pthread_attr_init(&attr1_c);
    pthread_attr_setdetachstate(&attr1_c,PTHREAD_CREATE_DETACHED);
    if(pthread_create(&udp1_c_thread_id,NULL,udp1_client_process,0) !=0 )
    {
        printf("UserTimer thread (create) error\n");
        //while(1);
    }
    printf("\n=======udp1 client thread ok======\n");
}
//网口2的UDP服务器、客户端代码
void init_udp2()
{
    pthread_attr_t attr2={0};
    pthread_attr_init(&attr2);
    pthread_attr_setdetachstate(&attr2,PTHREAD_CREATE_DETACHED);
    if(pthread_create(&udp2_thread_id,NULL,udp2_server_process,0) !=0 )
    {
        printf("UserTimer thread (create) error\n");
    }
    

    pthread_attr_t attr2_c={0};
    pthread_attr_init(&attr2_c);
    pthread_attr_setdetachstate(&attr2_c,PTHREAD_CREATE_DETACHED);
    if(pthread_create(&udp2_c_thread_id,NULL,udp2_client_process,0) !=0 )
    {
        printf("UserTimer thread (create) error\n");
    }
    
}
int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        printf("Usage:./select_server [ip] [port]\n");
        //return 1;
    }

    init_udp1();
    init_udp2();
    while(1)
    {
        ;
    }
	printf("\n=======exit======\n");
       return 0;
}

(1)网口2 服务器 代码

//该服务器仍是属于回显服务器,接收到什么就回复什么
void *udp2_server_process(void *arg)
{
	struct sockaddr_in server;
    struct sockaddr_in client;
    char buf[BUFFER_SIZE];
    int32_t len = sizeof(client);
    int32_t ret = 0;
    int32_t file_len = 0;
    printf("\n=======udp2 server thread ok======\n");
    int32_t sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("create socket failed!");
        exit(1);
    }
    bzero(&server, sizeof(struct sockaddr_in));
    server.sin_family = AF_INET;
    server.sin_port = htons(udp2_port);     /*本机IP-端口 主机字节序转化成网络字节序 */
    server.sin_addr.s_addr = inet_addr(udp2_ip); /*本机IP 字符串转换in_addr的函数 */
    /* 绑定服务器 */
    if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)    
    {
        perror("bind failed!");
        exit(1);
    }
    while(1)
    {
        ret = recvfrom(sockfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&client, &len);
        if (ret < 0)
        {
            perror("recvfrom failed!");
            exit(1);
        }
        buf[ret] = '\0';
        printf("client message: %s\n", buf);
        printf("client's ip is %s, port is %d.\n", inet_ntoa(client.sin_addr), htons(client.sin_port));
    
        /**< 向客户端发送信息 */
        if (sendto(sockfd, "Welcome to server", BUFFER_SIZE, 0, (struct sockaddr *)&client, len) < 0)
        {
            perror("send file len to client error");
        }
    }
}

 

(2)网口2的客户端代码

 

void *udp2_client_process(void *arg)
{
   struct sockaddr_in server;
    struct sockaddr_in peer;
    char buf[BUFFER_SIZE];
    int32_t len = sizeof(peer);
    char sendbuf[BUFFER_SIZE] = "welcome to client";
    int32_t num = 0;
    printf("\n=======udp2 client thread ok======\n");
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("create udp2 socket failed!");
        exit(1);
    }
    bzero(&server, sizeof(struct sockaddr_in));
    server.sin_family = AF_INET;
    server.sin_port = htons(udp2_r_port);                //远端服务器的端口
    server.sin_addr.s_addr = inet_addr(udp2_r_ip);//远端服务器的地址
    while(1)
    {
        if (sendto(sockfd, sendbuf, BUFFER_SIZE, 0, (struct sockaddr *)&server, sizeof(server)) < 0)
        {
            perror("sendto socket failed!");
            exit(1);
        }

        if ((num = recvfrom(sockfd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&peer, &len)) < 0)
        {
            perror("recvfrom socket failed!");
            exit(1);
        }
        
        buf[num] = '\0';
        printf("Server Message: %s\n", buf);
    }
    

    close(sockfd);

    return 0;
}

Makefile

.SUFFIXES : .x .o .c .s

CC := arm-linux-gcc
STRIP := arm-linux-strip

TARGET = udp_app
SRCS := main.c udp.c
LIBS = -lpthread -lc -lgcc

all:
	$(CC) -static $(SRCS) -o $(TARGET) $(LIBS)
	$(STRIP) $(TARGET)
	cp -va $(TARGET) /nfsroot/
clean:
	rm -f *.o
	rm -f *.x
	rm -f *.flat
	rm -f *.map
	rm -f temp
	rm -f *.img
	rm -f $(TARGET)
	rm -f *.gdb

测试结果

        怎么看UDP比tcp更简单,不过TCP也一定不难!!!

 

注意:因为服务器需要在ubuntu上运行,故采用电脑的编译器编译:编译指令gcc -o server server.c

而客户端再开发板(arm内核)上运行,故采用arm的编译器:编译指令 arm-none-linux-gnueabi-gcc -o client client.c 编译 client.c

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaoxilang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值