Socket编程 - API

基础知识部分:http://www.cnblogs.com/Jimmy1988/p/7839940.html

1. 基本流程

mark

Process Client Server Comment
socket() 创建socket文件描述符 同← 通信双方都需建立
socket文件描述符是应用层通信接口
bind() 可选
一般不绑定,由内核自动分配
必选
IP地址不能为公网地址
远程:一般设置为"0.0.0.0"或 INADDR_ANY
局域网:可设置为内网地址
详细可见:
https://www.cnblogs.com/ok-lanyan/articles/2634242.html
Listen() 可选,一般不监听 必选
使socket处于监听网络状态
阻塞等待
connect() 向服务端发起链接请求 包含目标主机的IP和Port
accept() 服务端接受链接请求
接受链接后,使用一个新的socket描述符,
使服务端可接受多个链接
可以提取客户端的IP和Port
Send/Recv() 进行数据包的发送和接受 同← socket同样可以认为是文件,
可以直接read/write()
close() 关闭socket链接 同← 可以分开关闭读/写操作

2. API

1). Socket()

#include <sys/socket.h>

int socket(int domain, int type, int protocol)

/* Return Value: 
 * If successed: A file descriptor for new socket
 * else : -1
 */

①. domain:

用于选择本次通信的协议簇(地址簇);
其详细定义见 /usr/include/bits/socket.h, 常用的宏如下:

Name Purpose Man page
AF_UNIX,
AF_LOCAL
Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_IPX IPX - Novell protocols
AF_NETLINK Kernel user interface device netlink(7)

②. type

协议类型
详细信息见/usr/include/bits/socket_types.h,常用的如下:

Type Purpose
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams.
An out-of-band data transmission mechanism may be supported.
SOCK_DGRAM Supports datagrams (connectionless,
unreliable messages of a fixed maximum length).

③. protocol

一般由系统自动选择,通常设置为0

2). bind()

当使用socket()创建一个socket后,存在着一个name space,但是没有内存地址与其关联;
本函数的目的就是为了分配内存地址给socket

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

/* Return Value:
 * If successsed: 0 
 * else: -1
 */

①. socktfd

socket()函数返回的socket文件描述符

②. addr

分配给socket的地址空间首地址;
根据协议的不同,结构体定义不同,可以使用man 7 ip/ipv6/etc..查看;
一般的定义形式如下:

/* Structure describing a generic socket address.  */
struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];       /* Address data.  */
  };

/*IF flags = AF_INET, 即IPv4*/
struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */
               in_port_t      sin_port;   /* port in network byte order */
               struct in_addr sin_addr;   /* internet address */
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };

③. addrlen

地址长度,一般使用sizeof(xx)

3). Listen()

#include <sys/socket.h>

int listen(int sockfd, int backlog)

/*
 * backlog: 请求排队的最大长度
 */
 
/* Return Value
 * If successed: 0
 * else: -1
 */

4). connect()

#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

/* Return Value
 * If successed: 0
 * else: -1
 */

5). accept()

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/* Return Value
 * If successed: 有效的接收到的socket描述符
 * else: -1
 */

6). read/write()

读写时,可以把socket看成普通文件,因此读写的方式与普通文件相同:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

7). send/recv()

针对面向连接的socket的读写操作

#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

/* Arguments: 
 * 1. buf: 数据存放位置
 * 2. len: 数据发送/接收大小
 * 3. flags: 见下面
 */
 
/* Return Value
 * If successed: 发送/接收的数据量
 * else: -1
 */ 

**flags: **

