linux socket 编程一:简单的服务器和客户端通信

一    什么是socket

socket是最初由伯克利分校为unix设计的通信机制,它对网络通信的底层(各协议栈)进行了封装(例如tcp/ip协议栈由操作系统设计,已经集成到内核中了,通过socket接口可以来调用系统通信方面的内核函数),使得底成的机制对用户来说是透明的。后来其他的厂商也实现了各自的socket,包括微软等公司,为了兼容unix套接字,他们的接口和unix socket的接口是一致的。unix socket 一般分为三种: SOCK_STREAM(tcp socket), SOCK_DGRAM(udp), raw socket(自己指定底成的协议,原生套接字).

二 unix socket使用

1. 包含的头文件

#include      包含了socket接口的声明

#include      包含了tcp/ip(AF_INET)相关地址结构,地址转换函数的声明,sockaddr,sockaddr_in,inet_addr,()inet_ntoa(),inet_aton()


#include    包含了字节序转换的相关函数 htons() ,  ntohs(), htonl(),ntohl()

ps:字节序分为大端自己序,和小端字节序。他们影响数据在内存中存放的规律。大端字节序表示数据的高位存在内存的低地址,数据的低位存放在高地址:如 0x12345678 ,假设内存地址从左到右依次递增,则数据从左往右的排列为: 0x12   0x34  0x56  0x78.小端字节序刚好相反。socket通信中,字节序分为网络字节序和本地字节序,网络字节序规定为大端字节序,本地字节序由自己的环境决定,既可以为大端字节序,也可以为小端字节序。在通信的过程中,如果不统一字节序,将导致数据的不一致性。

2. protocol families 协议家族

协议家族就是一组协议的集合,例如tcp/ip。AF_INET, AF_IPX, AF_PACKET,都是socket定义好的协议家族,其中AF_INET指的就是tcp/ip.以下是linux文档中指明的相关协议家族。

 Name                Purpose                          Man page
 AF_UNIX, AF_LOCAL   Local communication              unix(7)
AF_INET             IPv4 Internet protocols          ip(7)

AF_INET6            IPv6 Internet protocols          ipv6(7)
AF_IPX              IPX - Novell protocols
AF_NETLINK          Kernel user interface device     netlink(7)
AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
AF_AX25             Amateur radio AX.25 protocol

AF_ATMPVC           Access to raw ATM PVCs

AF_APPLETALK        Appletalk                        ddp(7)
AF_PACKET           Low level packet interface       packet(7)



3 .协议家族的地址结构

a.通用的地址结构

不同的协议家族的地址结构是不一样的,不过他们都可以转换成通用的socket地址结构:

           struct sockaddr {
               sa_family_t sa_family;
               char        sa_data[14];
           }


b.AF_INET协议家族的地址结构

            struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */
               in_port_t      sin_port;   /* port in network byte order  2个字节*/
               struct in_addr sin_addr;   /* internet address  4个字节*/

              char ext[8];/*应该有八个字节的填充来兼容sockaddr的,linux文档上没给出*/

           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };

注意:端口和地址都是网络字节序的

3.常用的接口

socket() 生成一个套接字,返回一个文件标志符,失败返回-1

            int socket(int domain, int type, int protocol);

            domian:协议家族

            type:创建生么类型的套接字,常见的类型如下:

                        SOCK_STREAM :

                        Provides sequenced,  reliable,  two-way,  connection-based
                       byte  streams.  An out-of-band data transmission mechanism
                       may be supported.

                       OCK_DGRAM

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

                        Provides raw network protocol access.

               protocol:具体的协议,如果type不是RAW,则type就已经决定了protocol,此时可以填0,当然你以可以指明具体的协议


 getsockopt(), setsockopt() - get and set options on sockets,设置和获取套接字的相关参数,失败返回-1

        int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

       sockfd:创建的socket的文件标志符

      level:级别,To  manipulate(操纵) options  at the  sockets  API  level, level is specified as SOL_SOCKET. For example, to  

                 indicate that an option is to be interpreted by the TCP protocol, level should  be  set  to the protocol number of TCP

      optname:设置参数的名称,如SO_REUSEADDR(关闭一个socket监听的服务器时,该进程将处于TIME_WAIT状态,如果设置了这个选项就可以在TIME_WAIT还没结束时,重新的开启服务器,并不是可以同时运行两个监听服务器的意思)

     optval:设置的值。如 int on = 1; 把&on传进去就好了。


