首页 > 学技术 > 技术网文 > C/C++ > 正文

[原创] Linux网络编程一步一步学-IPv6下网络编程步骤


来源 chinaunix.net kuqin整理

大家都知道,随着互联网上主机数量的增多,现有的32位IP地址已经不够用了,所以推出了下一代IP地址IPv6,写网络程序的要稍微改变一下现有的网络程序适应IPv6网络是相当容易的事。
对于我们来说就是IP地址变化了,所以程序里在用到IP地址的地方做相应的改变就可以了。

记住:主要是改变程序里设置IP地址和端口等部分的代码。

服务器端源代码如下:

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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: ipv6-server.c
*purpose: 演示最基本的IPv6网络编程步骤,开启服务接收客户端连接并和客户端通信,互相收发消息
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-29 13:06
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to:Google
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;

    /* struct sockaddr_in my_addr, their_addr; */ // IPv4
    struct sockaddr_in6 my_addr, their_addr; // IPv6

    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;

    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;

    /* if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { */ // IPv4
    if ((sockfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { // IPv6
        perror("socket");
        exit(1);
    } else
        printf("socket created\n");

    bzero(&my_addr, sizeof(my_addr));
    /* my_addr.sin_family = PF_INET; */ // IPv4
    my_addr.sin6_family = PF_INET6;    // IPv6
    /* my_addr.sin_port = htons(myport); */ // IPv4
    my_addr.sin6_port = htons(myport);   // IPv6
    if (argv[3])
        /* my_addr.sin_addr.s_addr = inet_addr(argv[3]); */ // IPv4
        inet_pton(AF_INET6, argv[3], &my_addr.sin6_addr);  // IPv6
    else
        /* my_addr.sin_addr.s_addr = INADDR_ANY; */ // IPv4
        my_addr.sin6_addr = in6addr_any;            // IPv6

    /* if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) */ // IPv4
    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_in6))  // IPv6
        == -1) {
        perror("bind");
        exit(1);
    } else
        printf("binded\n");

    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen\n");

    while (1) {
        len = sizeof(struct sockaddr);
        if ((new_fd =
             accept(sockfd, (struct sockaddr *) &their_addr,
                    &len)) == -1) {
            perror("accept");
            exit(errno);
        } else
            printf("server: got connection from %s, port %d, socket %d\n",
                   /* inet_ntoa(their_addr.sin_addr), */ // IPv4
                   inet_ntop(AF_INET6, &their_addr.sin6_addr, buf, sizeof(buf)), // IPv6
                   /* ntohs(their_addr.sin_port), new_fd); */ // IPv4
                   their_addr.sin6_port, new_fd); // IPv6

        /* 开始处理每个新连接上的数据收发 */
        bzero(buf, MAXBUF + 1);
        strcpy(buf,
               "这是在连接建立成功后向客户端发送的第一个消息\n只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息\n");
        /* 发消息给客户端 */
        len = send(new_fd, buf, strlen(buf), 0);
        if (len < 0) {
            printf
                ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
                 buf, errno, strerror(errno));
        } else
            printf("消息'%s'发送成功,共发送了%d个字节!\n",
                   buf, len);

        bzero(buf, MAXBUF + 1);
        /* 接收客户端的消息 */
        len = recv(new_fd, buf, MAXBUF, 0);
        if (len > 0)
            printf("接收消息成功:'%s',共%d个字节的数据\n",
                   buf, len);
        else
            printf
                ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
                 errno, strerror(errno));
        /* 处理每个新连接上的数据收发结束 */
    }

    close(sockfd);
    return 0;
}

每行程序后面的 “//IPv4” 表示这行代码是在IPv4网络里用的
而“//IPv6” 表示这行代码是在IPv6网络里用的,比较一下,会很容易看到差别的。

客户端源代码如下:

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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: ipv6-client.c
*purpose: 演示最基本的IPv6网络编程步骤,这是个客户端程序,与服务器互相收发消息
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-29 12:56
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to:Google
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd, len;
    /* struct sockaddr_in dest; */ // IPv4
    struct sockaddr_in6 dest;      // IPv6
    char buffer[MAXBUF + 1];

    if (argc != 3) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* 创建一个 socket 用于 tcp 通信 */
    /* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4
    if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {      // IPv6
        perror("Socket");
        exit(errno);
    }
    printf("socket created\n");

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    /* dest.sin_family = AF_INET; */  // IPv4
    dest.sin6_family = AF_INET6;     // IPv6
    /* dest.sin_port = htons(atoi(argv[2])); */ // IPv4
    dest.sin6_port = htons(atoi(argv[2]));     // IPv6
    /* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4
    if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) {                 // IPv6
        perror(argv[1]);
        exit(errno);
    }
    printf("address created\n");

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");

    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
    bzero(buffer, MAXBUF + 1);
    /* 接收服务器来的消息 */
    len = recv(sockfd, buffer, MAXBUF, 0);
    if (len > 0)
        printf("接收消息成功:'%s',共%d个字节的数据\n",
               buffer, len);
    else
        printf
            ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
             errno, strerror(errno));

    bzero(buffer, MAXBUF + 1);
    strcpy(buffer, "这是客户端发给服务器端的消息\n");
    /* 发消息给服务器 */
    len = send(sockfd, buffer, strlen(buffer), 0);
    if (len < 0)
        printf
            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
             buffer, errno, strerror(errno));
    else
        printf("消息'%s'发送成功,共发送了%d个字节!\n",
               buffer, len);

    /* 关闭连接 */
    close(sockfd);
    return 0;
}


编译程序用下列命令:
引用:
gcc -Wall ipv6-server.c -o ipv6server
gcc -Wall ipv6-client.c -o ipv6client



你自己的主机有IPv6地址吗?很多人会问,输入ifconfig命令看一下吧:
引用:
eth0      链路封装:以太网  硬件地址 00:14:2A:6D:5B:A5 
          inet 地址:192.168.0.167  广播:192.168.0.255  掩码:255.255.255.0
          inet6 地址: fe80::214:2aff:fe6d:5ba5/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1
          接收数据包:30507 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:26797 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:1000
          接收字节:31461154 (30.0 MiB)  发送字节:4472810 (4.2 MiB)
          中断:185 基本地址:0xe400

lo        链路封装:本地环回 
          inet 地址:127.0.0.1  掩码:255.0.0.0
          inet6 地址: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  跃点数:1
          接收数据包:13 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:13 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:0
          接收字节:1178 (1.1 KiB)  发送字节:1178 (1.1 KiB)



