C 基于UDP实现一个简易的聊天室

引言

  本文是围绕Linux udp api 构建一个简易的多人聊天室.重点看思路,帮助我们加深

对udp开发中一些api了解.相对而言udp socket开发相比tcp socket开发注意的细节要少很多.

但是水也很深. 本文就当是一个demo整合帮助开发者回顾和继续了解 linux udp开发的基本流程.

首先我们来看看 linux udp 和 tcp的异同.

/*
这里简单比较一下TCP和UDP在编程实现上的一些区别:

TCP流程
     建立一个TCP连接需要三次握手,而断开一个TCP则需要四个分节。当某个应用进程调用close(主动端)后
(可以是服务器端,也可以是客户 端),这一端的TCP发送一个FIN,表示数据发送完毕;另一端(被动端)发送一
个确认,当被动端待处理的应用进程都处理完毕后,发送一个FIN到主动端,并关闭套接口,主动端接收到这个
FIN后再发送一个确认,到此为止这个TCP连接被断开。 UDP套接口   UDP套接口是无连接的、不可靠的数据报协议;既然他不可靠为什么还要用呢?
  其一:当应用程序使用广播或多播是只能使用UDP协议;
  其二:由于它是无连接的,所以速度快。因为UDP套接口是无连接的,如果一方的数据报丢失,那另一方将无
限等待,解决办法是设置一个超时。在编写UDP套接口程序时,有几点要注意:建立套接口时socket函
数的第二个参数应该是SOCK_DGRAM,说明是建立一个UDP套接口;由于UDP是无连接的,所以服务器端
并不需要listen或accept函数;当UDP套接口调用connect函数时,内核只记录连接放的IP地址 和端
口,并立即返回给调用进程.
*/

 参照

    linux udp api简介   http://blog.****.net/wocjj/article/details/8315559

     tcp 和udp区别    http://www.cnblogs.com/Jessy/p/3536163.html

这里简单引述一下 udp相比tcp 用到的两个api .  recvfrom()/sendto() 具体细节如下

#include <sys/types.h>  
#include <sys/socket.h> 

/*
 * 这两个函数基本等同于 一个 send 和 recv . 详细参数解释如下
 * s        : 文件描述符,等同于 socket返回的值
 * buf        : 数据其实地址
 * len        : 发送数据长度或接受数据缓冲区最大长度
 * flags    : 发送标识,默认就用O.带外数据使用 MSG_OOB, 偷窥用MSG_PEEK .....
 * addr     : 发送的网络地址或接收的网络地址
 * alen     : sento标识地址长度做输入参数, recvfrom表示输入和输出参数.可以为NULL此时addr也要为NULL
 *        : 返回0表示执行成功,否则返回<0 . 更多细节查询man手册
 */
extern int sendto (int s, const void *buf, int len, unsigned int flags, const struct sockaddr *addr, int alen);  
extern int recvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *addr, int *alen); 

上面就是两个函数的大致用法. 具体可以查看linux api帮助手册. 最好就用 man sendto / man recvfrom 把那一系列函数都看看.

现在很多文章都是转载,但是找不见转载的地址, 下面会举一个简易的UDP回显服务器的demo加深理解.

前言

  首先看设计图

C 基于UDP实现一个简易的聊天室

有点low. 简单看看吧. 那我们先看 客户端代码  udpclt.c 代码

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define _SHORT_PORT    (8088)

//
// udp client heoo
//
int main(int argc, char * argv[]) {
    int fd, len;
    struct sockaddr_in ar = { AF_INET };
    socklen_t al = sizeof (struct sockaddr_in);
    char msg[BUFSIZ] = ":) 谁也不会喜欢工作狂 ~";

    // 创建 client socket
    if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
        perror("main socket dgram");
        exit(EXIT_FAILURE);
    }

    ar.sin_port = htons(_SHORT_PORT);
    // 开始发送消息包到服务器, 默认走 INADDR_ANY
    sendto(fd, msg, sizeof msg - 1, 0, (struct sockaddr *)&ar, al);
    
    // 开始接收服务器回过来的报文
    len = recvfrom(fd, msg, sizeof msg - 1, 0, (struct sockaddr *)&ar, &al);
    if (len == -1) {
        perror("main recvfrom");
        exit(EXIT_FAILURE);
    }
    msg[len] = '