Linux——编写一个简单的服务器、客户端(使用TCP)——服务器一直监听本机的xxxx号端口,如果收到连接请求,将接收请求并接收客户端发来的消息;客户端与服务器端建立连接并发送一条消息。

一、题目

编写一个简单的服务器、客户端(使用TCP)——服务器一直监听本机的6666号端口,如果收到连接请求,将接收请求并接收客户端发来的消息;客户端与服务器端建立连接并发送一条消息。

二、知识准备

  1. Linux常用命令(vi/vim/gedit,gcc)等;
  2. 套接字编程基础知识
  3. TCP/IP网络编程

三、硬件准备

  1. VMware Workstation 
  2. CentOs  7镜像以及安装系统

四、套接字知识

1.套接字是网络通信的基本操作单元,提供了不同主机之间进程双向通信的端点。

2.套接字分类:

  • 字节流套接字(流式套接字),基于TCP,面向连接,可靠;
  • 数据报套接字,基于UDP,无连接,不可靠,通常用于单个报文传输或可靠性不重要的场合。
  • 原始套接字,超级用户使用,用于开发新的协议或提取协议比较隐蔽的功能。

3.套接字地址结构:

3.1.通用的地址结构体

struct sockaddr

{

    unsigned    short  sa_family;    //地址家族或协议簇,一般是“AF_xxx”形式

    char  sa_data[14];          //14字节协议地址,里面包含IP地址和端口

}

3.2.地址结构类型的名字为开始,以协议簇为后缀的地址结构体

以INET协议簇为例:

struct sockaddr_in

{

    short  int  sin_family;    //地址家族或协议簇,一般是“AF_INET”形式

    unisigned short int sin_port;   //存储端口号,需要注意网络字节顺序

    struct in_addr sin_addr;    //存储IP地址

    unsigned char sin_zero[8];   //为了使sockaddr_in和sockaddr_in两个地址结构保持大小相同而保留的空字节,协议簇、端口号、IP地址共占用8字节,而sockaddr通用地址结构共16字节,所以sin_zero数组元素个数为8,使用时,将其清零。

}

4.面向连接套接字通信过程

4.1服务端

  • 服务器进程首先调用socket创建一个流式套接字
  • 调用bind将服务器所在的主机地址和端口号绑定在套接字上。
  • 调用listen将套接字转换为被动监听套接字,并在套接字指定的端口上监听客户端连接请求。
  • 调用accept做好与客户端进程建立连接的准备,如果没有客户端连接请求,那么服务器将被阻塞,知道客户端连接为止
  • 当接收客户端的请求后,服务进程被唤醒,生成一个新的套接字,并相互建立连接,使用send(write)和recv(read)来发送和接收数据。

4.2客户端

  • 客户端调用socket创建流式套接字
  • 根据服务器的地址和端口,调用connect向服务进程发出连接请求
  • 服务进程同意后,客户端通过send、recv与服务器进行数据通信
  • 通信结束后,客户端关闭套接字,中断连接

五.代码

创建一个目录socket:mkdir  /socket

创建一个服务器的代码文件:vi  server.c

创建一个客户端的代码文件:vi  client.c

1.服务器代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
int main(int argc, char **argv)
{
    int fd;
    int ret;
    int i;
    struct sockaddr_in serv_addr;
    //创建socket,指定IPv4协议族 TCP协议
fd = socket(AF_INET, SOCK_STREAM, 0);

    if (fd == -1)
    {
          perror("socket create error...");
          exit(1);
}

 //初始化一个地址
  serv_addr.sin_family = AF_INET;         //选择协议族为IPv4
  serv_addr.sin_port = htons(6666);                //绑定端口号
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址

    //绑定服务器地址
    printf("bind...\n");
    ret = bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (ret == -1)//bind failed!
    {
        perror("bind error\n");
    }
printf("listen...\n");//设置监听上限,设定链接上限
    ret = listen(fd, 128);
    if (ret == -1)
    {
            perror("listen error\n");
    }

    printf("wait....\n");
    //等待并接收连接请求
    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);

     printf("accept...\n");
    //监听客户端链接, 会阻塞
    int cfd = accept(fd, (struct sockaddr *)&client_addr, &len);
    if (cfd == -1)
     {
            perror("accept error");
     }
    printf("accept successful\n");

    char ipbuf[64] = { 0 };
    client_addr.sin_port = 6666;

printf("client ip:%s, port:%d\n", 
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)),
        ntohs(serv_addr.sin_port));

    send(cfd,"hello,you are connected!\n",26,0);
    while (1)
    {
            char buf[1024] = { 0 };
            int len = read(cfd, buf, sizeof(buf));
            if (len == -1)
            {
                perror("read error");
            }
            else if (len == 0)
            {
                printf("客户端断开连接\n");
                close(cfd);
                break;
            }
            else
            {
                printf("receive from client:%s\n",buf);
                printf("send(return) to client : %s", buf);
                  send(cfd,buf,100,0);
            }
    }

    close(fd);
    return 0;
}

2.客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h>
#define MAXDATASIZE  100

int main(int argc, char **argv)
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    char buf[512];
    if (fd == -1)
    {
        perror("socket error");
    }
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(6666);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);

    //与服务器建立连接
     printf("create bind to service...\n");
    int ret = connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (ret == -1)
    {
        perror("connect error");
    }

    //接收服务器发过来的bind successful !
    int len = recv(fd,buf,MAXDATASIZE,0);
    printf("%s\n",buf);
while (1)
{
        fgets(buf, sizeof(buf), stdin);
        //send 数据给服务器
        send(fd,buf,MAXDATASIZE,0);

        //接收服务器发过来的数据
        int len = recv(fd,buf,MAXDATASIZE,0);

        if (len == -1)
        {
            perror("read error");
        }
        else if (len == 0)
        {
            printf("服务器端关闭了连接\n");
            break;
        }
        else
        {
            printf("receive  from service : %s\n", buf);
        }
    }
    close(fd);
    return 0;
}

代码写好后,如下操作:

开启两个终端,一个模拟服务器,一个模拟客户端

分别执行一下命令:

gcc   service.c  -o  service

./service

————————————————————

gcc client.c -o client

./client

六、效果图

1.服务器等待连接

2.客户端向服务器发送连接

3.服务器和客户端连接成功

4.连接成功后,服务器向客户端发送一条信息:hello,you are connected!

5.客户端向服务器发送消息,服务器接收并返回

6.客户端接收服务器的返回信息

7.客户端断开连接

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值