看到“inet6 地址:”这两行了吗?后面就是你的IPv6地址
关于IPv6地址类型,可以参考一下 [url=http://docs.hp.com/zh_cn/B3936-90083/aphs01.html]惠普主页上的技术文档资料“IPv6地址类型”

启动服务:
./ipv6server 7838 1
或者加上IP地址启动服务:
./ipv6server 7838 1 fe80::214:2aff:fe6d:5ba5/64

启动客户端测试一下:
./ipv6client ::1/128 7838

./ipv6client fe80::214:2aff:fe6d:5ba5/6 7838

[ 本帖最后由 zhoulifa 于 2007-1-29 13:34 编辑 ]



 universes 回复于:2007-01-29 15:07:16

赞一个先!
本人也是搞IPV6的,不过不是开源,在公司做IPV6产品的开发。编写了公司ND(邻居发现)部分的全部代码。
另外修改了TELNET代码以适应IPV6。
感觉IPV6现在还不很成熟,有兴趣的话可以一起交流、学习!


 arenxl 回复于:2007-01-29 15:18:38

以前关注过,不过还是等以后用的时候再继续研究吧。


 zhoulifa 回复于:2007-01-29 17:37:46

to universes:
高人!
你有没有NDP协议rfc文档? 还是说NDP目前没有标准,只是大家各自按自己的协议在做?
如果有,可否发一份给我? [email]zhoulifa@gmail.com[/email]
先谢谢了!


 fxyxsl 回复于:2007-01-29 17:49:59

这个和windows环境下有什么不同?

再请教个问题:windows XP环境下如何获取本机的IP地址(IPv6的地址)?我试了很多方法,2003下可以,XP却始终不行~


 converse 回复于:2007-01-29 18:13:13

LZ你好,我看了你发的几个帖子,感觉不错,建议你整理到一个帖子里面这样以后查找方便也便于将你的想法系列化.我给你加原创精华,谢谢.


 yingen2006 回复于:2007-01-29 23:00:37

mark


 大大狗 回复于:2007-01-29 23:45:06

强人


 zhoulifa 回复于:2007-02-03 21:00:05

引用:原帖由 converse 于 2007-1-29 18:13 发表
LZ你好,我看了你发的几个帖子,感觉不错,建议你整理到一个帖子里面这样以后查找方便也便于将你的想法系列化.我给你加原创精华,谢谢. 



感谢版主!我也想全部发到这里,但不知道怎样才能一次发完全部的,所以只挑了几个有代表性的放在这里。我原本是一系列“ Linux网络编程一步一步学”,共24篇,如下:

[url=http://zhoulifa.bokee.com/6062858.html] &#8226; Linux网络编程一步一步学-简单客户端编写

[url=http://zhoulifa.bokee.com/6062924.html]  &#8226; Linux网络编程一步一步学-绑定IP和端口

[url=http://zhoulifa.bokee.com/6062989.html] &#8226; Linux网络编程一步一步学-循环读取服务器上的数据

[url=http://zhoulifa.bokee.com/6063041.html]  &#8226; Linux网络编程一步一步学-设置非阻塞方式

[url=http://zhoulifa.bokee.com/6064410.html]  &#8226; Linux网络编程一步一步学-开启网络监听服务

[url=http://zhoulifa.bokee.com/6064418.html] &#8226; Linux网络编程一步一步学-接受客户端连接请求

[url=http://zhoulifa.bokee.com/6064468.html] &#8226; Linux网络编程一步一步学-向客户端发送消息

[url=http://zhoulifa.bokee.com/6064533.html]  &#8226; Linux网络编程一步一步学-客户端和服务器端互相收发消息

[url=http://zhoulifa.bokee.com/6065457.html]  &#8226; Linux网络编程一步一步学-UDP编程介绍

[url=http://zhoulifa.bokee.com/6065676.html]  &#8226; Linux网络编程一步一步学-UDP方式点对点通讯

[url=http://zhoulifa.bokee.com/6065720.html] &#8226; Linux网络编程一步一步学-UDP方式广播通讯

[url=http://zhoulifa.bokee.com/6066939.html] &#8226; Linux网络编程一步一步学-网络广播、组播与单播

[url=http://zhoulifa.bokee.com/6066993.html]  &#8226; Linux网络编程一步一步学-UDP组播

[url=http://zhoulifa.bokee.com/6067924.html]  &#8226; Linux网络编程一步一步学-同步聊天程序

[url=http://zhoulifa.bokee.com/6068027.html]  &#8226; Linux网络编程一步一步学-异步通讯聊天程序select

[url=http://zhoulifa.bokee.com/6069209.html]  &#8226; Linux网络编程一步一步学-编写一个HTTP协议的目录浏览和文件下载服务器

[url=http://zhoulifa.bokee.com/6071872.html] &#8226; Linux网络编程一步一步学-用C自己编写一个telnet服务器

[url=http://zhoulifa.bokee.com/6072092.html] &#8226; Linux网络编程一步一步学-网络编程函数说明-来自“永远的UNIX”

[url=http://zhoulifa.bokee.com/6073915.html]  &#8226; Linux下上网方法总结

[url=http://zhoulifa.bokee.com/6074014.html] &#8226; Linux网络编程一步一步学-利用OpenSSL提供的SSL操作函数进行加密通讯原始例子

[url=http://zhoulifa.bokee.com/6075800.html]  &#8226; Linux网络编程一步一步学-IPv6下网络编程步骤

[url=http://zhoulifa.bokee.com/6079174.html]  &#8226; Linux网络编程一步一步学-HTTPS客户端程序示例

[url=http://zhoulifa.bokee.com/6079257.html] &#8226; OpenSSL体系下使用密钥数字证书等

[url=http://zhoulifa.bokee.com/6081520.html] &#8226; Linux网络编程一步一步学-epoll同时处理海量连接的代码  编写一个服务器同时处理上万的网络连接,这对大量用户同时在线的编程相当重要。

[url=http://zhoulifa.bokee.com/6085904.html] &#8226; Linux网络编程一步一步学-加密通讯协议SSL研究 这对银行、证券等行业来说相当重要

[url=http://zhoulifa.bokee.com/6087775.html] &#8226;  Linux网络编程一步一步学-select详解

还请告诉我如何将此全部发到这里。

[ 本帖最后由 zhoulifa 于 2007-2-3 21:09 编辑 ]


 converse 回复于:2007-02-03 22:32:46

>>还请告诉我如何将此全部发到这里。
汗,你就把帖子一个一个的贴在这个帖子里面就好了,我敢打赌会N热门的~~


 zw2002 回复于:2007-02-03 22:51:53

是啊,支持


 linternt 回复于:2007-02-03 23:51:24

现在有代码都实现IPV4和IPV6通用了,支持精神!


 zhoulifa 回复于:2007-02-04 12:23:26

引用:原帖由 converse 于 2007-2-3 22:32 发表
>>还请告诉我如何将此全部发到这里。
汗,你就把帖子一个一个的贴在这个帖子里面就好了,我敢打赌会N热门的~~ 



那我就当一回搬运工了。不过我觉得这种方式比较那个,好象与咱们信息化时代不太相符哦


 converse 回复于:2007-02-04 12:29:11

引用:原帖由 zhoulifa 于 2007-2-4 12:23 发表


那我就当一回搬运工了。不过我觉得这种方式比较那个,好象与咱们信息化时代不太相符哦 



或者在某个帖子里面一次过把所有帖子的链接都放进来,我说的方案都是为了方便别人查看你的帖子罢了。


 zhoulifa 回复于:2007-02-04 12:30:37


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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: simple-socket.c
*purpose: 演示最基本的网络编程步骤,这是个客户端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-23 19:41:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_in dest;
    char buffer[MAXBUF];

    if (argc != 3) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* 创建一个 socket 用于 tcp 通信 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(argv[1]);
        exit(errno);
    }

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }

    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
    bzero(buffer, MAXBUF);
    recv(sockfd, buffer, sizeof(buffer), 0);
    printf("%s", buffer);

    /* 关闭连接 */
    close(sockfd);
    return 0;
}

编译此程序使用如下命令:
引用:
gcc -Wall simple-socket.c


运行此程序使用如下命令(假设你的主机上开启了ssh服务):
引用:
./a.out 127.0.0.1 22


[size=2]
其实socket客户端编程相当简单:
第1步:建立一个socket句柄,用socket()函数;
第2步:设定你要连接的服务器的IP地址和端口等信息;
第3步:与服务器建立连接,用connect函数;
第4步:收发消息,用recv(sockfd,...)/send(sockfd,...)或者read(sockfd,...)/write(sockfd,...)都可以;
第5步:通讯结束后关闭连接,用close()函数即可,当然也有人用shutdown()函数。
[/size]

[ 本帖最后由 zhoulifa 于 2007-2-4 12:48 编辑 ]


 zhoulifa 回复于:2007-02-04 12:50:11

为了服务器的安全,有些服务器要求客户端只能以指定的IP地址和特定的端口才能连接上来。这就要求我们客户端连接之前绑定IP地址和端口信息。

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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: simple-bind.c
*purpose: 演示最基本的网络编程步骤,这是个客户端程序以固定 IP 和端口连接服务器
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-23 19:51:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_in dest, mine;
    char buffer[MAXBUF];

    if (argc != 5) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s 对方IP地址 对方端口 本机IP地址 本机端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来以本机固定的端口从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* 创建一个 socket 用于 tcp 通信 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(argv[1]);
        exit(errno);
    }

    /* 初始化自己的地址和端口信息 */
    bzero(&mine, sizeof(mine));
    mine.sin_family = AF_INET;
    mine.sin_port = htons(atoi(argv[4]));
    if (inet_aton(argv[3], (struct in_addr *) &mine.sin_addr.s_addr) == 0) {
        perror(argv[3]);
        exit(errno);
    }

    /* 把自己的 IP 地址信息和端口与 socket 绑定 */
    if (bind(sockfd, (struct sockaddr *) &mine, sizeof(struct sockaddr)) ==
        -1) {
        perror(argv[3]);
        exit(errno);
    }

    /* 以自己特定的端口和IP连接服务器的特定端口 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }

    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
    bzero(buffer, MAXBUF);
    recv(sockfd, buffer, sizeof(buffer), 0);
    printf("%s", buffer);
    sleep(10);
    /* 关闭连接 */
    close(sockfd);
    return 0;
}

编译程序用此命令:
引用:
gcc -Wall simple-bind.c


运行程序用此命令:
引用:
./a.out 127.0.0.1 22 127.0.0.1 3000


同时可以用下列netstat命令查看网络连接状态:
引用:
netstat -an|grep 3000


查看到如下信息:
引用:
tcp 0 0 127.0.0.1:3000 127.0.0.1:22 ESTABLISHED



与前面一份源代码相比,这里多了设置自己的IP地址和端口信息,所以编程过程变成了:
第1步:建立一个socket句柄,用socket()函数;
第2步:设定你要连接的服务器的IP地址和端口等信息;
[color=red]第x步:设定你自己的IP地址和端口等信息与socket绑定,用bind()函数; #与前面相比就是多了这一个步骤[/color]
第3步:与服务器建立连接,用connect函数;
第4步:收发消息,用recv(sockfd,...)/send(sockfd,...)或者read(sockfd,...)/write(sockfd,...)都可以;
第5步:通讯结束后关闭连接,用close()函数即可,当然也有人用shutdown()函数。

[ 本帖最后由 zhoulifa 于 2007-2-4 12:55 编辑 ]


 zhoulifa 回复于:2007-02-04 12:57:11


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

#define MAXBUF 10
/************关于本文档********************************************
*filename: simple-readall.c
*purpose: 演示最基本的网络编程,循环读取服务器上发过来的内容,直到读完为止
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-23 20:16:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd, ret;
    struct sockaddr_in dest, mine;
    char buffer[MAXBUF + 1];

    if (argc != 5) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s 对方IP地址 对方端口 本机IP地址 本机端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来以本机固定的端口从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* 创建一个 socket 用于 tcp 通信 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(argv[1]);
        exit(errno);
    }

    /* 初始化自己的地址和端口信息 */
    bzero(&mine, sizeof(mine));
    mine.sin_family = AF_INET;
    mine.sin_port = htons(atoi(argv[4]));
    if (inet_aton(argv[3], (struct in_addr *) &mine.sin_addr.s_addr) == 0) {
        perror(argv[3]);
        exit(errno);
    }

    /* 把自己的 IP 地址信息和端口与 socket 绑定 */
    if (bind(sockfd, (struct sockaddr *) &mine, sizeof(struct sockaddr)) ==
        -1) {
        perror(argv[3]);
        exit(errno);
    }

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }

    /* 接收对方发过来的消息,每次最多接收 MAXBUF 个字节,直到把对方发过来的所有消息接收完毕为止 */
    do {
        bzero(buffer, MAXBUF + 1);
        ret = recv(sockfd, buffer, MAXBUF, 0);
        printf("读到%d个字节,它们是:'%s'\n", ret, buffer);
    } while (ret == MAXBUF);

    /* 关闭连接 */
    close(sockfd);
    return 0;
}

编译程序使用如下命令:
引用:
gcc -Wall simple-readall.c


运行程序用如下命令:
引用:
./a.out 127.0.0.1 22 127.0.0.1 3000


此程序运行结果如下:
引用:
读到10个字节,它们是:'SSH-2.0-Op'
读到10个字节,它们是:'enSSH_4.3p'
读到10个字节,它们是:'2 Debian-5'
读到8个字节,它们是:'ubuntu1
'


注意:如果你运行时程序长久没有退出,请把程序的第一行:
引用:#define MAXBUF 10

稍微改一下,比如改成下面的:
引用:#define MAXBUF 9

再编译运行试试。
为什么?请继续看下一篇文章“设置非阻塞方式”。


 zhoulifa 回复于:2007-02-04 13:00:49


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

#define MAXBUF 10
/************关于本文档********************************************
*filename: simple-nonblock.c
*purpose: 演示最基本的网络编程,循环读取服务器上发过来的内容,直到读完为止
*wrote by: zhoulifa([email]zhoulifa@163.com[/email]) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-23 20:46:54
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd, ret, rcvtm = 0;
    struct sockaddr_in dest, mine;
    char buffer[MAXBUF + 1];

    if (argc != 5) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s 对方IP地址 对方端口 本机IP地址 本机端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来以本机固定的端口从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }

    /* 创建一个 socket 用于 tcp 通信 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(argv[1]);
        exit(errno);
    }

    /* 初始化自己的地址和端口信息 */
    bzero(&mine, sizeof(mine));
    mine.sin_family = AF_INET;
    mine.sin_port = htons(atoi(argv[4]));
    if (inet_aton(argv[3], (struct in_addr *) &mine.sin_addr.s_addr) == 0) {
        perror(argv[3]);
        exit(errno);
    }

    /* 把自己的 IP 地址信息和端口与 socket 绑定 */
    if (bind(sockfd, (struct sockaddr *) &mine, sizeof(struct sockaddr)) ==
        -1) {
        perror(argv[3]);
        exit(errno);
    }

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }

    /* 设置 socket 属性为非阻塞方式 */
    if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
        perror("fcntl");
        exit(errno);
    }

    /* 接收对方发过来的消息,每次最多接收 MAXBUF 个字节,直到把对方发过来的所有消息接收完毕为止 */
    do {
      _retry:
        bzero(buffer, MAXBUF + 1);
        ret = recv(sockfd, buffer, MAXBUF, 0);
        if (ret > 0)
            printf("读到%d个字节,它们是:'%s'\n", ret, buffer);

        if (ret < 0) {
            if (errno == EAGAIN) {
                if (rcvtm)
                    break;
                else {
                    printf("数据还未到达!\n");
                    usleep(100000);
                    goto _retry;
                };
            };
            printf("接收出错了!\n");
            perror("recv");
        }
        rcvtm++;
    } while (ret == MAXBUF);

    /* 关闭连接 */
    close(sockfd);
    return 0;
}

编译程序用下列命令:
引用:
gcc -Wall simple-nonblock.c


运行程序用下列命令:
引用:
./a.out 127.0.0.1 21 127.0.0.1 3000


程序运行输出结果如下:
引用:
数据还未到达!
读到10个字节,它们是:'220 (vsFTP'
读到10个字节,它们是:'d 2.0.4)
'



问题:

1、非阻塞是什么?
网络通信有阻塞和非阻塞之分,例如对于接收数据的函数recv:在阻塞方式下,没有数据到达时,即接收不到数据时,程序会停在recv函数这里等待数据的到来;而在非阻塞方式下就不会等,如果没有数据可接收就立即返回-1表示接收失败。
2、什么是errno?
errno是Linux系统下保存当前状态的一个公共变量,当前程序运行时进行系统调用如果出错,则会设置errno为某个值以告诉用户出了什么错误。可以用printf("%d %s\n", errno, strerror(errno));得到具体信息。
3、什么是EAGAIN?
man recv
当recv系统调用返回这个值时表示recv读数据时,对方没有发送数据过来。


 zhoulifa 回复于:2007-02-04 13:11:12


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

/************关于本文档********************************************
*filename: simple-listen.c
*purpose: 演示最基本的网络编程步骤,开启服务端的监听,等待客户端来连接
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 12:31:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd;
    struct sockaddr_in my_addr;
    unsigned int myport, lisnum;

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;

    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    } else
        printf("socket created\n");

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    if (argv[3])
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    else
        my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
        == -1) {
        perror("bind");
        exit(1);
    } else
        printf("binded\n");

    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen\n");

    sleep(100);
    close(sockfd);
    return 0;
}

编译程序用下列命令:
引用:
gcc -Wall simple-listen.c


运行程序用如下命令:
引用:
./a.out 7838 2


这将在你自己主机的所有IP地址是等待客户端连接。比如你的网卡lo的IP地址127.0.0.1,又比如你的网卡eth0的IP地址192.168.0.100
如果要指定只在某个地址是开启监听服务,可以用下面的命令:
引用:
./a.out 7838 2 127.0.0.1


这样,客户端只能通过127.0.0.1的7838端口连接你的程序。
你可以开启一个终端输入telnet 127.0.0.1 7838来测试是否能连接成功。
同时可以用netstat -an|grep 7838命令来查看网络是否连接正常
[color=red]
其实socket服务器端编程也是相当简单的:
第1步:建立一个socket句柄,用socket()函数;
第2步:设定自己服务器的IP地址和端口等信息,是用于让对方连接的;
第3步:把自己的IP地址和端口等信息与socket绑定,用bind()函数;
第4步:开启监听服务,用listen()函数;    #至此,客户端已经能连接你这个IP地址和端口了
注:但由于我们还没有接受客户端的连接,没有建立用于消息收发的socket句柄,所以客户端还没法一客户端进行消息收发。
[/color]


 zhoulifa 回复于:2007-02-04 13:19:51


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

/************关于本文档********************************************
*filename: simple-accept.c
*purpose: 演示最基本的网络编程步骤,开启服务端的监听,并接收每个客户端的连接请求
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 12:41
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;

    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    } else
        printf("socket created\n");

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    if (argv[3])
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    else
        my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
        == -1) {
        perror("bind");
        exit(1);
    } else
        printf("binded\n");

    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen\n");

    while (1) {
        len = sizeof(struct sockaddr);
        if ((new_fd =
             accept(sockfd, (struct sockaddr *) &their_addr,
                    &len)) == -1) {
            perror("accept");
            exit(errno);
        } else
            printf("server: got connection from %s, port %d, socket %d\n",
                   inet_ntoa(their_addr.sin_addr),
                   ntohs(their_addr.sin_port), new_fd);
    }

    close(sockfd);
    return 0;
}

编译程序用下列命令:
引用:
gcc -Wall simple-accept.c


运行程序用如下命令:
引用:
./a.out 7838 1 127.0.0.1


另外开多几个客户端程序连接上来,
程序输出结果如下:
引用:
server: got connection from 127.0.0.1, port 15949, socket 4
server: got connection from 127.0.0.1, port 15950, socket 5
server: got connection from 127.0.0.1, port 15951, socket 6
server: got connection from 127.0.0.1, port 15952, socket 7



[color=red]与上一篇相比,这里只是增加了accept函数,这个函数会创建一个新的socket句柄,这个句柄才是我们真正可以用来通讯的句柄,即客户端和服务端的消息收发是通过这个accept产生的socket进行的[/color]
accpet是一个典型的阻塞函数,即程序会停止在accept函数处直到有客户端连接上来才会返回。

[ 本帖最后由 zhoulifa 于 2007-2-4 13:37 编辑 ]


 zhoulifa 回复于:2007-02-04 13:32:01


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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: simple-send.c
*purpose: 演示最基本的网络编程步骤,开启服务接收客户端连接并向客户端发送消息
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 13:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];

    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;

    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket");
        exit(1);
    } else
        printf("socket created\n");

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    if (argv[3])
        my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    else
        my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
        == -1) {
        perror("bind");
        exit(1);
    } else
        printf("binded\n");

    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen\n");

    while (1) {
        len = sizeof(struct sockaddr);
        if ((new_fd =
             accept(sockfd, (struct sockaddr *) &their_addr,
                    &len)) == -1) {
            perror("accept");
            exit(errno);
        } else
            printf("server: got connection from %s, port %d, socket %d\n",
                   inet_ntoa(their_addr.sin_addr),
                   ntohs(their_addr.sin_port), new_fd);

        /* 开始处理每个新连接上的数据收发 */
        bzero(buf, MAXBUF + 1);
        strcpy(buf,
               "这是在连接建立成功后向客户端发送的第一个消息\n只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息\n");
        len = send(new_fd, buf, strlen(buf), 0);
        if (len < 0) {
            printf
                ("消息'%s'发送失败!错误代码是%d,错误消息是'%s'\n",
                 buf, errno, strerror(errno));
        } else
            printf("消息'%s'发送成功,共发送了%d个字节!\n",
                   buf, len);
        /* 处理每个新连接上的数据收发结束 */
    }

    close(sockfd);
    return 0;
}

编译程序用下列命令:
引用:
gcc -Wall simple-send.c


运行程序用如下命令:
引用:
./a.out 127.0.0.1 7838 1


程序输出结果如下:
引用:
socket created
binded
begin listen
server: got connection from 127.0.0.1, port 13097, socket 4
消息'这是在连接建立成功后向客户端发送的第一个消息
只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息
'发送成功,共发送了227个字节!
server: got connection from 127.0.0.1, port 13099, socket 5
消息'这是在连接建立成功后向客户端发送的第一个消息
只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息
'发送成功,共发送了227个字节!


客户端运行如下命令:
引用:
telnet 127.0.0.1 7838


客户端得到输出如下:
引用:
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
这是在连接建立成功后向客户端发送的第一个消息
只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息
Connection closed by foreign host.


[color=red]注意:只对accpet接收连接请求产生的新的socket句柄进行读写操作![/color]


 zhoulifa 回复于:2007-02-04 13:45:34

服务器端源代码如下:

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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: simple-accept.c
*purpose: 演示最基本的网络编程步骤,开启服务接收客户端连接并和客户端通信,互相收发消息
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 13:26
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];

    if (argv[1])
      myport = atoi(argv[1]);
    else
      myport = 7838;

    if (argv[2])
      lisnum = atoi(argv[2]);
    else
      lisnum = 2;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
      perror("socket");
      exit(1);
    } else
      printf("socket created\n");

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    if (argv[3])
      my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    else
      my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
      == -1) {
      perror("bind");
      exit(1);
    } else
      printf("binded\n");

    if (listen(sockfd, lisnum) == -1) {
      perror("listen");
      exit(1);
    } else
      printf("begin listen\n");

    while (1) {
      len = sizeof(struct sockaddr);
      if ((new_fd =
           accept(sockfd, (struct sockaddr *) &their_addr,
                  &len)) == -1) {
          perror("accept");
          exit(errno);
      } else
          printf("server: got connection from %s, port %d, socket %d\n",
                 inet_ntoa(their_addr.sin_addr),
                 ntohs(their_addr.sin_port), new_fd);

      /* 开始处理每个新连接上的数据收发 */
      bzero(buf, MAXBUF + 1);
      strcpy(buf,
             "这是在连接建立成功后向客户端发送的第一个消息\n只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息\n");
      /* 发消息给客户端 */
      len = send(new_fd, buf, strlen(buf), 0);
      if (len < 0) {
          printf
              ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
               buf, errno, strerror(errno));
      } else
          printf("消息'%s'发送成功,共发送了%d个字节!\n",
                 buf, len);

      bzero(buf, MAXBUF + 1);
      /* 接收客户端的消息 */
      len = recv(new_fd, buf, MAXBUF, 0);
      if (len > 0)
          printf("接收消息成功:'%s',共%d个字节的数据\n",
                 buf, len);
      else
          printf
              ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
               errno, strerror(errno));
      /* 处理每个新连接上的数据收发结束 */
    }

    close(sockfd);
    return 0;
}

客户端源代码如下:

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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: simple-socket.c
*purpose: 演示最基本的网络编程步骤,这是个客户端程序,与服务器互相收发消息
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 13:26
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    int sockfd, len;
    struct sockaddr_in dest;
    char buffer[MAXBUF + 1];

    if (argc != 3) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* 创建一个 socket 用于 tcp 通信 */
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("Socket");
        exit(errno);
    }
    printf("socket created\n");

    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(atoi(argv[2]));
    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) {
        perror(argv[1]);
        exit(errno);
    }
    printf("address created\n");

    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");

    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
    bzero(buffer, MAXBUF + 1);
    /* 接收服务器来的消息 */
    len = recv(sockfd, buffer, MAXBUF, 0);
    if (len > 0)
        printf("接收消息成功:'%s',共%d个字节的数据\n",
               buffer, len);
    else
        printf
            ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
             errno, strerror(errno));

    bzero(buffer, MAXBUF + 1);
    strcpy(buffer, "这是客户端发给服务器端的消息\n");
    /* 发消息给服务器 */
    len = send(sockfd, buffer, strlen(buffer), 0);
    if (len < 0)
        printf
            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
             buffer, errno, strerror(errno));
    else
        printf("消息'%s'发送成功,共发送了%d个字节!\n",
               buffer, len);

    /* 关闭连接 */
    close(sockfd);
    return 0;
}

编译两个程序用下列命令:
引用:
gcc -Wall simple-server.c -o server
gcc -Wall simple-client.c -o client


启动服务端程序用如下命令:
引用:
./server 7838 1


启动客户端程序用如下命令:
引用:
./client 127.0.0.1 7838


服务端程序运行屏幕输出如下:
引用:
socket created
binded
begin listen
server: got connection from 127.0.0.1, port 32537, socket 4
消息'这是在连接建立成功后向客户端发送的第一个消息
只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息
'发送成功,共发送了227个字节!
接收消息成功:'这是客户端发给服务器端的消息
',共43个字节的数据


客户端程序运行屏幕输出如下:
引用:
socket created
address created
server connected
接收消息成功:'这是在连接建立成功后向客户端发送的第一个消息
只能向new_fd这个用accept函数新建立的socket发消息,不能向sockfd这个监听socket发送消息,监听socket不能用来接收或发送消息
',共227个字节的数据
消息'这是客户端发给服务器端的消息
'发送成功,共发送了43个字节!


注意:这里的recv函数也是一个典型的阻塞函数,如果对方没有发送数据,程序都会停在recv函数处等待直到超时出错时退出。
所以这个例子是个典型的同步通讯程序,即一端发送数据时另一端一定要接收数据,反之亦然。
所以一端是send、recv、recv、send时,另一端一定是recv、send、send、recv

[ 本帖最后由 zhoulifa 于 2007-2-4 13:47 编辑 ]


 zhoulifa 回复于:2007-02-04 13:53:46

通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为 SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。
通过查看socket的man手册可以看到socket函数的第一个参数的值可以为下面这些值:
引用:
Name                              Purpose
PF_UNIX, PF_LOCAL     Local communication
PF_INET                         IPv4 Internet protocols
PF_INET6                       IPv6 Internet protocols
PF_IPX                           IPX - Novell protocols
PF_NETLINK                  Kernel user interface device
PF_X25                           ITU-T X.25 / ISO-8208 protocol
PF_AX25                         Amateur radio AX.25 protocol
PF_ATMPVC                    Access to raw ATM PVCs
PF_APPLETALK             Appletalk
PF_PACKET                    Low level packet interface


第二个参数支持下列几种值:
引用:
SOCK_STREAM
Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be sup‐
ported.

SOCK_DGRAM
Supports datagrams (connectionless, unreliable messages of a fixed maximum length).

SOCK_SEQPACKET
Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a con‐
sumer is required to read an entire packet with each read system call.

SOCK_RAW
Provides raw network protocol access.

SOCK_RDM
Provides a reliable datagram layer that does not guarantee ordering.

SOCK_PACKET
Obsolete and should not be used in new programs; see packet(7).


从这里可以看出,SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。
而SOCK_DGRAM 这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个 socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。
基于上述不同,UDP和TCP编程步骤也有些不同,如下:

/*********************************************************************
*filename: UDP编程介绍
*purpose: 通过比较UDP和TCP编程对二者编程步骤进行总结说明
*tidied by: zhoulifa([email]zhoulifa@163.com[/email]) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 20:12:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
*********************************************************************/

TCP编程的服务器端一般步骤是:
引用:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt(); * 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、开启监听,用函数listen();
5、接收客户端上来的连接,用函数accept();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;
8、关闭监听;


TCP编程的客户端一般步骤是:
引用:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接;


与之对应的UDP编程步骤要简单许多,分别如下:

UDP编程的服务器端一般步骤是:
引用:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();
4、循环接收数据,用函数recvfrom();
5、关闭网络连接;



UDP编程的客户端一般步骤是:
引用:
1、创建一个socket,用函数socket();
2、设置socket属性,用函数setsockopt();* 可选
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
4、设置对方的IP地址和端口等属性;
5、发送数据,用函数sendto();
6、关闭网络连接;




 zhoulifa 回复于:2007-02-04 13:59:46

UDP通讯服务器端源代码如下:

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

/*********************************************************************
*filename: simple-udpserver.c
*purpose: 基本编程步骤说明,演示了UDP编程的服务器端编程步骤
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 20:22:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    struct sockaddr_in s_addr;
    struct sockaddr_in c_addr;
    int sock;
    socklen_t addr_len;
    int len;
    char buff[128];

    /* 创建 socket , 关键在于这个 SOCK_DGRAM */
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
      perror("socket");
      exit(errno);
    } else
      printf("create socket.\n\r");

    memset(&s_addr, 0, sizeof(struct sockaddr_in));
    /* 设置地址和端口信息 */
    s_addr.sin_family = AF_INET;
    if (argv[2])
      s_addr.sin_port = htons(atoi(argv[2]));
    else
      s_addr.sin_port = htons(7838);
    if (argv[1])
      s_addr.sin_addr.s_addr = inet_addr(argv[1]);
    else
      s_addr.sin_addr.s_addr = INADDR_ANY;

    /* 绑定地址和端口信息 */
    if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1) {
      perror("bind");
      exit(errno);
    } else
      printf("bind address to socket.\n\r");

    /* 循环接收数据 */
    addr_len = sizeof(c_addr);
    while (1) {
      len = recvfrom(sock, buff, sizeof(buff) - 1, 0,
                     (struct sockaddr *) &c_addr, &addr_len);
      if (len < 0) {
          perror("recvfrom");
          exit(errno);
      }

      buff[len] = '\0';
      printf("收到来自%s:%d的消息:%s\n\r",
             inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port), buff);
    }
    return 0;
}

