TCP服务器和客户端的实现

TCP服务器的实现过程

1. 创建套接字:socket函数

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

domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。

protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

功能:创建一个套接字文件,然后以文件形式来操作通信

2. 绑定IP地址以及端口号:bind()

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

参数:
scokfd:socket函数创建的套接字
const struct sockaddr *addr:保存IP和端口号的结构体
socklen_t addrlen:结构体的长度

功能:指定通信协议的套接字文件与IP以及端口绑定起来

返回值:成功返回0,失败返回-1

struct socketaddr_in
{
sa_family sin_family;//设置IPV4 IPV6
_be16  sin_port;//设置端口号
struct in_addr sin_addr;//设置IP
};
unit32_t htonl(unit32_t hostlong);//字节序转化的函数
unit16_t htons(unit16_t hostshort);
h代表host,n表示network,s代表16位短整型

3. 将套接字文件描述符,从主动变为被动(做监听准备)listen()

int listen(int sockfd, int backlog);

int sockfd:套接字文件描述
int backlog:指定队列的容量(小于30即可,一般是2,3)

功能:将套接字文件描述符,从主动变为被动(做监听准备)

返回值:成功返回0,失败返回-1

4. 等待客户端响应,accept()

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

功能:被动监听客户端发起的三次握手请求,成功,即建立连接

返回值:成功返回一个通信描述符,专门用于与该连接成功的客户端通信,总之后续服务器与客户端的正式通信,使用的是accept

举例实现服务器:

#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"unistd.h"

#include"sys/types.h"
#include"sys/socket.h"
#include"arpa/inet.h"

#define PORT 33333

int main()
{
    int sockfd;
    int cfd;

    int c_len;
    char buffer[1024];

    struct sockaddr_in addr;
    struct sockaddr_in c_addr;

    sockfd = socket(AF_INET, SOCK_STREAM,0);

    if(-1 == sockfd)
    {
	perror("socket create error!");
	exit(1);
    }

    printf("socket success\n");

    int opt = 1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    
    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family =AF_INET;
    addr.sin_port =htons(PORT);
    addr.sin_addr.s_addr = inet_addr("192.168.12.13");

    if(bind(sockfd,(struct sockaddr *)(&addr),sizeof(struct sockaddr_in)) <0)
    {
	perror("bind error!");
	exit(1);
    }

    printf("bind success\n");

    if(listen(sockfd,3)<0)
    {
	perror("listen error!");
        exit(1);
    }

    printf("listen success\n");
    while(1)
    {
    memset(buffer,0,sizeof(buffer));

    bzero(&c_addr,sizeof(struct sockaddr_in));
    c_len = sizeof(struct sockaddr_in);
    printf("accept.......\n");

    cfd = accept (sockfd,(struct sockaddr *)(&c_addr),&c_len);
    if(cfd<0)
    {
	perror("accept error!");
	exit(1);
    }
    
    printf("port =%d  ip =%s\n",ntohs(c_addr.sin_port),inet_ntoa(c_addr.sin_addr));
   
    //read(cfd,buffer,sizeof(buffer));	
    recv(cfd,buffer,sizeof(buffer),0);
    printf("recv = %s\n",buffer);

    usleep(2);
    
    //write(cfd,buffer,strlen(buffer));
    send(cfd,buffer,sizeof(buffer),0);

    close(cfd);
    }

    return 0;
}

TCP客户端的实现过程

1. 用socket创建套接字文件,指定使用TCP协议

2. 调用connect主动向服务器发起三次握手,进行连接

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

功能:向服务器主动发起连接

返回值:成功返回0,失败返回-1

3. 调用read(recv)和write(send)收发数据

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:向对方发送消息

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:接收对象发送的消息

sockfd:用于通信描述符
*buf:应用缓存
len:长度
flags:0

4. 调用close或者shutdown关闭连接

int shoutdown(int sockfd,int how)

sockfd:返回的文件描述符
how:如何断开连接

how:
SHUT_RD:只断开读连接
SHUT_WR:只断开写连接
SHUT_RDWR:读写连接都断开

