Linux_套接字编程

转载自:http://blog.sina.com.cn/s/blog_51335a0001018ue9.html
面向无连接的套接字通信流程:
服务器:socket(), bind(), recvfrom(), sendto();
客户端:socket(), bind(), sendto(), recvfrom();

面向连接的套接字通信流程:
服务器:socket(), bind(), listen(), accept(), read(), write();
客户端:socket(), connect(), write(), read();

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
作用:创建套接字。
参数:domain表示通信使用的协议族,有如下值:
AF_UNIX, AF_LOCAL Local communication
AF_INET IPv4 Internet protocols
AF_INET6 IPv6 Internet protocols
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device
AF_X25 ITU-T X.25 / ISO-8208 protocol
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_APPLETALK Appletalk
AF_PACKET Low level packet interface
type表示套接字类型,有如下值:
SOCK_STREAM
SOCK_DGRAM
SOCK_SEQPACKET
SOCK_RAW
SOCK_RDM
SOCK_PACKET
SOCK_NONBLOCK
SOCK_CLOEXEC
protocol表示套接字通信时使用的一个特定协议,通常设置为0。
返回值:文件描述符表示一个新创建的套接字。-1表示失败。errno将被系统设置标明具体的错误。

库函数:
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
作用,参数,返回值同系统函数的 int socket(...);

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
作用:设置某个套接字选项的值。
参数:sockfd表示需要设置哪个套接字选项的值。level表示该选项在哪个协议层,通常设置为SOL_SOCKET。optname表示 需要设置哪个选项,有如下值:
SO_DEBUG 
SO_BROADCAST 
SO_REUSEADDR 
SO_KEEPALIVE 
SO_LINGER 
SO_OOBINLINE 
SO_SNDBUF 
SO_RCVBUF 
SO_DONTROUTE 
SO_RCVLOWAT 
SO_RCVTIMEO 
SO_SNDLOWAT 
SO_SNDTIMEO
optval表示设置的值由该指针指向。optlen表示设置的值的长度。
返回值:0表示设置成功。-1表示失败。 errno将被系统设置标明具体的错误。

库函数:
#include <sys/socket.h>
int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
作用,参数,返回值同系统函数的int  setsockopt(...);

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
作用:获取某个套接字选项的值。
参数:同系统函数的int setsockopt();
返回值: 0表示获取成功。-1表示失败。 errno将被系统设置标明具体的错误。

库函数:
#include <sys/socket.h>
int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len);
作用,参数,返回值同系统函数的int get sockopt(...);但有更多的选项可以获取,有如下值:
SO_ACCEPTCONN 
SO_ERROR 
SO_TYPE 

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
作用:发送消息到另一个套接字。
参数:sockfd表示源套接字的fd。buf表示要发送的消息。len表示消息的长度。flags可以使用或操作设置为如下值:
MSG_CONFIRM (Since Linux 2.3.15)
MSG_DONTROUTE
MSG_DONTWAIT (since Linux 2.2)
MSG_EOR (since Linux 2.2)
MSG_MORE (Since Linux 2.4.4)
MSG_NOSIGNAL (since Linux 2.2)
MSG_OOB
dest_addr和addrlen的设置情况如下:
If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments dest_addr and addrlen are ignored (and the error EISCONN may be returned when they are not NULL and 0), and the error ENOTCONN is returned when the socket was not actually connected. Otherwise, the address of the target is given by dest_addr with addrlen specifying its size.
返回值:实际发送的字符个数。-1表示发送失败。 errno将被系统设置标明具体的错误。
额外说明:也可以使用ssize_t write(int fd, const void *buf, size_t count);将数据写入到套接字中,相当于sendto()

库函数:
#include <sys/socket.h>
ssize_t send(int socket, const void *buffer, size_t length, int flags);
ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
作用,参数,返回值同系统函数的ssize_t sendto(...);但参数flags仅可以设置为如下值:
MSG_EOR
MSG_OOB

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
作用:从一个套接字接收消息。
参数:sockfd表示接收套接字的fd。buf表示将接收到的数据放在哪(通常是数组)。len表示buf的长度。flags的值如下:
MSG_CMSG_CLOEXEC (recvmsg() only; since Linux 2.6.23)
MSG_DONTWAIT (since Linux 2.2)
MSG_ERRQUEUE (since Linux 2.2)
MSG_OOB
MSG_PEEK
MSG_TRUNC (since Linux 2.2)
MSG_WAITALL (since Linux 2.2)
MSG_EOR
MSG_TRUNC
MSG_CTRUNC
MSG_OOB
MSG_ERRQUEUE
flagssrc_addr和addrlen的设置如下:
If  src_addr  is not NULL, and the underlying protocol provides the source address, this source address is filled in. When  src_addr is NULL, nothing is filled in; in this case,  addrlen  is not used, and should also be NULL. The argument  addrlen  is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with  src_addr , and modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case,  addrlen  will return a value greater than was supplied to the call.
返回值:实际接收到的字节数。-1表示失败。 If no messages are available to be received and the peer has performed an orderly shutdown, recvfrom() shall return 0. errno将被系统设置标明具体的错误。
额外说明:也可以使用ssize_t read(int fd, void *buf, size_t count);将从套接字中读取数据,相当于recvfrom()