客户端源代码如下:

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

/*********************************************************************
*filename: simple-udpclient.c
*purpose: 基本编程步骤说明,演示了UDP编程的客户端编程步骤
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 21:22:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    struct sockaddr_in s_addr;
    int sock;
    int addr_len;
    int len;
    char buff[128];

    /* 创建 socket , 关键在于这个 SOCK_DGRAM */
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(errno);
    } else
        printf("create socket.\n\r");

    /* 设置对方地址和端口信息 */
    s_addr.sin_family = AF_INET;
    if (argv[2])
        s_addr.sin_port = htons(atoi(argv[2]));
    else
        s_addr.sin_port = htons(7838);
    if (argv[1])
        s_addr.sin_addr.s_addr = inet_addr(argv[1]);
    else {
        printf("消息必须有一个接收者!\n");
        exit(0);
    }

    /* 发送UDP消息 */
    addr_len = sizeof(s_addr);
    strcpy(buff, "hello i'm here");
    len = sendto(sock, buff, strlen(buff), 0,
                 (struct sockaddr *) &s_addr, addr_len);
    if (len < 0) {
        printf("\n\rsend error.\n\r");
        return 3;
    }

    printf("send success.\n\r");
    return 0;
}

编译程序用下列命令:
引用:
gcc -Wall simple-udpserver.c -o server
gcc -Wall simple-udpclient.c -o client