enum
  {
    MSG_OOB     = 0x01, /* Process out-of-band data.  */
#define MSG_OOB     MSG_OOB
    MSG_PEEK        = 0x02, /* Peek at incoming messages.  */
#define MSG_PEEK    MSG_PEEK
    MSG_DONTROUTE   = 0x04, /* Don't use local routing.  */
#define MSG_DONTROUTE   MSG_DONTROUTE
#ifdef __USE_GNU
    /* DECnet uses a different name.  */
    MSG_TRYHARD     = MSG_DONTROUTE,
# define MSG_TRYHARD    MSG_DONTROUTE
#endif
    MSG_CTRUNC      = 0x08, /* Control data lost before delivery.  */
#define MSG_CTRUNC  MSG_CTRUNC
    MSG_PROXY       = 0x10, /* Supply or ask second address.  */
#define MSG_PROXY   MSG_PROXY
    MSG_TRUNC       = 0x20,
#define MSG_TRUNC   MSG_TRUNC
    MSG_DONTWAIT    = 0x40, /* Nonblocking IO.  */
#define MSG_DONTWAIT    MSG_DONTWAIT
    MSG_EOR     = 0x80, /* End of record.  */
#define MSG_EOR     MSG_EOR
    MSG_WAITALL     = 0x100, /* Wait for a full request.  */
#define MSG_WAITALL MSG_WAITALL
    MSG_FIN     = 0x200,
#define MSG_FIN     MSG_FIN
    MSG_SYN     = 0x400,
#define MSG_SYN     MSG_SYN
    MSG_CONFIRM     = 0x800, /* Confirm path validity.  */
#define MSG_CONFIRM MSG_CONFIRM
    MSG_RST     = 0x1000,
#define MSG_RST     MSG_RST
    MSG_ERRQUEUE    = 0x2000, /* Fetch message from error queue.  */
#define MSG_ERRQUEUE    MSG_ERRQUEUE
    MSG_NOSIGNAL    = 0x4000, /* Do not generate SIGPIPE.  */
#define MSG_NOSIGNAL    MSG_NOSIGNAL
    MSG_MORE        = 0x8000,  /* Sender will send more.  */
#define MSG_MORE    MSG_MORE
    MSG_WAITFORONE  = 0x10000, /* Wait for at least one packet to return.*/
#define MSG_WAITFORONE  MSG_WAITFORONE

    MSG_CMSG_CLOEXEC    = 0x40000000    /* Set close_on_exit for file
                       descriptor received through
                       SCM_RIGHTS.  */
#define MSG_CMSG_CLOEXEC MSG_CMSG_CLOEXEC
  };

8). close/shutdown()

两种方式关闭socket

I. close()

完全关闭socket通道,包括读和写

#include <unistd.h>

int close(int fd);

II. shutdown()

具备更强的灵活性,可选择性关闭读或写

 #include <sys/socket.h>

int shutdown(int sockfd, int how);

how:

  • how=0: 只关闭读通道;
  • how=1: 只关闭写通道
  • how=2: 关闭读写通道

9). getsockname/getpeername()

I. 获取socket的本地地址

要求:已完成绑定本地IP

#include <sys/socket.h>

int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/* Return Value
 * If successed: 0
 * else: -1
 */ 

II. 获取socket远程信息

要求:该socket已经完成远程连接

#include <sys/socket.h>

int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/* Return Value
 * If successed: 0
 * else: -1
 */ 

10). 获取本机IP地址等信息

#include <ifaddrs.h>

int getifaddrs(struct ifaddrs **ifap);
void freeifaddrs(struct ifaddrs *ifa)

/*Return value:
 * If successed:0 
 * else: -1
 */
 
 struct ifaddrs {
    struct ifaddrs  *ifa_next;    /* Next item in list */
    char            *ifa_name;    /* Name of interface */
    unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */
    struct sockaddr *ifa_addr;    /* Address of interface */
    struct sockaddr *ifa_netmask; /* Netmask of interface */
    union {
           struct sockaddr *ifu_broadaddr; /* Broadcast address of interface */
            struct sockaddr *ifu_dstaddr;
            /* Point-to-point destination address */
            } ifa_ifu;
    #define              ifa_broadaddr ifa_ifu.ifu_broadaddr
    #define              ifa_dstaddr   ifa_ifu.ifu_dstaddr
    void            *ifa_data;    /* Address-specific data */
    };

3. 示例代码

本次实现client/server两端的实时聊天功能,彼此不影响,可随时发送/接收对方数据;
当输入"quit”后,结束本次链接。

1. Client.c

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

#define SIZE 1024
#define PORT 12345
//#define IP_ADDR "192.168.139.129"

#define IP_ADDR "139.196.121.132"
//#define IP_ADDR "47.89.246.154"

int sockfd;

