socket服务端、客户端代码实现数据通信

本文介绍了使用C语言实现TCP和UDP协议的数据通信。对比了两者在连接方式、可靠性、数据处理等方面的差异,并详细阐述了服务端的创建套接字、绑定、监听、接受连接及数据交互的流程。同时,提供了服务端和客户端的代码示例,以及在Linux终端的运行结果。在多客户端场景下,为避免数据竞争,建议服务端仅接收客户端发送的数据。
摘要由CSDN通过智能技术生成

PS:TCP/UDP对比:

1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接

2.  TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

3.  TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)

4.  每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5.  TCP首部开销20字节;UDP的首部开销小,只有8个字节

6.  TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

一、服务端编写思路流程

1.socket()创建套接字

2.bind()为套接字添加信息(IP地址和端口号)

3.listen()监听网络连接

4.accept()监听到有客户端接入,接受一个连接

5.read()、write()数据交互

 二、服务端代码实现

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

//       int socket(int domain, int type, int protocol);
//       int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//       int listen(int sockfd, int backlog);
//       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//       void *memset(void *s, int c, size_t n);

int main(int argc, char **argv[])
{
        int s_fd;
        int c_fd;
        int clen;
        int n_read;
        char readBuf[128];
        char *msg = "I get your connect";

        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;

        memset(&s_addr, 0, sizeof(struct sockaddr_in));
        memset(&c_addr, 0, sizeof(struct sockaddr_in));
        //1. socket(创建套接字)
        s_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (s_fd == -1){
                perror("socket");
                exit(-1);
        }

        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &s_addr.sin_addr);
        //2. bind(为套接字添加信息, ip地址和端口号)
        bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
        //3. listen(监听网络连接)
        listen(s_fd, 10);
        //4. accept(监听到有客户端接入,接受一个连接)
        clen = sizeof(struct sockaddr_in);
        while(1){
                c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
                if (c_fd == -1){
                        perror("accept");
                }
                else {
                        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
                }
                //5. read(读数据)
                memset(readBuf, 0, 128);
                n_read = read(c_fd, readBuf, 128);
                if (n_read == -1){
                        perror("read");
                }
                printf("get message: %d, %s\n", n_read, readBuf);
                //6. wiite(写数据)
                write(c_fd, msg, strlen(msg));
        }

        return 0;
}
~    

 三、客户端代码实现

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

//       int socket(int domain, int type, int protocol);
//       int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//       void *memset(void *s, int c, size_t n);

int main(int argc, char **argv[])
{
        int c_fd;
        int clen;
        int n_read;
        char readBuf[128];
        memset(readBuf, 0, 128);  //memset(msg, 0, sizeof(msg),否则服务端读取数据会有乱码memset(msg, 0, sizeof(msg))
        char *msg = "get message from client";

        struct sockaddr_in c_addr;

        memset(&c_addr, 0, sizeof(struct sockaddr_in));
        //1. socket(创建套接字)
        c_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (c_fd == -1){
                perror("socket");
                exit(-1);
        }

        c_addr.sin_family = AF_INET;
        c_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &c_addr.sin_addr);
        //2. connect(客户端连接)
        if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1){
                perror("connect");
                exit(-1);
        }
        //5. send(发数据)
        write(c_fd, msg, strlen(msg));
        //6. read(读数据)
        n_read = read(c_fd, readBuf, 128);
        if (n_read == -1){
                perror("read");
        }
        else {
                printf("get message from server: %d, %s\n", n_read, readBuf);
        }

        return 0;
}

四、Linux终端运行结果

五、实现双方聊天

1.服务端代码

o.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>

//       int socket(int domain, int type, int protocol);
//       int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//       int listen(int sockfd, int backlog);
//       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//       void *memset(void *s, int c, size_t n);