库函数:
#include <sys/socket.h>
ssize_t recv(int socket, void *buffer, size_t length, int flags);
ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
作用,参数,返回值同系统函数的ssize_t recvfrom(...); 但参数flags仅可以设置为如下值:
MSG_PEEK
MSG_OOB
MSG_WAITALL

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:将套接字与IP地址/端口号绑定。
参数:sockfd表示需要绑定的套接字。addr表示指向sockaddr结构体的指针。addrlen表示sockaddr的长度。
struct sockaddr {
      sa_family_t sa_family;
      char            sa_data[14];
}
返回值:0表示绑定成功。-1表示失败。 errno将被系统设置标明具体的错误。
额外说明: http://linux.die.net/man/2/bind  中例子。

库函数:
#include <sys/socket.h>
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
作用,参数,返回值同系统函数的int bind(...);

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
作用:设置套接字接口的监听状态。
参数:sockfd表示需要设置监听状态的套接字。backlog表示能够处理的最大的客户端连接数。
返回值:0表示监听状态设置成功。-1表示失败。 errno将被系统设置标明具体的错误。

库函数:
#include <sys/socket.h>
int listen(int socket, int backlog);
作用,参数,返回值同系统函数的int listen(...);

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
作用:接受客户端的连接请求。
参数:sockfd表示监听客户端连接请求的套接字。 addr表示指向sockaddr结构体的指针。addrlen表示sockaddr的长度。
struct sockaddr {
      sa_family_t sa_family;
      char            sa_data[14];
}
返回值:一个非负整数表示用于数据传输的文件描述符。-1表示失败。 errno将被系统设置标明具体的错误。
额外说明:函数执行到accept()函数时,会hang住,等待客户端的连接,这时可以通过开启一个新的终端,输入netstat -an | grep <port_number>来查看服务器的状态。之后输入telnet <ip_address> <port_number> (相当于一个连接请求)来查看服务器的accept()函数的执行效果。之后输入test send content aaaaaaa (相当于发送一个字符串)来查看服务器的read()或recvfrom()等函数的执行效果。

库函数:
#include <sys/socket.h>
int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
作用,参数,返回值同系统函数的int accept(...);
额外说明: The  accept () function shall extract the first connection on the queue of pending connections, create a new socket with the same socket type protocol and address family as the specified socket, and allocate a new file descriptor for that socket.

系统函数:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:在客户端的套接字上发送连接请求。
参数:sockfd表示发送连接请求的客户端套接字。 addr表示指向sockaddr结构体的指针,sockaddr中存储着服务器的IP地址和端口信息。addrlen表示sockaddr的长度。
struct sockaddr {
      sa_family_t sa_family;
      char            sa_data[14];
}
返回值: 0表示连接成功。-1表示失败。 errno将被系统设置标明具体的错误。

库函数;
#include <sys/socket.h>
int connect(int socket, const struct sockaddr *address, socklen_t address_len);
作用,参数,返回值同系统函数的int connect(...);

客户端代码:
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

const char *const socket_name = “my_socket”;
const char *const cmd_edload_test = "edload";
int socket_fd;
struct sockaddr_un serv_addr;
int pass = 0;
int fail = 0;

int open_socket()
{
    socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (socket_fd < 0) {
        LOGE("Failed to create socket: %s", strerror(errno));
        return RET_FAILED;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sun_family = AF_LOCAL;
    //snprintf(serv_addr.sun_path, UNIX_PATH_MAX, "%s", socket_name);
    snprintf(serv_addr.sun_path, 108, "%s", socket_name);
    serv_addr.sun_path[0] = 0;

    return RET_SUCCESS;
}

int connect_socket()
{
    if (connect(socket_fd, (struct sockaddr*) &serv_addr,
                offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) != 0) {
        LOGE("Failed to connect to socket: %s", strerror(errno));
        close(socket_fd);
        return RET_FAILED;
    }
    return RET_SUCCESS;
}

int write_data()
{
    int bytes_written;
    bytes_written = write(socket_fd, cmd_edload_test, strlen(cmd_edload_test));
    if (bytes_written < 0) {
        LOGE("Failed to write data to socket :%s:", strerror(errno));
        close (socket_fd);
        return RET_FAILED;
    }
    return RET_SUCCESS;
}

int read_data()
{
    int status;
    int bytes_read;
    int offset = 0;
    do {
        bytes_read = read(socket_fd, &status, sizeof(status) - offset);
        if (bytes_read < 0 && errno != EAGAIN) {
            LOGE("Failed to read data from socket: %s", strerror(errno));
            return RET_FAILED;
        }
        offset += bytes_read;
    } while (bytes_read > 0 && offset < sizeof(int));
    LOGI("Return status is %d", status);

    if (status == RET_SUCCESS) {
        pass ++;
        return RET_SUCCESS;
    }
    LOGE("Failed to read expected status");
    fail ++;
    return RET_FAILED;
}

int close_socket()
{
    if (close(socket_fd) != 0) {
        LOGE("Failed to close socket: %s", strerror(errno));
        return RET_FAILED;
    }
    return RET_SUCCESS;
}

void record_result()
{
    printf("========= RESULT RECORD START =========\n");
    printf("Total: M\n", pass + fail);
    printf("Pass:  M\n", pass);
    printf("Fail:  M\n", fail);
    printf("========= RESULT RECORD FINISH =========\n");
}

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


    printf("will open socket...");
    if (open_socket() == RET_FAILED) {
        LOGE("Open socket failed");
        return -1;
    }

    printf("will connect socket...");
    if (connect_socket() == RET_FAILED) {
        LOGE("Connect socket failed");
        return -1;
    }

    //do {
        //run_times --;
        printf("will write data...");
        if (write_data() == RET_FAILED) {
            LOGE("Write data to socket failed");
            return -1;
        }
        sleep(3);
        printf("will read data...");
        if( read_data() == RET_FAILED) {
            LOGE("Read data from socket failed");
            return -1;
        }
    //} while (run_times > 0);

    printf("will close socket...");
    if (close_socket() == RET_FAILED) {
        LOGE("Close socket failed");
        return -1;
    }

    record_result();
    return 0;
}

