您的位置:首页 > 理论基础 > 计算机网络

linux下udp 网络间通信程序+书上没有提过的问题自己想法

2012-05-07 19:28 597 查看
经过五一好几天的娱乐,这几天翻过头又把socket的UDP编程反刍一下,借了几本书又思考了一下,又把给我解答问题的朋友们的留言看了一遍,对UDP的socket编程终于有所小获,在网络中跨平台试了一下,好使,甚是开心,并打算在此总结一下以供初入门的朋友们少走弯路,并在此感谢积极给我解答问题的朋友们 @圣何塞白话人 @mallon @zino @xinzaibing @mallon @dd 是你们让我懂得了开源的精神,我也会以开源支持者的身份积极参与其中。下面是我的正文。

Linux下的socket编程主要就是几个结构体和几个函数就可以实现用协议通信的功能。主要的结构体如下:

struct in_addr
{
in_addr_t s_addr; //in_addr_t 其实就是unsigned long
};

struct sockaddr
{
unsigned short sa_family;
char sa_data[14];
};

struct sockaddr_in
{
short int sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};


第二个结构体和第三个结构体其实内容是一样的,只是后面讲的要用到的socket的函数bind, recvfrom, sendto, listen, accept, recv, send 等函数中用到的参数都是struct sockaddr,但是赋值的时候还是用sockaddr_in方便,于是就略显“多此一举”的给struct sockaddr_in定义的结构体的成员变量赋值,在调用函数的时候再用(struct sockaddr*)强制转换一下(这些在书上都是不说的)。

主要需要调用的函数上面提到了一些,这里就不说了,用终端中的man可以查到很详细的东西。下面贴一下程序:

服务器程序:

#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 80
#define sport 4567
#define sip "49.140.191.30"

void do_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
int n;
socklen_t len;
char mesg[MAXLINE];
int i=0;
while(1)
{
printf("OK %d\n",i);
i++;
len = clilen;
/* waiting for receive data */
n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
/* sent data back to client */
sendto(sockfd, mesg, n, 0, pcliaddr, len);

}
}

/*主函数*/
int main(void)
{
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = socket(AF_INET, SOCK_DGRAM, 0); /* create a socket */

/* init servaddr */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=inet_addr(sip);
//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(sport);

/* bind address and port to socket */
if(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
{
perror("bind error");
exit(1);
}

do_echo(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));

return 0;
}


客户端程序:

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

#define MAXLINE 80
#define SERV_PORT 4567

void do_cli(FILE *fp, int sockfd, struct sockaddr *pservaddr, socklen_t servlen)
{
int n;
char sendline[MAXLINE], recvline[MAXLINE + 1];

/* connect to server */
if(connect(sockfd, (struct sockaddr *)pservaddr, servlen) == -1)
{
perror("connect error");
exit(1);
}

while(fgets(sendline, MAXLINE, fp) != NULL)
{
/* read a line and send to server */
write(sockfd, sendline, strlen(sendline));
/* receive data from server */
n = read(sockfd, recvline, MAXLINE);
if(n == -1)
{
perror("read error");
exit(1);
}
recvline
= 0; /* terminate string */
fputs(recvline, stdout);
}
}

int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;

/* check args */
if(argc != 2)
{
printf("usage: udpclient <IPaddress>\n");
exit(1);
}

/* init servaddr */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
printf("[%s] is not a valid IPaddress\n", argv[1]);
exit(1);
}

sockfd = socket(AF_INET, SOCK_DGRAM, 0);

do_cli(stdin, sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

return 0;
}


在实际情况下,服务器用的是公网IP地址,而我在学校用的校园网正好也是给每个学生分配公网IP地址(封杀路由器),所以正好就把本机的公网IP作为服务器的IP地址,端口随意设了一个没有用的。客户端的程序和书上不同之处(优点)在于:当客户端连接到主机之后(connect调用之后),检测一下是否读到服务器回复的消息,如果没有回复就显示错误并退出,防止接收不到消息一直等待。

在本机中测试良好;在网络环境中,我在本机中开启服务器端程序,同学在win下用udp小工具作为客户端,同样可以达到互联的功能。同学用的是CMCC,是私有IP地址。(但这都不是问题的关键,因为NAT的作用把私有IP地址和端口号都转变为公有的IP地址和另一个端口号)

在这篇文章中要重点说书上没有的东西:(按我的个人理解,由于还在不断学习中,共同进步为原则,写出来在批判的过程中一起发现本质)

1,socket到底是什么?

int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);


sockfd是int类型,但其实它是具有指针功能的(虽然它不是指针),但是在创建socket的同时已经开辟了一段存储空间,用于存放和此socket绑定的相关信息。虽然在表面上不是指针,但是通过socket号可以确定是哪段存储空间这样便确定是哪段信息,和指针功能一样。

2,在服务器程序中,在37行

struct sockaddr_in servaddr, cliaddr;


对servaddr和cliaddr做了定义,但是后面只对servaddr赋值,cliaddr中只是定义时系统赋的初始值,用gdb显示如下:

(gdb) print cliaddr
$5 = {sin_family = 45860, sin_port = 42, sin_addr = {s_addr = 2797556},
sin_zero = "U<\026\000Y\207\004\b"}


但是,服务器得到了客户端发送的信息并且回复了客户端。它究竟是如何得到客户端的IP地址和端口号的呢?由于UDP是在传输层,很多下层一些的东西都不知道,要想弄明白这个我还需要继续学习(这也是我要做的),但是现在入门姑且这样理解,在这个日子mark一下,等发下一篇文章的时候就知道更本质的东西了,也知道我这些天到底有没有用功啦~~

姑且就这两个问题是书上没有解释的,现在的书都是一大抄,借来一堆书发现内容都一样,甚至程序都一样;还有的书程序很乱,编译都出错,书名就不提了,《21天。。。》。。。好了,期待自己的下一篇文章,也是下一个阶段(虽然我现在还很菜~~)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux socket udp 问题 idea