运行程序用下列命令:
引用:
./server 127.0.0.1 7838
./client 127.0.0.1 7838




 zhoulifa 回复于:2007-02-04 14:02:41

和前一篇文章<Linux网络编程一步一步学-UDP方式点对点通讯>一样,只是在客户端源代码里加一行设置socket属性为广播方式即可。
需要加的一句是:
引用:
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));


源代码变成下面的:

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

/*********************************************************************
*filename: broadc-udpclient.c
*purpose: 基本编程步骤说明,演示了UDP编程的广播客户端编程步骤
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-24 21:30:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    struct sockaddr_in s_addr;
    int sock;
    int addr_len;
    int len;
    char buff[128];
    int yes;

    /* 创建 socket */
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(errno);
    } else
        printf("create socket.\n\r");

    /* 设置通讯方式对广播,即本程序发送的一个消息,网络上所有主机均可以收到 */
    yes = 1;
    setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
    /* 唯一变化就是这一点了 */

    /* 设置对方地址和端口信息 */
    s_addr.sin_family = AF_INET;
    if (argv[2])
        s_addr.sin_port = htons(atoi(argv[2]));
    else
        s_addr.sin_port = htons(7838);
    if (argv[1])
        s_addr.sin_addr.s_addr = inet_addr(argv[1]);
    else {
        printf("消息必须有一个接收者!\n");
        exit(0);
    }

    /* 发送UDP消息 */
    addr_len = sizeof(s_addr);
    strcpy(buff, "hello i'm here");
    len = sendto(sock, buff, strlen(buff), 0,
                 (struct sockaddr *) &s_addr, addr_len);
    if (len < 0) {
        printf("\n\rsend error.\n\r");
        return 3;
    }

    printf("send success.\n\r");
    return 0;
}

