使用套接字实现简单TCP服务器客户端模型

来源:互联网 发布:剑网3成男捏脸数据图表 编辑:程序博客网 时间:2024/06/06 05:11
利用套接字实现一个简单的TCP服务器客户端模型基本步骤如下:

1.创建套接字
#include <sys/types.h>
#include <sys/socket.h>
 int socket(int domain, int type, int protocol);
参数描述:
     domian:协议域,AF_INET 对应 ipv4,AF_INET6  对应  ipv6, AF_UNIX 表示这个socket是非网络形式的unix域,可以用来进行非网络形式的进程间通信。
     type:指定socket类型
     1.SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。  这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
     2.SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
     3.SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
返回值:
     成功返回socket描述符,失败返回-1

2.将服务器IP与端口和套接字进行绑定
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
参数描述:
     sockfd:通过socket()函数创建,用来表示唯一一个socket
     addr:指向要绑定给sockfd的协议地址(协议类型,IP,端口号)
     addrlen:对应地址长度
返回值:
     成功返回0,失败返回-1
注意:
在IPv4因特网域AF_INET中,套接字地址用结构sockaddr_in表示,如下:
struct sockaddr_in
{
     sa_family_t sin_family;     //unsigned short 地址族
     in_port_t sin_sport;         //uint16_t
     struct in_addr sin_addr;  //IPv4
};
struct in_addr
{
     in_addr_t s_addr;            //uint32_t
};

3.监听请求
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
参数描述:
     sockfd:要监听的套接字
     backlog:连接队列长度
     backlog 具体一些是什么意思呢?每一个连入请求都要进入一个连入请求队列,等待listen 的程序调用accept()函数来接受这个连接。当系统还没有调用accept()函数的时候,如果有很多连接,那么本地能够等待的最大数目就是backlog 的数值。
返回值:
     成功返回0,失败返回-1

4.服务器端接受监听到的请求
当服务器端进行监听到一个连接请求时,就可以使用accept()函数来接受这个请求。
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数描述:
     socket:服务器的套接字
     addr:用于存储客户端的协议地址(协议类型,IP,端口号)
     addrlen:地址长度(sizeof(addr))
返回值:
     成功返回一个新的套接字描述符,代表与返回客户端的TCP连接

5.客户端连接服务器
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
参数描述:
     sockfd:客户端的sockfd
     addr:用于存储服务器的协议地址(协议类型,IP,端口号)
     addrlen:地址长度(sizeof(addr))
返回值:
     成功返回0,失败返回-1

注意:
在实现过程中还应注意网络字节序的问题,主机的字节序是不确定的,有的使用大端存储,有的使用小端存储,为了不使网络通信因为机器之间存储模式而造成错误,TCP/IP协议规定网络数据流使用大端字节序。
下面库函数可以完成主机序列和为网络序列的转换:
#include <arpa/inet.h>
//主机序列到网络序列
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
//网络序列到主机序列
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

server:#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<error.h>#include<unistd.h>#include<sys/types.h>static void Usage(char* proc){    printf("Usage: %s [local_ip] [local_port]\n", proc);}int startup(char* ip, int port){    //1.创建文件特性套接字    //AF_INET ipv4,SOCK_STREAM,基于字节流服务    int sock = socket(AF_INET, SOCK_STREAM, 0);    if(sock < 0)    {    perror("socket");    return 2;    }    printf("sock =  %d\n", sock);    struct sockaddr_in local;    //确定地址协议类型    local.sin_family = AF_INET;    //绑定端口    local.sin_port = htons(port);    //绑定ip    //这里的IP是点分十进制,使用inet_addr()可以将其转换成二进制    local.sin_addr.s_addr = inet_addr(ip);    //2.绑定网络特性将套接字与服务器ip和端口号绑定    if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)    {    perror("bind");    return 3;    }    //3.监听请求    if(listen(sock, 10) < 0)    {    perror("listen");    return 4;    }    return sock;}int main(int argc, char* argv[]){    if(argc != 3)    {    Usage(argv[0]);    return 1;    }    //获取套接字    int listen_sock = startup(argv[1], atoi(argv[2]));    while(1)    {    //用于存储连接的客户端地址信息(地址类型,IP,端口号)    struct sockaddr_in client;    socklen_t len = sizeof(client);    //4.接受监听到的连接    int new_sock = accept(listen_sock,(struct sockaddr*)&client, &len);    if(new_sock < 0)    {        perror("accept");        return 5;    }    while(1)    {        char buf[1024];        //从套接字读取信息到buf中        ssize_t s = read(new_sock, buf, sizeof(buf) - 1);        if(s > 0)        {        buf[s] = 0;        printf("client#  %s\n", buf);        printf("Please Enter# ");        fflush(stdout);        //从键盘输入信息到buf中        ssize_t _s = read(0, buf, sizeof(buf)-1);        buf[_s-1] = 0;        //发送信息到套接字            write(new_sock, buf, strlen(buf));           }        else if(s == 0)        {        printf("client colse !!\n");        return 6;        }        else        {        perror("read");        return 7;        }    }    }    return 0;}
client:#include<stdio.h>#include<string.h>#include<sys/socket.h>#include<netinet/in.h>#include<unistd.h>#include<sys/types.h>static void Usage(char *proc){    printf("Usage %s [server_ip] [server_port]\n", proc);}int main(int argc, char* argv[]){    if(argc != 3)    {    Usage(argv[0]);    return 1;    }    //创建套接字    int sock = socket(AF_INET, SOCK_STREAM,0);    if(sock < 0)    {    perror("socket");    return 2;    }    struct sockaddr_in server;    server.sin_family = AF_INET;    server.sin_port = htons(atoi(argv[2]));    server.sin_addr.s_addr = inet_addr(argv[1]);    //将创建的套接字,连入指定的网络服务中,这里连到了服务器    if(connect(sock,(struct sockaddr*)&server, sizeof(server)) < 0)    {    perror("connect111");    return 3;    }    char buf[1024];    while(1)    {    printf("Please Enter# ");    fflush(stdout);    //从键盘写入内容到缓冲区    ssize_t s = read(0, buf, sizeof(buf)-1);    if(s >0)    {        buf[s-1] = 0;        printf("server# ");        fflush(stdout);        //将缓冲区内容通过套接字发送到服务器        write(sock, buf, strlen(buf));        //在从套接字中读取服务器的回应信息        ssize_t _s = read(sock, buf, sizeof(buf)-1);        if(_s > 0)        {        buf[_s] = 0;        printf("%s\n", buf);        }        if(s < 0)        {        perror("read");        return 4;        }    }    }    return 0;}

原创粉丝点击