int main(int argc, char **argv)
{
        int s_fd;
        int c_fd;
        int clen;
        int n_read;
        int mark = 0;
        char readBuf[128];
//      char *msg = "I get your connect";
        char msg[128] = {0};

        if (argc != 3){
                printf("the param is not good\n");
                exit(-1);
        }

        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;

        memset(&s_addr, 0, sizeof(struct sockaddr_in));
        memset(&c_addr, 0, sizeof(struct sockaddr_in));
        //1. socket(创建套接字)
        s_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (s_fd == -1){
                perror("socket");
                exit(-1);
        }

        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &s_addr.sin_addr);
        //2. bind(为套接字添加信息, ip地址和端口号)
        bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
        //3. listen(监听网络连接)
        listen(s_fd, 10);
        //4. accept(监听到有客户端接入,接受一个连接)
        clen = sizeof(struct sockaddr_in);
        while(1){
                c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
                if (c_fd == -1){
                        perror("accept");
                }
                else {
                        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
                }
                mark++;
                if (fork() == 0){
                        if (fork() == 0){
                                while(1){
                                        memset(msg, 0, sizeof(msg));
                                        printf("input: ");
                                        fgets(msg, 10, stdin);
                                        write(c_fd, msg, strlen(msg));
                                }
                        }
                        while(1){
                                memset(readBuf, 0, sizeof(readBuf));
                                n_read = read(c_fd, readBuf, 128);
                                if (n_read == -1){
                                        perror("read");
                                }
                                printf("get message: %d, %s\n", n_read, readBuf);
                        }
                        break;
                }
        }

        return 0;
}
   

2.客户端代码

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

//       int socket(int domain, int type, int protocol);
//       int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//       void *memset(void *s, int c, size_t n);

int main(int argc, char **argv)
{
        int c_fd;
        int clen;
        int n_read;
        char readBuf[128];
//      char *msg = "get message from client";
        char msg[128] = {0};

        if (argc != 3){
                printf("the param is not good\n");
                exit(-1);
        }

        struct sockaddr_in c_addr;

        memset(&c_addr, 0, sizeof(struct sockaddr_in));
        //1. socket(创建套接字)
        c_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (c_fd == -1){
                perror("socket");
                exit(-1);
        }

        c_addr.sin_family = AF_INET;
        c_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &c_addr.sin_addr);
        //2. connect(客户端连接)
        if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1){
                perror("connect");
                exit(-1);
        }

        while(1){
                if (fork() == 0){
                        while(1){
                                memset(msg, 0, sizeof(msg));
                                printf("input: ");
                                fgets(msg, 10, stdin);
                                write(c_fd, msg, strlen(msg));
                        }
                }
                while(1){
                        memset(readBuf, 0, sizeof(readBuf));
                        n_read = read(c_fd, readBuf, 128);
                        if (n_read == -1){
                                perror("read");
                        }
                        else {
                                printf("get message from server: %d, %s\n", n_read, readBuf);
                        }
                }
        }

        return 0;
}

3.运行结果

ps:如果开启多个客户端时,服务端给客户端发送数据,客户端接收读取数据时会存在竞争,为了避免这种情况服务端就不要使用fgets(),只允许客户端给服务端键盘输入发送数据,服务端负责就收该数据即可,服务端代码如下:


//       int socket(int domain, int type, int protocol);
//       int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//       int listen(int sockfd, int backlog);
//       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//       void *memset(void *s, int c, size_t n);

int main(int argc, char **argv)
{
        int s_fd;
        int c_fd;
        int clen;
        int n_read;
        int mark = 0;
        char readBuf[128];
//      char *msg = "I get your connect";
        char msg[128] = {0};

        if (argc != 3){
                printf("the param is not good\n");
                exit(-1);
        }

        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;

        memset(&s_addr, 0, sizeof(struct sockaddr_in));
        memset(&c_addr, 0, sizeof(struct sockaddr_in));
        //1. socket(创建套接字)
        s_fd = socket(AF_INET, SOCK_STREAM, 0);
        if (s_fd == -1){
                perror("socket");
                exit(-1);
        }

        s_addr.sin_family = AF_INET;
        s_addr.sin_port = htons(atoi(argv[2]));
        inet_aton(argv[1], &s_addr.sin_addr);
        //2. bind(为套接字添加信息, ip地址和端口号)
        bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
        //3. listen(监听网络连接)
        listen(s_fd, 10);
        //4. accept(监听到有客户端接入,接受一个连接)
        clen = sizeof(struct sockaddr_in);
        while(1){
                c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
                if (c_fd == -1){
                        perror("accept");
                }
                else {
                        printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
                }
                mark++;
                if (fork() == 0){
                        if (fork() == 0){
                                while(1){
                                        sprintf(msg, "welcom No.%d client", mark);
                                        write(c_fd, msg, strlen(msg));
                                        sleep(3);
                                }
                        }
                        while(1){
                                memset(readBuf, 0, sizeof(readBuf));
                                n_read = read(c_fd, readBuf, 128);
                                if (n_read == -1){
                                        perror("read");
                                }
                                printf("get message: %d, %s\n", n_read, readBuf);
                        }
                        break;
                }
        }

        return 0;
}

代码结果如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值