编译这个程序用下列命令:
引用:
gcc -Wall broadc-udpclient.c -o client


运行程序用下列命令:
引用:
./client 192.168.0.255 7838


就会往192.168.0网络内所有主机发消息。
其它主机如果运行了服务端:
./server 自己的IP地址 7838
则都会收到上述客户端发的消息了。


 zhoulifa 回复于:2007-02-04 14:09:10

这里以下图所示的网络为基础来说明网络通讯的各种方式:


什么是广播?
引用:
以前面的文章<Linux网络编程一步一步学-UDP方式广播通讯>为例:就是用下列命令在上图所示的主机192.168.100.xa上运行客户端程序:
./client 192.168.100.255 7838
则上图所示网络上的所有主机,只要其IP地址192.168.100.*与网络掩码(比如255.255.255.0)运算得到的子网(比如 192.168.100.0)与192.168.100.xa主机所在的子网是一样的,都会在自己的7838端口收到192.168.xa主机发出来的 UDP消息。消息会被复制并发到每个主机的网卡上去,网卡收到消息后提交给操作系统去处理,操作系统发现有程序在7838端口接收UDP数据则把消息转给相应的程序去处理,如果没有程序接收来自7838端口的UDP消息,则操作系统丢弃该消息。


/*********************************************************************
*filename: Linux网络编程一步一步学-网络广播、组播与单播
*purpose: 说明网络广播、组播与单播
*tidied by: zhoulifa([email]zhoulifa@163.com[/email]) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 13:10:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
广播的缺点:不管主机是否有程序接收广播消息,广播消息一定会被网卡收到并提交给操作系统去处理,所以会造成网络上流量增大,对不接收广播消息的主机造成一定的负担。

什么是单播?
引用:
以前面的文章<Linux网络编程一步一步学-客户端和服务器端互相收发消息>为例:就是用下列命令在上图所示的主机192.168.100.xa上运行客户端程序:
./client 192.168.100.xf 7838
则消息只会从192.168.100.xa主机发到192.168.100.xf主机上,192.168.100.xf主机的网卡收到消息后转给操作系统去处理,操作系统再把此消息转给相应程序去处理,如果没有程序处理就丢弃该包。


TCP方式和UDP方式都可以实现单播。也是大多数情况下网络通讯所采取的方式。

什么是组播?
引用:
以后面的文章<Linux网络编程一步一步学-UDP组播>为例:就是用下列命令在上图所示的主机192.168.100.xa上运行客户端程序:
./mcastclient 230.1.1.1 7838
则消息只会从192.168.100.xa主机发到加入了组230.1.1.1的主机的7838端口。象广播一样,组播消息一样会被复制发到网络所有主机的网卡上,但只有宣布加入230.1.1.1这个组的主机的网卡才会把数据提交给操作系统去处理。如果没有加入组,则网卡直接将数据丢弃。
要想接收组播消息的主机必须运行命令加入组,如下方式:
./mcastserver 230.1.1.1 7838


注意:按照RFC规定,组播地址范围是D类IP地址,即224.0.0.1-239.255.255.255。
组播IP地址不能用我们平时所有的C类IP地址。


 zhoulifa 回复于:2007-02-04 14:17:26

组播客户端代码如下:

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

#define BUFLEN 255
/*********************************************************************
*filename: mcastclient.c
*purpose: 演示组播编程的基本步骤,其实这就是一个基本的UDP客户端程序
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 13:10:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    struct sockaddr_in peeraddr, myaddr;

    int sockfd;
    char recmsg[BUFLEN + 1];
    unsigned int socklen;

/* 创建 socket 用于UDP通讯 */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
      printf("socket creating error\n");
      exit(1);
    }
    socklen = sizeof(struct sockaddr_in);

    /* 设置对方的端口和IP信息 */
    memset(&peeraddr, 0, socklen);
    peeraddr.sin_family = AF_INET;
    if (argv[2])
      peeraddr.sin_port = htons(atoi(argv[2]));
    else
      peeraddr.sin_port = htons(7838);
    if (argv[1]) {
        /* 注意这里设置的对方地址是指组播地址224.0.0.0-239.255.255.255,而不是对方的实际IP地址 */
      if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
          printf("wrong group address!\n");
          exit(0);
      }
    } else {
      printf("no group address!\n");
      exit(0);
    }

    /* 设置自己的端口和IP信息 */
    memset(&myaddr, 0, socklen);
    myaddr.sin_family = AF_INET;
    if (argv[4])
      myaddr.sin_port = htons(atoi(argv[4]));
    else
      myaddr.sin_port = htons(23456);

    if (argv[3]) {
      if (inet_pton(AF_INET, argv[3], &myaddr.sin_addr) <= 0) {
          printf("self ip address error!\n");
          exit(0);
      }
    } else
      myaddr.sin_addr.s_addr = INADDR_ANY;

    /* 绑定自己的端口和IP信息到socket上 */
    if (bind
      (sockfd, (struct sockaddr *) &myaddr,
       sizeof(struct sockaddr_in)) == -1) {
      printf("Bind error\n");
      exit(0);
    }

    /* 循环接受用户输入的消息发送组播消息 */
    for (;;) {
        /* 接受用户输入 */
      bzero(recmsg, BUFLEN + 1);
      if (fgets(recmsg, BUFLEN, stdin) == (char *) EOF)
          exit(0);
        /* 发送消息 */
      if (sendto
          (sockfd, recmsg, strlen(recmsg), 0,
           (struct sockaddr *) &peeraddr,
           sizeof(struct sockaddr_in)) < 0) {
          printf("sendto error!\n");
          exit(3);
      }
      printf("'%s' send ok\n", recmsg);
    }
}

