现在的位置: 首页 > 综合 > 正文

一个简单的linux下原生socket的tcp程序及其修改

2013年10月31日 ⁄ 综合 ⁄ 共 5069字 ⁄ 字号 评论关闭

    最初参考网上资料完成如下:

/*
 * tcpserver.c
 *Author:coder_xia
 * Description:a simple tcp server
 */

#include <sys/socket.h>
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //inet_addr
#include <unistd.h> //close

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFERSIZE 128
#define TCPPORT 8001
#define BACKLOG 5 //max connection number

int main(void) {
    int server_sockfd, client_sockfd;
    int addr_len, result, recv_len, on = 1;
    struct sockaddr_in server_address;
    struct sockaddr_in client_address;
    char recvbuf[BUFFERSIZE];

    //init address
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(TCPPORT);
    addr_len = sizeof(struct sockaddr_in);

    //new server socket
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == server_sockfd) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    //Enable address reuse
    result = setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    //bind and listen
    result = bind(server_sockfd, (struct sockaddr*)&server_address, addr_len);
    if (-1 == result) {
        perror("bind");
        close(server_sockfd);
        exit(EXIT_FAILURE);
    }
    result = listen(server_sockfd, BACKLOG);
    if (-1 == result) {
        perror("listen");
        close(server_sockfd);
        exit(EXIT_FAILURE);
    }

    while (1) {
        memset(recvbuf, 0, BUFFERSIZE);
        printf("server is waiting\n");
        //Accept a connection, accept will block here,
        //so ,there's no need to sleep in this loop like in udp
        client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, &addr_len);
        //recv message from client
        recv_len = recv(client_sockfd, recvbuf, BUFFERSIZE, 0);
        if (-1 == recv_len) { //error while recv
            perror("recv");
            close(client_sockfd);
            close(server_sockfd);
            exit(EXIT_FAILURE);
        } else if (0 == recv_len) { //peer closed the socket
            close(client_sockfd);
            close(server_sockfd);
            exit(EXIT_FAILURE);
        }
        printf("server receieve : %s\n", recvbuf);
        printf("server port : %d\n", ntohs(server_address.sin_port));
        printf("client port : %d\n", ntohs(client_address.sin_port));
        printf("client addr : %s\n", inet_ntoa(client_address.sin_addr));
        //send to client via client_sockfd
        result = send(client_sockfd, recvbuf, recv_len, 0);
        if (-1 == result) {
            perror("send");
            close(client_sockfd);
            close(server_sockfd);
            exit(EXIT_FAILURE);
        }
    }
    close(server_sockfd);
    return 0;
}

不过这个程序是存在一些问题的,虽然情况下都能运行,不过对异常情况没有任何抵抗力

   1、client_sockfd未关闭

   2、%s输出方式,如果收到的字符串不包含\0等特殊情况?(关于%s,更多参考http://blog.sina.com.cn/s/blog_514885cf0100thxk.html

    3、server为所有client服务,如果一个client端出错或者关闭,则关闭server,逻辑处理失当

修改while部分如下:

    while (1) {
        memset(recvbuf, 0, BUFFERSIZE);
        printf("server is waiting\n");
        //Accept a connection, accept will block here,
        //so ,there's no need to sleep in this loop like in udp
        client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, &addr_len);
        //recv message from client
        recv_len = recv(client_sockfd, recvbuf, BUFFERSIZE-1, 0);
        if (-1 == recv_len) { //error while recv
            perror("recv");
            close(client_sockfd);
            continue;
        } else if (0 == recv_len) { //peer closed the socket
            close(client_sockfd);
            continue;
        }
        recvbuf[recv_len] = '\0';
        printf("server receieve : %s\n", recvbuf);
        printf("server port : %d\n", ntohs(server_address.sin_port));
        printf("client port : %d\n", ntohs(client_address.sin_port));
        printf("client addr : %s\n", inet_ntoa(client_address.sin_addr));
        sleep(3);
        //send to client via client_sockfd
        result = send(client_sockfd, recvbuf, recv_len, 0);
        if (result != result) {
            perror("send");
            close(client_sockfd);
            continue;
        }
        close(client_sockfd);
    }

   client部分如下:

/*
 * tcpclient.c
 * Author : coder_xia
 * Description:a simple tcp client
 */

#include <sys/socket.h>
#include <netinet/in.h> //sockaddr_in
#include <arpa/inet.h> //inet_addr
#include <unistd.h>  //close

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TCPPORT 8001
#define LOCALHOST "127.0.0.1"
#define BUFFERSIZE 128

int main(void) {
    int sockfd, addr_len;
    struct sockaddr_in address;
    int result;
    char strsend[] = "hello server,I am client \n";
    char recvbuf[BUFFERSIZE];

    //Init address.
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr(LOCALHOST);
    address.sin_port = htons(TCPPORT);
    //zero the left part of struct
    bzero(&(address.sin_zero), sizeof(address.sin_zero));
    addr_len = sizeof(address);

    //Create socket for the client.
    //After this, we need not to call bind()
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    //Connect to the server's socket via sockfd
    //connect will autoly find an unused port and call bind() to bind socket on that port
    result = connect(sockfd, (struct sockaddr*)&address, addr_len);
    if (result == -1) {
        perror("connect");
        exit(1);
    }

    //Now we can read/write via sockfd.
    memset(recvbuf, 0, BUFFERSIZE);
    result = send(sockfd, strsend, strlen(strsend) + 1, 0);
    if (-1 == result) { //send error
        perror("write");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    result = recv(sockfd, recvbuf, BUFFERSIZE, 0);
    if (-1 == result) { //error while recv
        perror("recv");
        close(sockfd);
        exit(EXIT_FAILURE);
    } else if (0 == result) { //server has been closed
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("recieve from server : %s\n", recvbuf);
    close(sockfd);
    return EXIT_SUCCESS;
}

   存在隐患如下:

  1、发送时采用strlen(数组)+1方法,如果发送缓冲区包含\0,则无法发送所有数据

  2、接收输出%s显示,如接收字符串不包含\0?(实际上,由于是接收保存到数组,所以会查找到数组结束位置,如果为char*,则可能带来越界访问的危害)

  解决办法为:修改send一行如下:

result = send(sockfd, strsend, sizeof(strsend), 0);

       说点个人的看法吧:

       网上的很多参考的东东,在功能实现上,的确是不会出太大问题的,正常情况下,一般不会出bug,不过健壮性欠佳,当然,也是对我们思考的一个好锻炼。不能只是拿来用啊。

     关于linux套接字,更多可以参考http://www.ibm.com/developerworks/cn/linux/l-hisock.html,和http://www.ibm.com/developerworks/cn/linux/l-sockpit/(其中用到了这里面的地址复用)

     就这样吧,have a good day

抱歉!评论已关闭.