linux socket 网络进程间通信 (光谷王凯的博客)

服务端:

1,创建fd

    /*
     * socket(int domain, int type, int protocol)
     * domain:
     *   AF_INET: IPv4
     *   AF_INET6:IPv6
     * type:
     *   SOCK_DGRAM: if protocol = 0, mains UDP protocol.
     *   SOCK_STREAM: if protocol = 0, mains TCP protocol.
     * protocol:
     *  actually zero, main default protocol.
     */
    int    listenfd;
    if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }

2,设置服务器地址

structsockaddr_in结构初始化:

    struct sockaddr_in     servaddr;	
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);

3,绑定第1节生成的fd和服务器地址

    if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1 ) {
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
        goto listenfd_close;
    }

出错处理:

bindsocket error: Address already in use(errno: 98)

在服务程序被终止后,再次调用服务端程序,很容易出现上面一串错误信息。原因是底层设计socketbind的时候要等到一定时间超时才彻底释放相关资源。需要加入一个设置函数,保证socket可以重复bind。只需要在bind前,socket函数后面,加入如下设置代码:

    /*
     * for error:
     *      bind socket error: Address already in use(errno: 98)
     */
    int opt = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
        &opt, sizeof(opt)) < 0) {
        perror("setsockopt failed!");
        goto listenfd_close;
    }

4,设置多少个客户端的请求被放入队列

    if ( listen(listenfd, 10) == -1 ) {
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
        goto listenfd_close;
    }

5,服务端主程序逻辑

客户端:

1,生成客户端的fd

        int    sockfd;
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
            return 0;
        }

2,填充目标服务器的地址信息

        struct sockaddr_in    servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(6666);
        if( inet_pton(AF_INET, *ipaddress, &servaddr.sin_addr) <= 0){
            printf("inet_pton error for %s\n", *argv);
            goto sockfd_close;
        }

inet_pton的作用是将指定域(此处为AF_INET)的字符串ip地址转换为二进制的网络格式ip

3,建立连接

        if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
            printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
            goto sockfd_close;
        }

4,读写数据

recvsend

源码:

配置文件common_cfg.h

#ifndef _COMMON_CFG_H__
#define _COMMON_CFG_H__

#define TEST_SZ         0x400000

#define SEND_SZ_ONE_TIME    256

#define NEED_CHECK     1

#endif

服务端程序server.c

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

char    buff[TEST_SZ];

int call_server(int argc, char **argv)
{
    int    listenfd, connfd;
    struct sockaddr_in     servaddr;
    int     n;

    /*
     * socket(int domain, int type, int protocol)
     * domain:
     *   AF_INET: IPv4
     *   AF_INET6:IPv6
     * type:
     *   SOCK_DGRAM: if protocol = 0, mains UDP protocol.
     *   SOCK_STREAM: if protocol = 0, mains TCP protocol.
     * protocol:
     *  actually zero, main default protocol.
     */
    // TCP protocol
    if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
        return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);

    /*
     * for error:
     *      bind socket error: Address already in use(errno: 98)
     */
    int opt = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
        &opt, sizeof(opt)) < 0) {
        perror("setsockopt failed!");
        goto listenfd_close;
    }

    if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1 ) {
        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
        goto listenfd_close;
    }

    /*
     * 10 client connection request will be queued...
     */
    if ( listen(listenfd, 10) == -1 ) {
        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
        goto listenfd_close;
    }

    printf("======waiting for client's request======\n");
    int recCnt = 0;
    while (1) {
        if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
            continue;
        }

    #if NEED_CHECK
        char ack[32];
        char tmpbuf[SEND_SZ_ONE_TIME - 4];
    tryagain:
        n = recv(connfd, tmpbuf, SEND_SZ_ONE_TIME, 0);
        if (n <= 0) {
            close(connfd);
            continue;
        }

        if (*(volatile unsigned int *)(tmpbuf + SEND_SZ_ONE_TIME - 4)
            == 0x12345678) {
            strcpy(ack, "well done");
        } else {
            strcpy(ack, "dame it");
        }
        ack[strlen(ack) + 1] = '\0';
        if (send(connfd, ack, strlen(ack) + 1, 0) < 0) {
            printf("send ack error\n");
            goto tryagain;
        }
        memcpy(buff + recCnt, tmpbuf, SEND_SZ_ONE_TIME - 4);
        recCnt += (n - 4);
    #else
        n = recv(connfd, buff + recCnt, SEND_SZ_ONE_TIME, 0);
        if (n <= 0) {
            close(connfd);
            continue;
        }
        recCnt += n;
    #endif
        if (recCnt >= sizeof(buff)) {
            printf("finished, check val ");
            int st = 0;
            for (int i=0; i<sizeof(buff); i+=4) {
                if (*(volatile unsigned int *)(buff+i) != i) {
                    st = 1;
                    break;
                }
            }
            printf("%s\n", st==0 ? "OK" : "Error");
            goto connfd_close;
        }
        close(connfd);
    }

