C/C++ tcp编程

一:api详解

二:服务端:api接口说明

1.int socket(AF_INET/AF_INET6, SOCK_STREAM/SOCK_DGRAM, IPPROTO_TCP/IPPTOTO_UDP)

参数一:AF_INET(ipv4) AF_INET6(ipv6)

参数二: SOCK_STREAM(面向连接的传输方式).  SOCK_DGRAM(面向无连接传输方式)

参数三:IPPROTO_TCP(tcp协议)        IPPTOTO_UDP(udp协议)

返回值:小于0 表示创建socket套接字失败

2.int bind(socket,(struct sockaddr *)&addr_info,sizeof(addr_info))

参数一:socket套接字

参数二:本级ip地址+端口号信息结构体

        信息结构体的定义及初始化:1. struct sockaddr_in addr_info; 2. addr_info.sin_family = AF_INET; //表示使用ipv4

               3.addr_info.sin_addrs.sin_addr = htonl(INADDR_ANY);INADDR_ANY表示能收到任意网卡连接的

               4.addr_info.sin_port = htons(端口号);

参数三:参数二结构体的大小

返回值:小于0表示绑定失败

3.int listen(socket,最大连接数量)

参数一:socket

参数二:最大客户端连接数

返回值:小于0表示绑定失败

4.int accept(socket, (struct sockaddr *)&accept_addr_info,&len)

参数一:socket

参数二:表示连接的客户端信息的结构体

参数三:参数二结构体的大小

返回值:返回值为通信套接字,用于服务器与客户端之间通信

        定义客户端信息结构体:1. struct sockaddr_in * accept_addr_info;2. socklen_t len = sizeof(accept_addr_info);                 

5.收发消息send()/recv()        read()/write()

ssize_t send(accept,buf,sizeof(buf)); recv()函数同理;

参数一:accept套接字

参数二:接受数据的字符串数组 buf

参数三:buf大小        

返回值:成功返回写入/读出的字节数,失败返回-1;

socket缓冲区及阻塞模式,数据的写入和读取有两个区,分为写入缓存区和读入缓存区。当send/write时,数据不是直接发送到网络而是先把数据写入缓存区,写入成功函数返回值就默认成功,此时关闭套接字数据仍会发送并且数据并不是写入一次就立即发送,会受到网络环境和进程的影响。recv/read 读取数据同理,也是从输入缓存区读取

TCP粘包分析

6.tcp进阶函数

int setsockopt(int socket, int level, int optname, void * optval, socklen_t * optlen)

作用:设置套接字描述符的属性

参数一:socket套接字

参数二:表示套接字使用的协议(如,ipv4、 ipv6、tcp、stcp,通用协议用SOL_SOCKET)

参数三:与参数二是对应关系,参数二不同的协议下面有多个选项,通过参数三进行设置

参数四:指向某个变量的指针,该变量是要设置新值的缓冲区。可以是一个结构体,也可以是普通变量

参数五:socklen_t * optlen,即参数四值的大小

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

补充:参数二与参数三的对应关系

        level = SOL_SOCKET

        optname = SO_BROADCAST(允许发送广播数据)        SO_DEBUG(允许调试)  SO_LINGER(延迟关闭连接)  SO_OOBINLINE(带外数据放入正常数据流)  SO_RCVBUF(接收缓冲区大小)  SO_SNDBUF(发送缓冲区大小)  SO_RCVLOWAT(接收缓冲区下限) SO_SNDLOWAT(发送缓冲区下限)  SO_RCVTIMEO(接收超时)    SO_SNDTIMEO(发送超时)       SO_REUSEADDR(ip+端口复用)   SO_REUSEPORT(ip+端口复用) 

        level = IPPROTO_IP

        optname  = IP_HDRINCL(在数据包中包含IP首部).  IP_OPTINOS(IP首部选项).  IP_TTL(生存时间)

        level = IPPRO_TCP

        optname = TCP_MAXSEG(TCP最大数据段的大小).   TCP_NODELAY(不使用Nagle算法)

