操作系统课程设计

一、课程设计目标与任务

目的:熟悉linux下socket、网络编程的基本方法,掌握实现客户/服务器程序的编写方法;

任务:编写一个简单的程序,该程序可实现基于TCP协议的简单的客户/服务器方式。

二、涉及的主要理论及算法思想

TCP协议,socket、网络编程

三、具体设计及开发思路

编写两个程序,一个客户端,一个服务器,使两个程序建立socket连接,以相互传送数据。

四、代码

扫描二维码关注公众号,回复: 6383330 查看本文章

Client:

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

int main()
{
        //一、创建套接字socket
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
        {
                perror("socket failed");
                exit(-1);
        }

        //二、准备通信地址
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;      //指定协议族,和socket()的参数保持一致
        addr.sin_port = htons(port);    //设置网络通信使用的端口号
        inet_aton("192.168.1.129", &addr.sin_addr);     //存储网络通信用ip地址

        //三、连接服务器
        int con = connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
        if (con == -1)
        {
                perror("connect failed");
                exit(-1);
        }
        printf("连接成功!\n");

        //四、与服务器交换数据
        char buf[100] = {0};
        char *str = "hello server.";
        send(sockfd, str, strlen(str), 0);
        recv(sockfd, buf, sizeof(buf), 0);
        printf("收到来自服务器的信息:%s\n", buf);

        //五、关闭连接
        close(sockfd);

        return 0;
}

Server

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

int main()
{
        //一、创建套接字
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
        {
                perror("socket failed");
                exit(-1);
        }

        //二、准备通信地址
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        inet_aton("192.168.1.129", &addr.sin_addr);

        //三、绑定socket和通信地址
       int bin = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
        if (bin == -1)
        {
                perror("bind failed");
                exit(-1);
        }

         //四、监听端口
         bin = listen(sockfd, 100);
         if (bin == -1)
         {
                perror("listen failed");
                exit(-1);
         }
         printf("开始监听%d端口...\n", port);

         //五、处理连接请求
         struct sockaddr_in fromaddr;
         socklen_t len = sizeof(fromaddr);
         int clientfd = accept(sockfd, (struct sockaddr *)&fromaddr, &len);
         if (clientfd == -1)
         {
                perror("accept failed");
                exit(-1);
         }
          printf("%s连接\n", inet_ntoa(fromaddr.sin_addr));
          //六、处理客户端数据
        char buf[100] = {0};
          recv(clientfd, buf, sizeof(buf), 0);
          printf("客户端:%s\n", buf);
          char *str = "hello client.";
          send(clientfd, str, strlen(str), 0);

          //七、关闭连接
          close(clientfd);
          close(sockfd);

          return 0;
}

五、程序运行效果展示截图

六、技术小结

1.基于TCP的网络编程:

    基于连接, 在交互过程中, 服务器和客户端要保持连接, 不能断开。重发一切出错数据、数据验证, 保证数据的正确性、完整性和顺序性,

    缺点是消耗的资源比较大。

2.编程步骤:

    服务器:

      ① 创建socket(套接字) socket()

      ② 准备通信地址

      ③ 将创建的socket和通信地址绑定 bind()

      ④ 监听端口 listen()

      ⑤ 等待客户端连接 accpet()

      ⑥ 通信双方收发数据 read()/write()

                 send()/recv()

      ⑦ 关闭socket

    客户端:

      ① 创建socket(套接字) socket()

      ② 准备通信地址

      ③ 连接服务器 connect()

      ④ 收发数据 read()/write()

             send()/recv()

      ⑤ 关闭socket 

3. API详解

(1) socket()函数

    int socket(domain, type, protocol)

    domain:

        AF_UNIX/AF_LOCAL/AF_FILE: 本地通信

        AF_INET: 网络通信 ipv4

        AF_INET6: 网络通信 ipv6

        注:如果AF换成PF效果一样

    type, 选择通信类型, 主要包括:

        SOCK_STREAM: TCP

        SOCK_DGRAM : UDP

    protocol, 本来应该指定通信协议, 但现在基本废弃, 因为协议已经在前面两个参数指定完成,给0即可

(2) bind()函数

    int bind(int sockfd, struct sockaddr *addr, size)

    sockfd: 要绑定的套接字描述符

size: 第二个参数占据的内存空间大小

    addr: 涉及三个数据结构struct sockaddr, sockaddr_un, sockaddr_in

     sockaddr, 主要用于函数参数, 不负责存储数据

     sockaddr_un, 当着本地通信时, 用于本地通信使用的地址 (sys/un.h)

     sockaddr_in, 当着网络通信时, 负责存储网络通信的地址数据

     struct sockaddr_in

{

         sin_family; //用于指定协议族, 和socket()的参数保持一致

         sin_port; //网络通信使用的端口号

         sin_addr; //存储网络通信的ip地址 

     }

(3) listen()函数

    int listen(int sockfd, int backlog)

     sockfd: 将sockfd参数所标识的套接字为被动模式, 使之可以接受连接请求

     backlog: 表示未决连接请求队列的最大长度, 即允许最多有多少个未决连接请求存在。若服务器的未决连接请求已达到该值, 则客户端通过 connect()连接服务器的操作将返回-1,且error为ECONNREFUSED

(4) accpet()函数

    int accpet(sockfd, struct sockaddr* addr, socklen_t *addrlen)

     从sockfd参数所标识套接字对应未决连接请求队列中取出的一个连接请求, 同时创建一个新的套接字,用于该连接通信, 返回套接字的描述符

     addr和addrlen 用于输出连接请求发起者的地址信息

     返回值: 为新创建用于和客户端通信的套接字描述符 失败-1, error

(5) recv()函数  

   int recv(int sockfd, buf, len, flags)

     flags, 通常取0: 阻塞收取数据

        O_NONBLOCK: 不阻塞, 如果未收到数据, 返回错误信息

     返回值:

        >0, 实际接受数据字节数

        -1 , 出错, error

         0 , 通信的另一端关闭

(6)send()函数

   int send(int sockfd, buf, len, flags)

     flags: 通常取0, 阻塞发送

     O_NONBLOCK: 不阻塞, 如果未收到数据, 返回错误信息

(7) connect()函数

   int connect(int sockfd, addr, addr_len)

     参数参考bind()

https://www.cnblogs.com/findumars/p/8030439.html

猜你喜欢

转载自www.cnblogs.com/rnss/p/10993530.html