服务器端代码:
static void* CommandThread(void *arg)
{
    char buffer[256 + 1];
    int socket_fd;
    int client_fd;
    int rcode;
    int bytes_read;
    int offset;
    char *command = NULL;
    struct stat socket_stat;
    const char * const socket_name = "my_socket";
    struct sockaddr_un serv_addr;
    struct ***_helper_drv *drv = (struct ***_helper_drv*)arg;

    socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if (socket_fd < 0) {
        LOGE("Server failed to create socket");
        goto error;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sun_family = AF_LOCAL;
    snprintf(serv_addr.sun_path, UNIX_PATH_MAX, "%s", socket_name);
    if (!stat(***_HELPER_SOCKET, &socket_stat)) {
        unlink(***_HELPER_SOCKET);
    }
    if (bind(socket_fd, (struct sockaddr*) &serv_addr, UNIX_PATH_MAX) < 0) {
        LOGE("Failed to bind to socket: %s",strerror(errno));
        goto error;
    }
    if (chmod(***_HELPER_SOCKET, S_IRUSR | S_IWUSR) == -1) {
        LOGE("Failed to set socket permissions: %s",strerror(errno));
        goto error;
    }
    if (listen(socket_fd, 1) < 0) {
        LOGE("Listen failed : %s", strerror(errno));
        goto error;
    }
    LOGI("Upgrade thread created");
    do {
        LOGI("Waiting for command");
        client_fd = accept(socket_fd, NULL,0);
        if (client_fd < 0) {
            LOGE("Failed to accept connection: %s",strerror(errno));
            goto cleanup;
        }
        offset = 0;
        do {
            bytes_read = read(client_fd, buffer + offset,
                    ***_HELPER_CMD_BUF_SIZE - offset);
            if (bytes_read < 0) {
                LOGE("Failed to read from socket: %s",
                        strerror(errno));
                goto cleanup;
            }
            offset += bytes_read;
        } while (bytes_read > 0 && offset < ***_HELPER_CMD_BUF_SIZE);
        LOGI("Command is :%s:",buffer);
        if (strncmp(buffer,
                    ***_EDLOAD_COMMAND_UPGRADE_START,
                    ***_HELPER_COMMAND_LENGTH) == 0) {
            LOGI("image_upgrade command received, client_fd=%d", client_fd);

            rcode = ***_edload(drv, 1);

            write(client_fd, &rcode, sizeof(rcode));
            close(client_fd);
            goto cleanup;
        }
        else if (strncmp(buffer,
                    ***_EDLOAD_COMMAND_TEST,
                    ***_HELPER_COMMAND_LENGTH) == 0)
        {
            LOGI("Edload test start command recieved");
            rcode = 1;
            rcode = ioctl(drv->device_descriptor, START_EDLOAD, &rcode);

            if (rcode == RET_FAILED) {
                write(client_fd, &rcode, sizeof(rcode));
                close (client_fd);
                LOGE("failed to reboot ***");
                goto cleanup;
            }

            LOGI("START_EDLOAD complete");
            rcode = get_edload_status(***_EDLOAD);

            if (1 != rcode)
            {
                LOGE("failed to ping %d", rcode);
                rcode = EDLOAD_RET_UART_PING_FAILED;
                write(client_fd, &rcode, sizeof(rcode));
                close (client_fd);
                goto cleanup;
            }
            else
            {
                rcode = 0;
            }
            rcode = write(client_fd, &rcode, sizeof(rcode));

            if (rcode != sizeof(rcode)) {
                LOGE("rcode fail: %d", rcode);
                LOGE("Failed to write to socket: %s",
                strerror(errno));
                close(client_fd);
                goto cleanup;
            }
            close(client_fd);
        }

        else
        {
            LOGE("Unrecognised command %s",command);
            rcode = RET_FAILED;
            write(client_fd, &rcode, sizeof(rcode));
            close(client_fd);
            goto cleanup;
  

你可能感兴趣的:(Linux_套接字编程)