TCP通信

一个程序使用套接字需要执行4个步骤。
  –分配套接口和初始化。
  –连接。
  –发送或接收数据。
  –关闭套接字。
涉及到的调用包括socket、 bind、 listen、 connect、 accept、 recv、 send。
 
 
 
分配套接口和初始化。
  –我们需要做的第一件工作就是分配套接口。
  –套接口可以看做是文件描述符。
  –不论是server端,还是client端,第一步都是一样的。
每个套接口都是一个通信的通道
两个进程通过套接口建立连接后就可以发送和接受数据了。
 
 
 

#include <sys/types.h>

#include <sys/socket.h>

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

系统调用socket带有以下参数
  –int domain。
  –int type。
  –int protocol(这个值一般都取0)。
  –成功返回套接字描述符,失败返回-1,并设置errno
 
domain说明

说明

AF_UNIX

UNIX内部使用

AF_INET

TCP/IP协议

AF_ISO

国际标准组织协议

AF_NS

Xerox网络协议

 

type说明

说明

SOCK_STREAM

使用TCP可靠连接

SOCK_DGRAM

使用UDP不可靠连接

 

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

setsockopt函数设置套接口
常见用法为:

int on = 1;

setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))

由于TCP 套接字状态 TIME_WAIT 引起该套接字关闭后约保留 2 到 4 分钟。在此期间bind绑定该端口会失败。
SO_REUSEADDR指示系统地址可重用。
 
 
 

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

bind将进程和一个套接口联系起来,bind通常用于服务器进程为接入客户连接建立一个套接口。
参数sockfd是函数socket调用返回的套接口值。
参数my_addr是结构sockaddr的地址。
参数addrlen设置了my_addr能容纳的最大字节数。
成功返回0,失败返回-1,并设置errno
 
 
 
对于客户端程序,下一步是要与之通信的服务器建立连接。
  –客户端只需使用connect即可
 对于服务端程序,就是要建立自己的套接口等待来自客户端的连接。
  –服务端需要调用listen和accept两个函数。
 
 
 

int listen(int sockfd, int backlog);

创建了套接口并且使用bind将它和一个进程关联起来以后,服务端就需要调用listen来监听指定端口的客户端连接。
参数sockfd是调用socket返回的套接口描述符
参数backlog设置接入队列的大小,通常把这个值设置的最够大就可以了。
成功返回0,失败返回-1,并设置errno。
 
 

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

当有客户端连接到服务端,它们会排入队列,直到服务端准备好处理它们为止,accept会返回一个新的套接口,同时原来的套接口继续listen指定端口号。
参数sockfd是调用socket返回的套接口描述符
参数addr指向结构sockaddr地址。
参数addrlen设置了addr能容纳的最大字节数。
成功返回新的套接字,失败返回-1,并设置errno。
 
 

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

客户端调用connect与服务端进行连接。
参数sockfd是调用socket返回的套接口描述符。
参数addr指向结构sockaddr地址。
参数addrlen设置了addr能容纳的最大字节数。
成功返回0,失败返回-1,并设置errno。
 
 
 
客户端和服务端建立了连接就可以在客户端和服务端之间传输数据了,需要两个系统调用。
  –send---发送数据。  
  –recv---接收数据。
一个套接口既可以发送数据,也可以接收数据。
 
 

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

send函数用来发送数据。
参数s是已经建立连接的套接口。
参数buf是接收数据内存buffer地址指针。
参数len指明buffer的大小,单位字节。
参数flags一般填0。。
成功返回发送的字节数,失败返回-1,并设置errno。
 
 

ssize_t recv(int s, void *buf, size_t len, int flags);

recv函数用来接收数据。
参数s是已经建立连接的套接口。
参数buf是接收数据内存buffer地址指针。
参数len指明buffer的大小,单位字节。
参数flags一般填0。。
成功返回接收到的字节数,失败返回-1,如果对端套接字已经关闭,返回0。
 
 
最后当你用完套接口以后,就该释放套接口所占用的资源了,通过close做到这一点。
  –close。
当试图向一个已经关闭的套接口写或者读数据就会出错。
 
 
 
客户端程序例子。
需要包含头文件

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int main(int arg, char *args[])
{
    int st = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if(connect(st, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        printf("connect failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }

    char s[1024];
    memset(s, 0, sizeof(1024));
    strcpy(s, "hello world");
    if(send(st, s, sizeof(s), 0) == -1)
    {
        printf("send failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    close(st);
    return EXIT_SUCCESS;
}

 

 

服务端程序例子。
需要包含头文件

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

int main(int arg, char *args[])
{
    int st = socket(AF_INET, SOCK_STREAM, 0);
    int on = 1;
    if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsockopt failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
    {
        printf("bind failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    if (listen(st, 20) == -1)
    {
        printf("listen failed %s\n", strerror(errno));
        return EXIT_FAILURE;
    }
    char s[1024];
    int client_st = 0;
    socklen_t len = 0;
    struct sockaddr_in client_addr;
    void *p = &client_addr;
    int i;
    for (i = 0; i < 5; i++)
    {
        memset(&client_addr, 0, sizeof(client_addr));
        socklen_t len = sizeof(client_addr);
        client_st = accept(st, p, &len);
        if (client_st == -1)
        {
            printf("accept failed %s\n", strerror(errno));
            return EXIT_FAILURE;
        }
        memset(s, 0, sizeof(1024));
        if (recv(client_st, s, sizeof(s), 0) == -1)
        {
            printf("recv failed %s\n", strerror(errno));
            close(client_st);
            return EXIT_FAILURE;
        }
        printf("revc is %s\n", s);
        close(client_st);
    }
    close(st);
    return EXIT_SUCCESS;
}

 

 

posted @ 2015-05-12 07:40  张仕传  阅读(134)  评论(0编辑  收藏  举报