组播服务器端程序源代码为:

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>

#define BUFLEN 255
/*********************************************************************
*filename: mcastserver.c
*purpose: 演示组播编程的基本步骤,组播服务器端,关键在于加入组
*tidied by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 13:20:00
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/
int main(int argc, char **argv)
{
    struct sockaddr_in peeraddr;
    struct in_addr ia;
    int sockfd;
    char recmsg[BUFLEN + 1];
    unsigned int socklen, n;
    struct hostent *group;
    struct ip_mreq mreq;

    /* 创建 socket 用于UDP通讯 */
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        printf("socket creating err in udptalk\n");
        exit(1);
    }

    /* 设置要加入组播的地址 */
    bzero(&mreq, sizeof(struct ip_mreq));
    if (argv[1]) {
        if ((group = gethostbyname(argv[1])) == (struct hostent *) 0) {
            perror("gethostbyname");
            exit(errno);
        }
    } else {
        printf
            ("you should give me a group address, 224.0.0.0-239.255.255.255\n");
        exit(errno);
    }

    bcopy((void *) group->h_addr, (void *) &ia, group->h_length);
    /* 设置组地址 */
    bcopy(&ia, &mreq.imr_multiaddr.s_addr, sizeof(struct in_addr));

    /* 设置发送组播消息的源主机的地址信息 */
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);

    /* 把本机加入组播地址,即本机网卡作为组播成员,只有加入组才能收到组播消息 */
    if (setsockopt
        (sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
         sizeof(struct ip_mreq)) == -1) {
        perror("setsockopt");
        exit(-1);
    }

    socklen = sizeof(struct sockaddr_in);
    memset(&peeraddr, 0, socklen);
    peeraddr.sin_family = AF_INET;
    if (argv[2])
        peeraddr.sin_port = htons(atoi(argv[2]));
    else
        peeraddr.sin_port = htons(7838);
    if (argv[1]) {
        if (inet_pton(AF_INET, argv[1], &peeraddr.sin_addr) <= 0) {
            printf("Wrong dest IP address!\n");
            exit(0);
        }
    } else {
        printf("no group address given, 224.0.0.0-239.255.255.255\n");
        exit(errno);
    }

    /* 绑定自己的端口和IP信息到socket上 */
    if (bind
        (sockfd, (struct sockaddr *) &peeraddr,
         sizeof(struct sockaddr_in)) == -1) {
        printf("Bind error\n");
        exit(0);
    }

    /* 循环接收网络上来的组播消息 */
    for (;;) {
        bzero(recmsg, BUFLEN + 1);
        n = recvfrom(sockfd, recmsg, BUFLEN, 0,
                     (struct sockaddr *) &peeraddr, &socklen);
        if (n < 0) {
            printf("recvfrom err in udptalk!\n");
            exit(4);
        } else {
            /* 成功接收到数据报 */
            recmsg[n] = 0;
            printf("peer:%s", recmsg);
        }
    }
}

