基于UDP的服务器和客户端之间的通信

一、socket 网络套接字

创建
函数原型:
int socket(int domain, int type, int protocol);
参数说明: 
domain:协议域,又称协议族(family)。常用的协议族有AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域Socket)、AF_ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
type:指定Socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式Socket(SOCK_STREAM)是一种面向连接的Socket,针对于面向连接的TCP服务应用。数据报式Socket(SOCK_DGRAM)是一种无连接的Socket,对应于无连接的UDP服务应用。
protocol:指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
注意:1.type和protocol不可以随意组合,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当第三个参数为0时,会自动选择第二个参数类型对应的默认协议。
2.WindowsSocket下protocol参数中不存在IPPROTO_STCP
二、sockaddr_in 
sockaddr_in(在netinet/in.h中定义):
1
2
3
4
5
6
7
8
9
10
11
12
13
struct sockaddr_in
 
{
 
short sin_family;/*Address family一般来说AF_INET(地址族)PF_INET(协议族)*/
 
unsigned short sin_port;/*Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
 
struct in_addr sin_addr;/*IP address in network byte order(Internet address)*/
 
unsigned char sin_zero[8];/*Same size as struct sockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
 
};
in_addr结构
1
2
3
4
5
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
s_addr按照网络字节顺序存储IP地址

三、绑定

int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);
参数说明:
socket:是一个套接字描述符。
address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号
address_len:确定address缓冲区的长度。
返回值:
如果函数执行成功,返回值为0,否则为SOCKET_ERROR。

四、接收

函数原型:
ssize_t recvfrom(int sockfd, void buf, int len, unsigned int flags, struct socketaddr* from, socket_t* fromlen);
参数说明:
sockfd:标识一个已连接套接口的描述字。
buf:接收数据缓冲区
len:缓冲区长度。
flags:调用操作方式。是以下一个或者多个标志的组合体,可通过or操作连在一起:
from:(可选)指针,指向装有源地址的缓冲区。
fromlen:(可选)指针,指向from缓冲区长度值。

五、发送

函数原型:
int sendto( SOCKET s, const char FAR* buf, int size, int flags, const struct sockaddr FAR* to, int tolen);
参数说明:
s套接字
buf:待发送数据的缓冲区
size:缓冲区长度
flags:调用方式标志位, 一般为0, 改变Flags,将会改变Sendto发送的形式
addr:(可选)指针,指向目的套接字的地址
tolen:addr所指地址的长度
返回值:
如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR。

基于UDP的服务器和客户端之间的通信

服务器端代码:

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

int main(int argc,char *argv[])
{
        if(argc!=3)
        {
                printf("Usage ./server [ip]  [port]\n");
                return 1;
        }
        int sock=socket(AF_INET,SOCK_DGRAM,0);
        if(sock<0)
        {
                perror("socket");
                return 2;
        }

        struct sockaddr_in local;

 local.sin_family=AF_INET;//ipv4
        local.sin_port=htons(atoi(argv[2]));//主机字节序转换为网络字节序
        local.sin_addr.s_addr=inet_addr(argv[1]);//字符串转in_addr的函数


        if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
        {
                perror("bind");
                return 3;
        }


        char buf[1024];
        struct sockaddr_in client;
        while(1)
        {
                socklen_t len=sizeof(client);
                ssize_t s=recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&client,&len);
                if(s>0)
                {
                        buf[s]=0;

                        printf("[%s:%d]:%s\n",inet_ntoa(client.sin_addr),\

   ntohs(client.sin_port),buf);
                        sendto(sock,buf,strlen(buf),0, \
                                        (struct sockaddr*)&client,sizeof(client));
                }
        }
        close(sock);
        return 0;
}

客户端的代码:

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

int main(int argc,char *argv[])
{
        if(argc!=3)
        {
           printf("./Usage [ip] [port]\n");
           return 1;
        }
        int sock=socket(AF_INET,SOCK_DGRAM,0);
        if(sock<0)
        {
                perror("socket");
                return 2;

        }

struct sockaddr_in server;
        server.sin_family=AF_INET;
        server.sin_port=htons(atoi(argv[2]));
        server.sin_addr.s_addr=inet_addr(argv[1]);


        char buf[1024];
        struct sockaddr_in peer;
        while(1)
        {
                socklen_t len=sizeof(peer);
                printf("Please Enter# ");
                fflush(stdout);
                ssize_t s=read(0,buf,sizeof(buf)-1);
                if(s>0)
                {
                        buf[s-1]=0;
                        sendto(sock,buf,strlen(buf),0,\
                                        (struct sockaddr*)&server,sizeof(server));
                        ssize_t _s=recvfrom(sock,buf,sizeof(buf)-1,0,\
                                        (struct sockaddr*)&peer,&len);

         if(_s>0)
                        {
                                buf[_s]=0;
                                printf("server echo# %s\n",buf);
                        }
                }
        }
        close(sock);
        return 0;

}

UDP是无连接的,面向数据报,不可靠的传输协议。