bind() 接口,将一个socket和一个ip绑定,失败返回-1,用于服务器端,客户端无需绑定

int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);


listen() 将一个socket 标记为被动套接字,不能主动发送请求,只能接收请求,用户服务器端。失败-1

      int listen(int sockfd, int backlog);

       backlog 服务器端的监听队列的长度,当请求数超过该队列时,如还有客户端连过来,客户端可能得到ECONNREFUSED的错误。


accept(),获取请求,返回一个新的socket文件定位符,用于和连接方进行数据传输,用于服务器端,失败返回 -1

         int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

         addr 用于输出,客户端的地址信息(地址结构体 sockaddr)(网络字节序)

       addrlen 用于输出 地址长度

注意:返回的新的socket为主动套接字,对原来的套接字没有影响


connect(),客户端连接服务器  ,失败返回-1

int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

          addr 输入参数, 请求连接的服务器端的地址信息


read(), 从socket中赌读取数据,返回实际读取的数据的长度

        ssize_t read(int fd, void *buf, size_t count);

        buf输出输出缓冲区,count缓冲区的大小


write(), 往socket中写入数据,返回实际写入的数据大小

        ssize_t write(int fd, const void *buf, size_t count);

        buf 输入缓冲区, count缓冲区的大小,




服务器端代码示例:

=====================================================================================================#i
#include
#include
#include
#include
#include

#define handle_err(msg) \
    do{perror(msg);exit(EXIT_FAILURE);} while(1)

int main(void)
{
    int backlog = SOMAXCONN;
/**
  * 1.create a socket fd
*/
int ser_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (ser_sockfd < 0)
{
    handle_err('create socket failed!');
}
/**
 * 2. bind socket to a specified addresss,in order to marke the socket as a passive socket,if a socket be a passive it can listen
*/
struct sockaddr_in ser_addr;
memset(&ser_addr, 0, sizeof(struct sockaddr_in));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(5555);
//ser_addr.sin_addr.s_addr = inet_addr('127.0.0.1');
ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(ser_sockfd, (struct sockaddr*)&ser_addr, sizeof(struct sockaddr)) < 0)
{
    handle_err('bind socket failed!');
}
/**
 *3. listen the socket
 */
if (listen(ser_sockfd, backlog) < 0)
{
    handle_err('listen socket failed');
}

/**
 * 4 accept the client require
 */
socklen_t peer_addr_len;
struct sockaddr_in peer_addr;
int conn;
memset(&peer_addr, 0, sizeof(struct sockaddr_in));
if ((conn = accept(ser_sockfd, (struct sockaddr*)&peer_addr, &peer_addr_len)) < 0)
{
    handle_err('accept require failed!');
}
while(1)
{
    char bufr[255] = {0};
    int r = read(conn, bufr, sizeof(bufr));
    fputs(bufr,stdout);
    write(conn, bufr, sizeof(bufr));
}
return 0;
}


==================================================================================================

实现客户端的代码:

#include
#include
#include

#define ERR_EXIT(msg)\
    do{perror(msg);}while(1)

int main(void)
{
    /**
     *the client implement
     */
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd < 0)  
    {   
        ERR_EXIT('create socket failed!');    
    }   
    /**
     * connect server
     */
    struct sockaddr_in clientaddr;
    memset(&clientaddr, 0, sizeof(struct sockaddr_in));
    clientaddr.sin_family = AF_INET;
    clientaddr.sin_port =htons(5555);
    clientaddr.sin_addr.s_addr = INADDR_ANY;
    if (connect(clientfd,(struct sockaddr*)&clientaddr, sizeof(struct sockaddr)) < 0)
    {   
        ERR_EXIT('failed to connect to server!');
    }   
    while(1)
    {   
        char bufw[255] = {0};
        char bufr[255] = {0};
        while(fgets(bufw, sizeof(bufw), stdin))
        {   
            write(clientfd, bufw, sizeof(bufw));
            int r = read(clientfd, bufr, sizeof(bufr));
            fputs(bufr,stdout);
        }
    }
}













                  




 

你可能感兴趣的:(linux socket 编程一:简单的服务器和客户端通信)