系统编程之UDP网络编程(循环服务器)

1.服务器基本操作

1.用socket()函数创建socket,返回值为socket的标识符sockfd,这个标识符在后面都会用到

2.用结构体sockaddr_in去保存服务器信息(协议,端口,ip)
联合体成员:struct sockaddr_in sever_addr{
sever_addr.sin_family //协议
sever_addr.sin_port //端口,一般大于1024
sever_addr.sin_addr.s_addr //ip
}

3.用bind()函数绑定信息
注意:第二个参数要强转成(struct sockaddr *)类型

4.用sockaddr_in函数保存客户端信息

6.在while循环里面用recvfrom()函数不停的接收信息并打印

7.用close函数关闭socket

服务器代码:

#include 
#include           /* See NOTES */
#include 
#include 
#include 
#include 
#include 
#include 


int main()
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);   //IPV4协议 数据包套接字
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    struct sockaddr_in server_addr;     //用于保存服务器信息
    memset(&server_addr, 0, sizeof(server_addr));   //清零
    server_addr.sin_family = AF_INET;    //协议
    server_addr.sin_port = 8000;        //端口
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");     //ip地址
    //绑定信息
    int ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if(-1 == ret)
    {
        perror("bind");
        exit(1);
    }

    char buf[32] = {0};
    struct sockaddr_in client_addr;   //用于保存客户端信息
    int length = sizeof(client_addr);
    while(1)
    {
        ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &length);
        if(-1 == ret)
        {
            perror("recvfrom");
            exit(1);
        }
        printf("接收%d  : %s\n", client_addr.sin_port, buf);
        memset(buf, 0, sizeof(buf));
    }

    return 0;
}

客户端基本操作

1.用socket()函数创建socket,返回值为socket的标识符sockfd,后面都会用到标识符

2.用sockaddr_in 结构体保存服务器信息

3.在while循环里面

4.用sendto()函数不停的发送信息

5.用close函数关掉socket

客户端代码:

#include 
#include           /* See NOTES */
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(1);
    }

    char buf[32] = {0};
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = 8000;
    server_addr.sin_addr.s_addr =inet_addr("127.0.0.1");

    int length = sizeof(server_addr);
    while(1)
    {
        scanf("%s",buf);

        int ret = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, length);
        if(-1 == ret)
        {
            perror("sendto");
            exit(1);
        }

        if(!strcmp(buf, "bye"))
        {
            break;
        }

        memset(buf, 0, sizeof(buf));
    }

    close(sockfd);
    return 0;
}

常用函数

recvfrom() 函数

函数原型: 函数原型:ssize_t recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from,socket_t *fromlen); ssize_t 相当于 int,socket_t 相当于int ,这里用这个名字为的是提高代码的自说明性。

功能: 经socket接收数据

参数:

sockfd:标识一个已连接套接口的描述字。

buf:接收数据缓冲区。

len:缓冲区长度。

flags:调用操作方式。是以下一个或者多个标志的组合体,可通过or操作连在一起:(默认为0)

from:客户端结构体名的地址 (需要强转成(struct sockaddr *)类型)

fromlen:客户端结构体长度的地址

返回值: 成功则返回接收到的字符数,失败返回-1.

sendto() 函数

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

功能: 使用指定的 sockfd,将指定字节数的数据发送到指定的终结点。

参数:
sockfd:正在监听端口的套接口文件描述符,通过socket获得

buf:发送缓冲区,往往是使用者定义的数组,该数组装有要发送的数据

len:发送缓冲区的大小,单位是字节

flags:填0即可

dest_addr:指向接收数据的主机地址信息的结构体,也就是该参数指定数据要发送到哪个主机哪个进程

addrlen:表示第五个参数所指向内容的长度

返回值: 成功返回发送成功的数据长度,失败返回-1

socket()函数

函数原型: int socket(int sockfd, int type, int protocol);

功能: 用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

参数:
第一个参数: 即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

第二个参数: 指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。

第三个参数(默认为0): 指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

注意: 并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

返回值: 成功的话返回socket的标示符sockid,失败的话返回-1;

bind()函数

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

功能: 把一个地址族中的特定地址赋给socket。当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

参数:
第一个参数: 即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。

第二个参数: 一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:
struct sockaddr_in server_addr{
server_addr.sin_family = AF_INET;
server_addr.sin_port = 800;
server_addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
};
注意要强转成struct sockaddr类型

第三个参数: 对应的是地址的长度。

返回值: 成功返回0,失败返回-1;

通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

close()函数

就好像操作完打开的文件要调用fclose关闭打开的文件,在读写完成之后最后关闭文件。

函数原型: int close(int fd);

参数: 套接字描述符 socket函数和accept函数的返回值;

你可能感兴趣的:(系统编程之UDP网络编程(循环服务器))