功能:可以按照要求关闭连接,而且不管有多少个描述符指向同一连接,只要调用shutdown去操作了其中某个操作符,连接就会立即断开

返回值:成功返回0,失败返回-1

  • 无法绑定的解决:
int opt =1;
setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt) );

举例实现客户端:

#include"stdio.h"
#include"stdlib.h"
#include"string.h"
#include"unistd.h"

#include"sys/types.h"
#include"sys/socket.h"
#include"arpa/inet.h"

#define PORT 33333

int main()
{
    int sockfd;

    struct sockaddr_in s_addr;

    if((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
    {
	perror("socket error");
	exit(1);
    }

    printf("client socket success!\n");

    bzero(&s_addr,sizeof(struct sockaddr_in));
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(PORT);
    s_addr.sin_addr.s_addr=inet_addr("192.168.12.13");

    if(connect(sockfd,(struct sockaddr *)(&s_addr),sizeof(struct sockaddr_in))<0)
    {
	perror("connect error!");
	exit(1);
    }

    printf("connect success!\n");

    write(sockfd,"hello world",12);

    char buffer[1024];

    read(sockfd,buffer,sizeof(buffer));

    printf("recv server =%s\n",buffer);

    return 0;
}

闲的无聊做了一丢丢优化:

  1. 实现客户端循环读入,且空格不影响输入
  2. 实现服务器持续接收,不会死循环读

server.c

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"

#define PORT 33333

int main()
{
    int sockfd;
    int cfd;

    int c_len;
    char buffer[1024];

    struct sockaddr_in addr;
    struct sockaddr_in c_addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    if (-1 == sockfd)
    {
        perror("socket create error!");
        exit(1);
    }

    printf("socket success\n");

    int opt = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bzero(&addr, sizeof(struct sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = inet_addr("192.168.128.128");

    if (bind(sockfd, (struct sockaddr *)(&addr), sizeof(struct sockaddr_in)) < 0)
    {
        perror("bind error!");
        exit(1);
    }

    printf("bind success\n");

    if (listen(sockfd, 3) < 0)
    {
        perror("listen error!");
        exit(1);
    }

    printf("listen success\n");
    while (1)
    {
        memset(buffer, 0, sizeof(buffer));

        bzero(&c_addr, sizeof(struct sockaddr_in));
        c_len = sizeof(struct sockaddr_in);
        printf("accept.......\n");

        cfd = accept(sockfd, (struct sockaddr *)(&c_addr), &c_len);
        if (cfd < 0)
        {
            perror("accept error!");
            exit(1);
        }
        while (1)
        {
            while (1)
            {
                printf("port =%d  ip =%s\n", ntohs(c_addr.sin_port), inet_ntoa(c_addr.sin_addr));

                recv(cfd, buffer, sizeof(buffer), 0);
                printf("recv = %s\n", buffer);
                usleep(2);
                send(cfd, buffer, sizeof(buffer), 0);

                break;
            }
        }
        close(cfd);
    }
    return 0;
}

client.cpp

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/socket.h"
#include "arpa/inet.h"
#include <iostream>
using namespace std;
#define PORT 33333

int main()
{
    int sockfd;

    struct sockaddr_in s_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket error");
        exit(1);
    }

    cout << "client socket success!" << endl;

    bzero(&s_addr, sizeof(struct sockaddr_in));
    s_addr.sin_family = AF_INET;
    s_addr.sin_port = htons(PORT);
    s_addr.sin_addr.s_addr = inet_addr("192.168.182.128"); // ifconfig查一下

    if (connect(sockfd, (struct sockaddr *)(&s_addr), sizeof(struct sockaddr_in)) < 0)
    {
        perror("connect error!");
        exit(1);
    }
    cout << "connect success!" << endl;
    while (1)
    {
        char str[1024];
        cin.getline(str,1024);
        write(sockfd, str, sizeof(str));
        char buffer[1024];
        read(sockfd, buffer, sizeof(buffer));
        cout << "recv server = " << buffer << endl;
    }
    return 0;
}

运行效果:

server:

daa3717ef724c0.png)

client
在这里插入图片描述

这里还是明确:里面的ip地址,要查一下自己的,然后换成自己的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值