您的位置:首页 > 运维架构 > Linux

Linux下基于UDP协议的C/S通信编程笔记

2013-11-11 23:16 761 查看

UDP协议的全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。在OSI模型中,在第四层——传输层,处于IP协议的上一层。UDP有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。UDP用来支持那些需要在计算机之间传输数据的网络应用。包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用UDP协议。UDP协议从问世至今已经被使用了很多年,虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天UDP仍然不失为一项非常实用和可行的网络传输层协议。与所熟知的TCP(传输控制协议)协议一样,UDP协议直接位于IP(网际协议)协议的顶层。根据OSI(开放系统互连)参考模型,UDP和TCP都属于传输层协议。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。以上是百度百科对UDP的简介,于TCP相比UDP是不保证数据正确传输的协议,它仅仅是对IP协议进行了简单的封装(增加了端口),所以要保证数据传输的正确性需要程序员自己来处理。UDP相对于TCP的一个好处就是没有建立连接时的开销,这样两个端点之间没有就必要维持连接,而且UDP
socket编程要比TCP 编程要简单的多,又因为UDP不需要保证数据的正确性所以效率也比UDP高,一般都应用于聊天软件,视频通话等开发。

下页面我来是基于UDP编程的C/S模式实现两端通信,笔记。

用到的函数:

1.socket()//用于创建一个套接字

原型为 int socket(int domain, int type, int protocol);

所需头文件:      

 #include <sys/types.h>          /* See NOTES */

 #include <sys/socket.h>

参数说明:domain指明所使用的协议族,通常为PF_INET,表示互连网协议族(TCP/IP协议族)

type参数指定socket的类型: SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程式使用低层协议;

protocol意思为协议一般设为0.

返回:套接字文件描述符,错误返回-1.

2.htons()//将主机字节序转换为网络字节序

不同类型CPU的主机中,内存存储多字节整数序列有两种方法,称为主机字节序(HBO)

小端序:(little-endian)低序字节存储在低地址

大端序:(big-endian)高序字节存储在低地址

而网络中传输的数据必须按网络字节序,即大端字节序。

两个主机直接的进程通信,在大部分PC上,当应用进程将整数送人socket前,需要转化成网络字节序;当应用进程从socket取出整数后,要转化成小端字节序。

这个函数正是为避免不同主机之间进行数据交换时字节序不同而设计的。

原型:uint16_t htons(uint16_t hostshort);

需要头文件: #include <arpa/inet.h>

返回:经转换后的网络字节序。

3.bind()  // 绑定socket地址(也适用于TCP),对于服务器端是必须的,对于客户端是可选的。使用bind()函数。Bind的实质是将socket与本地地址绑定。本地地址主要指IP和端口。对于一个主机来说,bind的主要意义在于绑定端口。

原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

sockfd套接字文件描述符。

addr要绑定的IP地址。

addrlen绑定地址的长度。

所需头文件:      

 #include <sys/types.h>          /* See NOTES */       #include <sys/socket.h>

返回:成功 0,失败-1

4.inet_addr();

原型:  in_addr_t inet_addr(const char *cp);//将cp所指向的字符串转换成IP地址

返回:转换之后的二进制ip地址.

5.sendto();

原型:ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

说明:将buf所指向的缓冲区数据以len大小发送到dest_addr指向的目标IP地址.

参数:sockfd套接字文件描述符.

buf要发送的缓冲区 。

len要发送的长度.

flags标志位一般为0.

dest_addr目标地址.

addrlen目标地址长度.

返回:成功,返回发送的字节数.失败,返回-1.

6.recvfrom();

原型:ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

说明:从src_addr指向的地址接收长度为len的数据到buf指向的缓冲区,

参数:sockfd套接字文件描述符.

buf指向的接收缓冲区。

len要发送的长度.

flags标志位一般为0.

src_addr指向的源地址.

addrlen源地址的大小.

UDP编程步骤:


                                 

Server端流程


                         
                                                    

  client端流程

client.c

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

#define SIZE	128

int main(int argc, char *argv[])
{
int socketfd;
char buff[SIZE];
socklen_t len;
struct sockaddr_in server_addr, client_addr;

if ((socketfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)	//创建套接字
{
perror("fail to socket");
exit(-1);
}

memset(buff, 0, sizeof(buff));
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
len = sizeof(server_addr);
while (1)
{
printf("Input >>>>>>>>>>\n");
fgets(buff, SIZE, stdin);          //输入消息
buff[sizeof(buff) - 1] = '\0';
if ((sendto(socketfd, buff, SIZE, 0, (struct sockaddr *)&server_addr, sizeof(server_addr))) < 0) //发送
{
perror("fail to sendto");
exit(-1);
}
if (strncmp(buff, "quit\0", 4) == 0)             //判断退出
{
printf("client is quit!\n");
break;
}
memset(buff, 0, sizeof(buff));
if ((recvfrom(socketfd, buff, SIZE,	0, (struct sockaddr *)&server_addr, &len)) < 0)  //接收
{
perror("fail to recvfrom");
exit(-1);
}
printf("message is :%s\n", buff);
}
close(socketfd);
return 0;
}


server.c

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

#define SIZE	128

int main(int argc, char *argv[])
{
int socketfd;
char buff[SIZE];
socklen_t len;
struct sockaddr_in server_addr, client_addr;

if ((socketfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("fail to socket");
exit(-1);
}

memset(buff, 0, sizeof(buff));
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));

server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
if ((bind(socketfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) < 0)
{
perror("fail to bind");
exit(-1);
}
len = sizeof(client_addr);
while (1)
{
if ((recvfrom(socketfd, buff, SIZE, 0, (struct sockaddr *)&client_addr, &len)) < 0)
{
perror("fail to sendto");
exit(-1);
}
if ((strncmp(buff, "quit\0", 4)) == 0)
{
printf("server is quit!\n");
break;
}

printf("message is :%s\n", buff);
//memset(buff, 0, sizeof(buff));
if ((sendto(socketfd, buff, SIZE,	0, (struct sockaddr *)&client_addr, sizeof(client_addr))) < 0)
{
perror("fail to recvfrom");
exit(-1);
}
}
close(socketfd);
return 0;
}


Makefile:

CC=gcc
CFLAG =-Wall
all: server client
server: server.c
client: client.c
clean:
rm -rf server client
runserver:
./server 192.168.1.17 8000

runclient:
./client 192.168.1.17 8000


测试:



参考:http://blog.csdn.net/chpdirector84/article/details/4507685
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  通信 网络应用 编程