Linux学习之socket编程(二)

Linux学习之socket编程(二)

1.C/S模型——UDP

UDP处理模型

  由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现。多保证通讯可靠性的机制需要在应用层实现。编译运行server,在两个终端里各开一个client与server交互,看看server是否具有并发服务的能力。用Ctrl+C关闭server,然后再运行server,看此时client还能否和server联系上。和前面TCP程序的运行结果相比较,体会无连接的含义。

复制代码
/* server.c */

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000

int main(void)
{
    struct sockaddr_in servaddr, cliaddr;//用于IPv4的地址
    socklen_t cliaddr_len;
    int sockfd;//文件描述符
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];//16 Bytes
    int i, n;
    /*构造用于UDP通信的套接字*/
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);//

    bzero(&servaddr, sizeof(servaddr));//将地址清零
    //设置地址
    servaddr.sin_family = AF_INET;/*IPv4*/
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//网络字节数,本地任意IP
    servaddr.sin_port = htons(SERV_PORT);
    bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    printf("Accepting connections ...
");
    while (1) {
        cliaddr_len = sizeof(cliaddr);

        n = recvfrom(sockfd, buf, MAXLINE, 0, (struct sockaddr         *)&cliaddr, &cliaddr_len);

    if (n == -1)
        perr_exit("recvfrom error");
    printf("received from %s at PORT %d
",

        inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
        ntohs(cliaddr.sin_port));

    for (i = 0; i < n; i++)
        buf[i] = toupper(buf[i]);//小写转大写
    
     //发送数据
     n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
     
    if (n == -1)
        perr_exit("sendto error");
    }
}        
复制代码
复制代码
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
    struct sockaddr_in servaddr;
    int sockfd, n;
    char buf[MAXLINE];
    char str[INET_ADDRSTRLEN];
    socklen_t servaddr_len;

    sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);

    while(fgets(buf, MAXLINE, stdin) != NULL) {

    n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
    if (n == -1)
         perr_exit ("sendto error");

    n = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);

    if (n == -1)
        perr_exit("recvfrom error");
   write(STDOUT_FILENO, buf, n);//写到屏幕上
    }
  close(sockfd);
    return 0;
}        
复制代码

2.出错处理封装函数   上面的例子不仅功能简单,而且简单到几乎没有什么错误处理,我们知道,系统调用不能保证每次都成功,必须进行出错处理,这样一方面可以保证程序逻辑正常,另一方面可以迅速得到故障信息。为使错误处理的代码不影响主程序的可读性,我们把与socket相关的一些系统函数加上错误处理代码包装成新的函数,做成一个模块wrap.c:

复制代码
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
void perr_exit(const char *s)
{
    perror(s);
    exit(1);
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    int n;
again:
    if ( (n = accept(fd, sa, salenptr)) < 0) {
    if ((errno == ECONNABORTED) || (errno == EINTR))
goto again;
    else
    perr_exit("accept error");
    }
    return n;
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (bind(fd, sa, salen) < 0)
    perr_exit("bind error");
}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (connect(fd, sa, salen) < 0)
    perr_exit("connect error");
}

void Listen(int fd, int backlog)
{
    if (listen(fd, backlog) < 0)
    perr_exit("listen error");
}

int Socket(int family, int type, int protocol)
{
    int n;
    if ( (n = socket(family, type, protocol)) < 0)
    perr_exit("socket error");
    return n;
}

ssize_t Read(int fd, void *ptr, size_t nbytes)
{
    ssize_t n;
again:
    if ( (n = read(fd, ptr, nbytes)) == -1) {
    if (errno == EINTR)
goto again;
    else
    return -1;
    }
    return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
    ssize_t n;
again:
    if ( (n = write(fd, ptr, nbytes)) == -1) {
    if (errno == EINTR)
    goto again;
    else
    return -1;
   }
   return   n;
}

void Close(int fd)
{
    if (close(fd) == -1)
    perr_exit("close error");
}

ssize_t Readn(int fd, void *vptr, size_t n)//指定读够n个字节返回
{
    size_t nleft;
    ssize_t nread;
    char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
       if ( (nread = read(fd, ptr, nleft)) < 0) {
          if (errno == EINTR)
                nread = 0;
          else
                return -1;
    }   else if (nread == 0)
    break;

    nleft -= nread;
    ptr += nread;

    }
    return n - nleft;
}

