socket编程,实现同时收发功能

之前使用socket进行网络编程的时候,以为只能是服务器接收数据,客户端发送数据。在查看相关资料后,发现服务器和客户端都可以实现收发功能。

基础的socket编程请看:
https://blog.csdn.net/yohe12/article/details/104451041
其他基础的函数在此文章中都有讲解

下面介绍几个关键函数:

1int sendto(int sockfd, const void *data, int data_len, unsigned int flags, struct sockaddr *remaddr,sock_lenremaddr_len)

功能:发送数据报,返回实际发送的数据长度,出错时返回-1

参数说明:

sockfd:套接字描述符

data:指向要发送数据的指针

data_len:数据长度

flags:通常为0

remaddr:远端地址:IP地址和端口号

remaddr_len:地址长度

2int recvfrom(int sockfd, void *buf,int buf_len,unsigned int flags,struct sockaddr *from,sock_len *fromlen);

功能:接收数据,返回实际接收的字节数,失败时返回-1

参数说明:

Sockfd:套接字描述符

buf:指向内存块的指针

buf_len:内存块大小,以字节为单位

flags:一般为0

from:远端的地址,IP地址和端口号

fromlen:远端地址长度

在实验的过程中我遇到一个情况:在运行一次服务器程序后,再次运行就会出现bind失败的情况,后面了解到TIME_WAIT状态对bind的影响。

当一个服务器由于某种原因挂掉了,这个服务器就会进入TIME_WAIT状态,进入TIME_WAIT状态后,原来的那个端口相当于还是被占用了,只有等待TIME_WAIT状态结束才能再次连接这个端口,一般要等待2分钟才行。
而我想要立马就能直接连接来进行测试。
那么我们可以使用一个函数来重启服务器:

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

参数说明:
sockfd:套接字描述符。

level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。

optname:需设置的选项。

optval:指针,指向存放选项待设置的新值的缓冲区。

optlen:optval缓冲区长度。

我们需要再socket和bind之间插入这个函数,这样我们在调试的时候直接结束这个进程,就可以不进入TIME_WAIT状态,直接重启服务器。

int opt = 1setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

UDP服务器

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 3333

void udp_server(int sockfd)
{

    socklen_t len;
    char buf[1024] = {0};
    struct sockaddr_in server_addr;
    int n;
    int opt = 1;
    len = sizeof(server_addr);
    memset(&buf, 0, sizeof(buf));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(PORT);

    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  //当服务器非正常断开的时候重启服务器,不会进入TIME_WAIT状态

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("can not bind\n");
        exit(1);
    }

    while (1) {
        printf("========wait for client's request========\n");
        n = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&server_addr, &len);
        buf[n] = '\0';
        printf("receive client's data: %s\n", buf);
        sendto(sockfd, buf, n, 0, (struct sockaddr *)&server_addr, len);
        printf("send data to client: %s\n", buf);
    }

    close(sockfd);
}

int main(int argc, char **argv)
{
    int sockfd;

    if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("create socket false\n");
        exit(1);
    }

    udp_server(sockfd);

    exit(0);
}

UDP客户端

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

#define PORT 3333

void udp_client(int sockfd)
{
    char send_buf[1024] = {0};
    char recv_buf[1024] = {0};

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = inet_addr("192.168.48.129");  //根据自己的IP地址进行改变

    printf("please input msg\n");
    
    while (fgets(send_buf, sizeof(send_buf), stdin) != NULL) {
        printf("send to server: %s\n", send_buf);
        sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));        
        if (recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, NULL, NULL) < 0) {
            printf("recv err\n");
            continue;
        }
        printf("recv from server: %s\n", recv_buf);

        memset(&send_buf, 0, sizeof(send_buf));
        memset(&recv_buf, 0, sizeof(recv_buf));
    }

    close(sockfd);
}


int main(int argc, char **argv)
{
    int sockfd;
    if ((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
        printf("can't create sockfd\n");
        exit(1);
    }

    udp_client(sockfd);

    exit(0);
}

实验过程:服务器等待客户端的数据,当客户端发出数据后,服务器接收数据,并将数据发回给客户端。

在这里插入图片描述

TCP的程序设计和UDP是差不多的,就是将套接字类型改变一下,并且增加connect函数和listen函数的使用。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值