7.服务端代码(添加poll+线程  i/o多路复用)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <ctype.h>
#include <poll.h>
#include <arpa/inet.h>
#include <pthread.h>


pthread_t ptid;
using namespace std;
int connect_fd,accept_fd;
struct sockaddr_in connect_addr;
struct sockaddr_in accept_addr;
char buf[32];

int max_t = 3;
socklen_t len = sizeof(accept_addr);
struct pollfd poll_fds[10];

int init_connect(){
    connect_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (connect_fd < 0){
        perror("******* create socket fd error  ***\n");
        return -1;
    }
    printf("======== socket_fd ok ============\n");
    connect_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    connect_addr.sin_family = AF_INET;
    connect_addr.sin_port = htons(8888);
    int on = 1;
    setsockopt(connect_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
    if (bind(connect_fd,(struct sockaddr *)&connect_addr,sizeof(connect_addr)) < 0){
        perror("******** ip地址绑定失败 **********\n");
        return -1;
    }
    printf("======== bind ip ok ============\n");
    if (listen(connect_fd,10) < 0){
        perror("****** 本机IP地址监听失败 ********\n");
        return -1;
    }
    printf("======== listener ip ok ============\n");
    return connect_fd;
}

void *accept_msg_while(void *fd){
    int ffd = *(int *)fd;

    while(1){
        if (read(ffd, buf, sizeof(buf)) <= 0){
                printf("^^^^^^^^ip %u 连接断开或连接出错 ^^^^^^^^^^^^\n",ntohl(accept_addr.sin_addr.s_addr));
                printf("^^^^^^^^ 等待下次连接 ^^^^^^^^^^^^^^^^^^\n");
                break;
        }
        else{
            printf("^^^^ accept fd = %d ^^^^^^\n",ffd);
            printf("*** recv ip %d msg : %s \n",(int)accept_addr.sin_addr.s_addr,buf);
        }
        memset(buf,0,sizeof(buf));
    }
    close(ffd);
}


int accept_msg(){
    while(1){
        accept_fd = accept(connect_fd,(struct sockaddr *)&accept_addr,&len);
        if(accept_fd < 0){
            perror("******* accept error ********\n");
            return -1;
        }
        printf("======== accept ok ==== fd val = %d  =======\n",accept_fd);

        if (accept_fd > max_t){
            poll_fds[max_t].fd = accept_fd;
            poll_fds[max_t].events = POLLIN;
            max_t = max_t +1;
        }
        else{
            poll_fds[accept_fd - 1].fd = accept_fd;
            poll_fds[accept_fd].events = POLLIN;
        }

        if (poll(poll_fds,max_t,0) < 0){
            perror("--- poll err -----\n");
        }
        for(int i=0;i < max_t+1;i++){
            if (poll_fds[i].fd == accept_fd && (poll_fds[i].events && POLLIN)){
                pthread_create(&ptid, NULL, accept_msg_while, (void *)&accept_fd);
                memset(buf,0,sizeof(buf));
            }
        }

        memset(buf,0,sizeof(buf));
    }
    pthread_exit(NULL);
   return 0;
}


int main(){


    init_connect();
    if (connect_fd < 0){
        return -1;
    }
    accept_msg();

    for(int i=3;i < max_t;i++){
        close(poll_fds[i].fd);
        printf("^^^^^ 已关闭accpect fd %d ^^^^^^^^^^\n",poll_fds[i].fd);
    }
    close(connect_fd);
    return 0;
}

8.客户端代码,待优化

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <signal.h>
#include <arpa/inet.h>

#define MAXSIZE 1024
#define IP_ADDR "127.0.0.1"
#define IP_PORT 8888

int connect_fd = -1;
int init_connect(){

    struct sockaddr_in con_sock;
    if((connect_fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0)	//建立套接字
	{
		printf("=====socket connect Error: %s (errno: %d) ====\n", strerror(errno), errno);
		return -1;
	}

	memset(&con_sock, 0, sizeof(con_sock));
	con_sock.sin_family = AF_INET;  //IPv4协议
    //IP地址转换(直接可以从物理字节序的点分十进制 转换成网络字节序)
	if(inet_pton(AF_INET, IP_ADDR, &con_sock.sin_addr) <= 0)
	{
		printf("inet_pton Error: %s (errno: %d)\n", strerror(errno), errno);
		return -1;
	}
	con_sock.sin_port = htons(IP_PORT);	//端口转换(物理字节序到网络字节序)

	if(connect(connect_fd, (struct sockaddr*)&con_sock, sizeof(con_sock)) < 0)	//主动向设置的IP和端口号的服务端发出连接
	{
		printf("connect Error: %s (errno: %d)\n", strerror(errno), errno);
		return -1;
	}
    return 0;
}

int send_to_sevice(){
    char msg[32];
        fflush(stdin);
        printf("--- 请输入消息内容 ---\n");
		fgets(msg, MAXSIZE, stdin); //获取终端输入
        printf("msg size = %lu \n",sizeof(msg));
        if(strncmp(msg,"quit",4)==0){
            printf("======= 客户端即将结束 ======\n");
            close(connect_fd);
            //break;
        }
		printf("will send: %s", msg);
		if(write(connect_fd, msg, sizeof(msg)) < 0)	//发送数据
		{
			printf("write Error: %s (errno: %d)\n", strerror(errno), errno);
			exit(1);
		}
        else{
            printf("==== 消息发送成功======\n");
        }

		memset(msg, 0, sizeof(msg));
    return 0;

}
int quit(){
    char msg[32];
    fgets(msg, MAXSIZE, stdin); //获取终端输入
    if(strncmp(msg,"quit",4)==0){
            printf("======= 客户端即将结束 ======\n");
            close(connect_fd);
            return 3;
        }
    return 3;
}
int main()
{
    printf("+++++++++++++++++++++++++++++++++++++++\n");
    printf("+++++++++++++++++++++++++++++++++++++++\n");
    printf("+++++++ 欢迎使用xxx客户端 +++++++++++++++\n");
    printf("+++++++++++++++++++++++++++++++++++++++\n");
    printf("++ 请选择功能 :  \n");
    printf("1. 连接 2。发送消息 3.退出 \n");
    printf("+++++++++++++++++++++++++++++++++++++++\n");
    int flag,connect_status;
    int q;
    while((scanf("%d",&flag)) != EOF){
        printf("您输入的选项为 %d \n",flag);
        switch(flag){
            case 1:
                init_connect();
                break;
            case 2:
                send_to_sevice();
                break;
            case 3:
                q = quit();
                break;
            default:
                printf("----输入参数有误--\n");
        }
        if(q == 3)
            break;
    }
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C/C++ 网络编程是使用 C/C++ 编程语言来实现网络通信的技术。通过网络编程,可以在不同的计算机之间进行数据交换和通信。网络编程主要涉及以下几个方面: 1. 套接字编程:套接字是网络编程的基础,它允许不同计算机之间通过网络进行通信。在 C/C++ 中,我们可以使用 socket 函数来创建套接字,并使用相关的函数来发送和接收数据。 2. TCP/IP 协议:TCP/IP网络通信的基本协议,它是一系列规范和协议的集合,用于在 Internet 上的不同计算机之间进行数据传输。C/C++ 网络编程中,我们可以使用相关的库函数和结构体来实现 TCP/IP 协议的功能,包括建立连接、发送和接收数据等。 3. 客户端和服务器:在网络编程中,通常有客户端和服务器两个角色。客户端是发起网络请求的一方,服务器是提供网络服务和响应请求的一方。C/C++ 网络编程可以通过套接字编程TCP/IP 协议来实现客户端和服务器之间的通信。 4. 多线程和并发:网络编程中,往往需要同时处理多个客户端请求。 C/C++ 提供了多线程编程的支持,可以使用线程来处理并发的网络请求。多线程编程可以提高网络程序的性能和效率。 总之,C/C++ 网络编程是一种使用 C/C++ 语言进行网络通信的技术。通过掌握套接字编程TCP/IP 协议、客户端和服务器的概念以及多线程和并发技术,可以实现各种网络应用程序,如聊天程序、文件传输、远程控制等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值