编译程序用下列命令:
引用:
gcc -Wall mcastclient.c -o mcastclient
gcc -Wall mcastserver.c -o mcastserver


运行程序用如下命令:
引用:
./mcastserver 230.1.1.1 7838


客户端程序运行命令为:
引用:
./mcastclient 230.1.1.1 7838 192.168.100.1 12345


注意组播地址的不同!
不是实际的IP地址,我们的IP地址是C类IP,组播用的D类IP,而且是虚拟出来的,未绑定在任何实际网卡设备上。


 zhoulifa 回复于:2007-02-04 14:33:31

服务器端源代码如下:

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

#define MAXBUF 1024
/************关于本文档********************************************
*filename: sync-server.c
*purpose: 演示网络同步通讯,这是服务器端程序
*wrote by: zhoulifa(zhoulifa@163.com) 周立发(http://zhoulifa.bokee.com)
Linux爱好者 Linux知识传播者 SOHO族 开发者 最擅长C语言
*date time:2007-01-25 20:26
*Note: 任何人可以任意复制代码并运用这些文档,当然包括你的商业用途
* 但请遵循GPL
*Thanks to: Google.com
*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力
* 科技站在巨人的肩膀上进步更快!感谢有开源前辈的贡献!
*********************************************************************/

int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
    struct sockaddr_in my_addr, their_addr;
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];

    if (argv[1])
      myport = atoi(argv[1]);
    else
      myport = 7838;

    if (argv[2])
      lisnum = atoi(argv[2]);
    else
      lisnum = 2;

    if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
      perror("socket");
      exit(1);
    }

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = PF_INET;
    my_addr.sin_port = htons(myport);
    if (argv[3])
      my_addr.sin_addr.s_addr = inet_addr(argv[3]);
    else
      my_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr))
      == -1) {
      perror("bind");
      exit(1);
    }

    if (listen(sockfd, lisnum) == -1) {
      perror("listen");
      exit(1);
    }

    while (1) {
      printf
          ("\n----等待新的连接到来开始新一轮聊天……\n");
      len = sizeof(struct sockaddr);
      if ((new_fd =
           accept(sockfd, (struct sockaddr *) &their_addr,
                  &len)) == -1) {
          perror("accept");
          exit(errno);
      } else
          printf("server: got connection from %s, port %d, socket %d\n",
                 inet_ntoa(their_addr.sin_addr),
                 ntohs(their_addr.sin_port), new_fd);

      /* 开始处理每个新连接上的数据收发 */
      while (1) {
          bzero(buf, MAXBUF + 1);
          printf("请输入要发送给对方的消息:");
          fgets(buf, MAXBUF, stdin);
          if (!strncasecmp(buf, "quit", 4)) {
              printf("自己请求终止聊天!\n");
              break;
          }
          len = send(new_fd, buf, strlen(buf) - 1, 0);
          if (len > 0)
              printf
                  ("消息:%s\t发送成功,共发送了%d个字节!\n",
                   buf, len);
          else {
              printf
                  ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
   &n