ssize_t Writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR)
                    nwritten = 0;
            else
                return -1;
    }
    nleft -= nwritten;
    ptr += nwritten;
    }
    return n;
}

static ssize_t my_read(int fd, char *ptr)
{
    static int read_cnt;
    static char *read_ptr;
    static char read_buf[100];

      if (read_cnt <= 0) {
again:
    if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
    if (errno == EINTR)
    goto again;
    return -1;
    }
     else if (read_cnt == 0)
    return 0;
    read_ptr = read_buf;
    }
    read_cnt--;
    *ptr = *read_ptr++;
    return 1;
}

ssize_t Readline(int fd, void *vptr, size_t maxlen)//一次读一行
{
    ssize_t n, rc;
    char c, *ptr;

    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ( (rc = my_read(fd, &c)) == 1) {
            *ptr++ = c;
              if (c == '
')
                    break;
           } else if (rc == 0) {
            *ptr = 0;
              return n - 1;
            } else
            return -1;
    }
    *ptr = 0;
    return n;
}   
复制代码
复制代码
#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
void perr_exit(const char *s)
{
    perror(s);
    exit(1);
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    int n;
again:
    if ( (n = accept(fd, sa, salenptr)) < 0) {
    if ((errno == ECONNABORTED) || (errno == EINTR))
goto again;
    else
    perr_exit("accept error");
    }
    return n;
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (bind(fd, sa, salen) < 0)
    perr_exit("bind error");
}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    if (connect(fd, sa, salen) < 0)
    perr_exit("connect error");
}

void Listen(int fd, int backlog)
{
    if (listen(fd, backlog) < 0)
    perr_exit("listen error");
}

int Socket(int family, int type, int protocol)
{
    int n;
    if ( (n = socket(family, type, protocol)) < 0)
    perr_exit("socket error");
    return n;
}

ssize_t Read(int fd, void *ptr, size_t nbytes)
{
    ssize_t n;
again:
    if ( (n = read(fd, ptr, nbytes)) == -1) {
    if (errno == EINTR)
goto again;
    else
    return -1;
    }
    return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
    ssize_t n;
again:
    if ( (n = write(fd, ptr, nbytes)) == -1) {
    if (errno == EINTR)
    goto again;
    else
    return -1;
   }
   return   n;
}

void Close(int fd)
{
    if (close(fd) == -1)
    perr_exit("close error");
}

ssize_t Readn(int fd, void *vptr, size_t n)//指定读够n个字节返回
{
    size_t nleft;
    ssize_t nread;
    char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
       if ( (nread = read(fd, ptr, nleft)) < 0) {
          if (errno == EINTR)
                nread = 0;
          else
                return -1;
    }   else if (nread == 0)
    break;

    nleft -= nread;
    ptr += nread;

    }
    return n - nleft;
}

ssize_t Writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
            if (nwritten < 0 && errno == EINTR)
                    nwritten = 0;
            else
                return -1;
    }
    nleft -= nwritten;
    ptr += nwritten;
    }
    return n;
}

static ssize_t my_read(int fd, char *ptr)
{
    static int read_cnt;
    static char *read_ptr;
    static char read_buf[100];

      if (read_cnt <= 0) {
again:
    if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
    if (errno == EINTR)
    goto again;
    return -1;
    }
     else if (read_cnt == 0)
    return 0;
    read_ptr = read_buf;
    }
    read_cnt--;
    *ptr = *read_ptr++;
    return 1;
}

ssize_t Readline(int fd, void *vptr, size_t maxlen)//一次读一行
{
    ssize_t n, rc;
    char c, *ptr;

    ptr = vptr;
    for (n = 1; n < maxlen; n++) {
        if ( (rc = my_read(fd, &c)) == 1) {
            *ptr++ = c;
              if (c == '
')
                    break;
           } else if (rc == 0) {
            *ptr = 0;
              return n - 1;
            } else
            return -1;
    }
    *ptr = 0;
    return n;
}   
复制代码
复制代码
/* wrap.h */
#ifndef __WRAP_H_
#define __WRAP_H_

void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, const struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
void Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
static ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);

#endif
复制代码
复制代码
/* wrap.h */
#ifndef __WRAP_H_
#define __WRAP_H_

void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, const struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
void Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
static ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);

#endif
复制代码
原文地址:https://www.cnblogs.com/zzdbullet/p/9513626.html