connfd_close:
    close(connfd);
listenfd_close:
    close(listenfd);

    return 0;
}

int main(int argc, char **argv)
{
    call_server(1, NULL);

    exit(0);
}

客户端client.c

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

char    sendline[TEST_SZ];
char    recvline[32];

int call_client(int argc, char **argv)
{
    int    sockfd;
    struct sockaddr_in    servaddr;

    if ( argc != 2 ) {
        printf("usage: ./client <ipaddress>\n");
        exit(0);
    }

    /*
     * init simulation datas to be send ...
     */
    for (int i=0; i<sizeof(sendline); i+=4) {
        *(volatile unsigned int *)(sendline+i) = i;
    }

    struct timeval start, end;
    gettimeofday(&start, 0);

    int sendCnt = 0;
    int step = 0;
    while (1) {
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
            return 0;
        }

        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(6666);
        if( inet_pton(AF_INET, *argv, &servaddr.sin_addr) <= 0){
            printf("inet_pton error for %s\n", *argv);
            goto sockfd_close;
        }

        if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
            printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
            goto sockfd_close;
        }
    #if NEED_CHECK
        char ack[32];
        char tmpbuf[SEND_SZ_ONE_TIME - 4];
        *(volatile unsigned int *)(tmpbuf + SEND_SZ_ONE_TIME - 4) = 0x12345678;
    tryagain:
        memcpy(tmpbuf, sendline + sendCnt, SEND_SZ_ONE_TIME - 4);
        if( (step = send(sockfd, tmpbuf, SEND_SZ_ONE_TIME, 0)) < 0 )
        {
            printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
            goto sockfd_close;
        }
        if (recv(sockfd, ack, sizeof(ack), 0) <= 0) {
            printf("ack from server failed!\n");
            goto tryagain;
        } else {
            if (strcmp(ack, "well done")) {
                printf("ack = %s, error\n", ack);
                goto tryagain;
            }
        }
        sendCnt += (step - 4);
    #else
        if( (step = send(sockfd, sendline + sendCnt, SEND_SZ_ONE_TIME, 0)) < 0 )
        {
            printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
            goto sockfd_close;
        }
        sendCnt += step;
    #endif

        if (sendCnt >= sizeof(sendline)) {
            printf("\n");
            printf("send finished!\n");
            close(sockfd);
            break;
        }
        close(sockfd);
    }

    gettimeofday(&end, 0);

    long ms = end.tv_usec/1000 + end.tv_sec*1000  /* end time */
        - (start.tv_usec/1000 + start.tv_sec*1000) /* start time */;
    printf("Total size = %d, speed = %lu bytes/second\n",
           sendCnt, sizeof(sendline) / (ms / 1000));

    return 0;

sockfd_close:
    close(sockfd);
    return 0;
}

int main(int argc, char **argv)
{
    char *ip = "172.16.1.164";
    call_client(2, &ip);

    exit(0);
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值