void *snd(void)
{
    int len = 0;
    char buf[SIZE];
    while(1)
    {
        memset(buf,'\0', sizeof(buf));
        printf("\nInput msg send to server:  ");
        fgets(buf, SIZE, stdin);
        if(buf[0] != '\0')
        {
            len = send(sockfd, buf, sizeof(buf), 0);
            if(len < 0)
            {
                printf("SND: Some Error occured or disconnection!\n");
                break;
            }                   

            if(!strncasecmp(buf, "quit",4))
            {
                printf("SND: sub thread has quit!\n");
                break;
            }
            printf("\nSend msg: %s \n", buf);
        }
    }
    
    pthread_exit(NULL);
}

void *rcv(void)
{
    int len=0;
    char buf[SIZE];
        
    while(1)
    {
        memset(buf,'\0', sizeof(buf));

        len = recv(sockfd, buf, SIZE, 0);

        if(len < 0)
        {
            printf("RCV: Some error occured!\n");
            break;
        }
        
        if(!strncasecmp(buf, "quit",4))
        {
            printf("RCV: sub thread has quit!\n");
            break;
        }

        printf("\nThe received msg:%s\n", buf);
    }

    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    int ret;

    //1. create socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    //2. bind the ip addr
    struct sockaddr_in dest_addr;
    memset(&dest_addr, 0, sizeof(struct sockaddr_in));
    
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(PORT);
    dest_addr.sin_addr.s_addr = inet_addr(IP_ADDR);

/*
    ret = bind(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in));
    if(-1 == ret)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
 */   

    //3. request the connection    
    ret = connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if(-1 == ret)
    {
        perror("connect");
        exit(EXIT_FAILURE);
    }
    printf("Connect the server(IP:%s) successed!\n", IP_ADDR);


    //5. send/receive the message
    pthread_t tid1, tid2;

    if( 0 != pthread_create(&tid1, NULL, (void *)*snd, NULL))
    {
        perror("pthread_create");
        exit(errno);
    }

    if( 0 != pthread_create(&tid2, NULL, (void *)*rcv, NULL))
    {
        perror("pthread_create");
        exit(errno);
    }

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    return 0;
}

2. Server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>


#define SIZE 1024
#define PORT 12345
//#define IP_ADDR "192.168.139.129"
#define IP_ADDR "139.196.121.132"

int main(int argc, char *argv[])
{
    int ret;
    pthread_t tid;
    int sockfd;

    //1. create socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    //2. bind the ip addr
    struct sockaddr_in local_addr;
    memset(&local_addr, 0, sizeof(struct sockaddr_in));
    
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(PORT);
    local_addr.sin_addr.s_addr = inet_addr(IP_ADDR);

    ret = bind(sockfd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr_in));
    if(-1 == ret)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    
    //3. listen the connection    
    unsigned int lisnum = 5;
    ret = listen(sockfd, lisnum);
    if(-1 == ret)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    printf("Wait for client connection!\n");

    //4. accept the connection
    int new_fd;
    socklen_t len;
    struct sockaddr_in client_addr;
    
    new_fd = accept(sockfd, (struct sockaddr *)&client_addr, &len);
    if(-1 == new_fd)
    {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    
    else
    {
//        printf("Server got connection: \n\tAddress:%s\tPort:%d\tSocket=%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), new_fd);
    }

    //5. send/receive the message
    char buf[SIZE];
    ssize_t length;    
    pid_t pid;
    pid = fork();
    if(pid == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }    
    
    if(pid == 0)
    {
        while(1)
        {
            memset(buf, '\0', SIZE);
            printf("Pls input the msg  to send: ");
            fgets(buf, SIZE, stdin);
            
            if((length=strlen(buf)) < 0)
            {
                printf("send error, the length=%d\n", length);
                break;
            }

            length = send(new_fd, buf, sizeof(buf), 0);            

            if(!strncasecmp(buf, "quit", 4))
            {
                printf("Snd: close the connection!\n");
                break;
            }
        }
    }

    else
    {
        while(1)
        {
            memset(buf, '\0', SIZE);
            length = recv(new_fd, buf, SIZE, 0);        

            if(length >= 0)
            {
                printf("\nReceive msg:  %s\n", buf);
            }

            else 
            {
                printf("Error occured\n");
                break;
            }

            if(!strncasecmp(buf, "quit", 4))
            {
                printf("Recv: close the connection!\n");
                break;
            }
        }
        
    }
    
    //Close the socket 
    close(sockfd);
    close(new_fd);
    
    return 0;
}

posted @ 2017-11-25 14:28  Jimmy_Nie  阅读(807)  评论(0编辑  收藏  举报