TCP/UDP网络编程总结

      

tcp/ip协议族

名词业务描述
应用层DNS、HTTP、FTP、PING软件程序
传输层TCP、UDP操作系统
网络层ICMP、IP、ARP操作系统
物理层物理网络 双绞线、无线网、光纤硬件设备

  专有名词                            作用
        ACK                    确认是否有效 值为1
        seq                            序列号标记位
        SYN                    请求建立连接 值为1
        FIN                            希望断开连接

TCP的三次握手

TCP的四次挥手

网络编程代码示例:

一、TCP简单通讯例子

TCP服务端代码

//服务端
#include <sys/types.h>      //基本系统数据类型
#include <sys/socket.h>     //socket套接字
#include <stdio.h>
#include <unistd.h>         //POSIX系统api访问
#include <string.h>
#include <arpa/inet.h>      //网络信息转换
#include <iostream>

using namespace std;
#define PORT 9990       //端口号
#define BUFFSIZE 1024   //接收数组大小

//创建套接字和初始化服务器地址以及监听函数
int create_and_init_socket()
{
    //创建socket
    /*
    *int socket(int family,int type,int protocol),返回一个文件描述符
    *family: 代表协议族,在socket中只能是AF_INET
    * type: 代表协议类型常见类型是SOCK_STREAM(TCP)-字节流,SOCK_DGRAM(UDP)-数据报
    * protocol: 代表具体的协议,对于标准套接字来说,其值是0。(原始套接字基本不会使用)
    */
    int socket_listen = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_listen == -1)    //创建监听失败
    {
        cout << "socket failed..." << endl;
        return -1;
    }

    //初始化服务器地址
    struct sockaddr_in server_dddr;    //IPv4地址的数据结构
    memset(&server_dddr, 0, sizeof(server_dddr));
    
    server_dddr.sin_family = AF_INET;                  //Internet地址族
    server_dddr.sin_addr.s_addr = htonl(INADDR_ANY);   //IP地址
    server_dddr.sin_port = htons(PORT);                //端口号
    
    //绑定socket套接字
    /*
    *int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    *sockfd 代表需要绑定的socket。是在创建socket套接字时返回的文件描述符
    *addr 存放了服务端用于通信的地址和端口
    *addrlen 代表addr结构体的大小
    */
    int re = bind(socket_listen, (struct sockaddr *) &server_dddr, sizeof(server_dddr));
    if (re == -1)
    {
        cout << "bind failed..." << endl;
        return -1;
    }

    //启动监听
    /*
    *int listen(int sockfd, int backlog);
    *listen函数的功能并不是等待一个新的connect的到来,
    *真正等待connect的是accept函数。
    *listen的操作就是当有较多的client发起connect时,
    *server端不能及时的处理已经建立的连接,
    *这时就会将connect连接放在等待队列中缓存起来。
    *这个等待队列的长度有listen中的backlog参数来设定。当listen运行成功时,返回0;运行失败时,返回值为-1.
    * sockfd: socket创建的文件描述符;
    * backlog: 指server端可以缓存连接的最大个数,也就是等待队列的长度。
    */
    int ret = listen(socket_listen, 5);
    if (ret == -1)
    {
        cout << "listen failed..." << endl;
        return - 1;
    }
    return socket_listen;
}

//等待客户端连接
int wait_to_accept_client(int socket_)
{
    //创建一个客户端IPv4地址的数据结构
    struct sockaddr_in client_addr;
    int client_addr_len = sizeof(client_addr);
    cout << "等待客户端连接..." << endl;

    //接收客户端连接,创建一个和客户端交流的套接字
    /*
    *int accept(int sockfd,struct sockaddr *client_addr,socklen_t *addrlen);
    *accept函数等待客户端的连接,如果没有客户端连上来,
    *它就一直等待,这种方式称为阻塞。
    *accept等待到客户端的连接后,创建一个新的socket,
    *函数返回值就是这个新的socket,
    *服务端用于这个新的socket和客户端进行报文的收发。
    * 
    *sockfd: 是已经被listen过的socket
    *client_addr: 用于存放客户端的地址信息,其中包含客户端的协议族,网络地址以及端口号。如果不需要客户端的地址,可以填0
    *addrlen: 用于存放参数二(client_addr)的长度
    */
    int client_socket = accept(socket_, (struct sockaddr *) &client_addr, (socklen_t *) &client_addr_len);
    if (client_socket == -1)
    {
        cout << "accept failed..." << endl;
        return -1;
    }
    cout << "连接到一个客户端..." << endl;
    return client_socket;
}


//处理信息函数
void handel_client_infos(int listen_socket, int client_socket)
{
    char buff[BUFFSIZE];

    //循环接收发送信息
    while (1)
    {
        //读取信息
        /*
        *int recv(int sockfd,void *buf,int len,int flags);
        *recv函数用于接收对端socket发送过来的数据。
        *不论是客户端还是服务端,
        *应用程序都用recv函数接受来自TCP连接的另一端发送过来的数据。
        *如果socket对端没有发送数据,recv函数就会等待,
        *如果对端发送了数据,函数返回接收到的字符数。出错时返回-1。
        *如果socket被对端关闭,返回值为0。
        *
        * sockfd: 接收端的套接字描述符,即通过socket()函数返回的文件描述符
        * buf: 为用于接收数据的内存地址,可以是C语言基本数据类型变量的地址,
        *       也可以是数组、结构体、字符串。只要是一块内存就行了。
        * len: 指明需要接收数据的字节数。不能超过buf的大小,否则内存溢出。
        * flags: 填0,其他数值意义不大
        */


       /*
       *ssize_t read(int fd, void *buf, size_t count);
       *read函数会阻塞等待,直到有数据可读或者发生错误。
       *它返回实际读取的字节数,如果返回值为0表示对端已关闭连接,返回-1表示发生错误。
       * fd是文件描述符,
       * buf是用于存储读取数据的缓冲区,
       * count是要读取的字节数(字节数的读取需要小于缓冲区区域,避免内存溢出)
       */
        int ret = read(client_socket, buff, BUFFSIZE);
        if (ret == -1)
        {
           cout << "read failed..." << endl;
           break;
        }
        if (ret == 0)   //零输入
        {
            break;
        }
        
        //给字符数组添加结尾符'\0'
        buff[ret] = '\0';
        //for (size_t i = 0; i < ret; i++)
        //{
        //   
        //}
        //cout << endl;
        cout << "客户端:" << buff << endl;

        //写入信息
        /*
        *ssize_t write(int fd, const void *buf, size_t count);
        *write函数会阻塞等待,直到所有数据都写入完毕或者发生错误。
        *它返回实际写入的字节数,返回-1表示发生错误
        *fd是文件描述符,
        *buf是要写入的数据的缓冲区,
        *count是要写入的字节数
        */
       char buf[BUFFSIZE];
       cout << "你想输入(服务端):";
       cin >> buf;
        int ret2 =  write(client_socket, buf, ret);
        if (ret2 == -1)
        {
            break;
        }
        
        if (strncmp(buff, "end", 3 == 0))   //退出符号
        {
           break;
        }
        
    }
    close(client_socket);
}

int main()
{
    int listen_socket = create_and_init_socket();

    int client_socket = wait_to_accept_client(listen_socket);

    handel_client_infos(listen_socket, client_socket);

    close(listen_socket);

    return 0;
}

TCP客户端代码

//客户端
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <iostream>
 
using namespace std;
#define PORT 9990
#define SIZE 1024
 
int main()
{
	int client_socket = socket(AF_INET, SOCK_STREAM, 0);   //创建和服务器连接套接字
	if(client_socket == -1)
	{
		//perror("socket");
		cout << "socket failed...";
		return -1;
	}
	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	
	addr.sin_family = AF_INET;  /* Internet地址族 */
    addr.sin_port = htons(PORT);  /* 端口号 */
    addr.sin_addr.s_addr = htonl(INADDR_ANY);   /* IP地址 */
	inet_aton("127.0.0.1", &(addr.sin_addr));   //将一个IPv4地址的字符串表示转换为网络字节序的32位二进制数
 
	int addrlen = sizeof(addr);
	int listen_socket =  connect(client_socket,  (struct sockaddr *)&addr, addrlen);  //连接服务器
	if(listen_socket == -1)
	{
		//perror("connect");
		cout << "connect failed...";
		return -1;
	}
	
	printf("成功连接到一个服务器\n");
	
	char buf[SIZE];
	
	while(1)        //向服务器发送数据,并接收服务器信息
	{
		printf("你想输入(客户端):");
		//scanf("%s", buf);
		cin >> buf;
		write(client_socket, buf, strlen(buf));

		int ret = read(client_socket, buf, strlen(buf));
		//printf("服务端 = %s", buf);
		//printf("\n");
		cout << "服务端:" << buf << endl;
		if(strncmp(buf, "end", 3) == 0)     //当输入end时客户端退出
		{
			break;
		}
	}
	close(listen_socket);
	
	return 0;
}

二、UDP简单通讯例子

udp服务端代码

#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>

using namespace std;
#define PORT 8888
#define BUFFSIZE 1024

//简单UDP通讯程序,服务端接收
int main() 
{
    //创建套接字
    int sock;
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("error");
        return -1;
    }
    
    //创建地址协议数据结构体
    struct sockaddr_in s;
    memset(&s, 0, sizeof(s));

    s.sin_family = AF_INET;
    s.sin_port = htons(8888);
    s.sin_addr.s_addr = htonl(INADDR_ANY);

    //将套接字与协议进行绑定
    if (bind(sock, (struct sockaddr *) &s, sizeof(s)) == -1)
    {
        perror("error");
        return -1;
    }

    //接收并输出信息
    char buff[BUFFSIZE];
    socklen_t sock_len = sizeof(s);

    /*
        int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
        (需要注意的是,这里recvfrom中最后一个长度参数传的是socklen_t *指针,而非sendto中的socklen_t)
        recvfrom函数会阻塞程序执行,直到有数据到达或者发生错误。当有数据到达时,
        它会将数据读取到buf指向的缓冲区中,并返回实际接收到的字节数。如果发生错误,它会返回-1,并设置errno来指示具体的错误原因。
        参数:
            sockfd:表示要接收数据的套接字描述符。
            buf:指向接收数据的缓冲区。
            len:表示接收缓冲区的大小。
            flags:用于指定接收操作的行为,常用的标志有MSG_DONTWAIT和MSG_WAITALL等。
            src_addr:指向一个sockaddr结构体,用于存储发送方的地址信息。
            addrlen:指向一个整数,表示src_addr结构体的长度。
    */
    recvfrom(sock, buff, sizeof(buff) /* - 1 */, 0, (struct sockaddr *)&s, (socklen_t *)&sock_len);
    //printf("client message:%s\n", buff);
    cout << "客户端信息:" << buff << endl;
    return 0;
}

udp客户端代码

#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netinet/in.h>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>

using namespace std;
#define PORT 8888
#define BUFFSIZE 1024

//简单UDP通讯程序,客户端发送
int main() 
{
    //创建socket套接字
    int sock;
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)  //使用数据报协议SOCK_DGRAM
    {
        perror("error");
        return -1;
    }

    //创建协议地址数据结构体并进行初始化
    struct sockaddr_in s;
    memset(&s, 0, sizeof(s));

    s.sin_family = AF_INET;
    s.sin_port = htons(PORT);
    s.sin_addr.s_addr = htonl(INADDR_ANY);

    //输入信息并进行发送
    char buff[BUFFSIZE];
    //scanf("%s", buff);
    cout << "你想输入(客户端):";
    cin >> buff;

    /*
    *int sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    sendto函数的返回值为成功发送的字节数,如果返回-1,则表示发送失败,可以通过errno来获取具体的错误信息。
    参数:
        sockfd:表示要发送数据的套接字描述符。
        buf:指向要发送的数据的缓冲区。
        len:表示要发送的数据的长度。
        flags:用于指定发送操作的可选标志,常用的标志有0和MSG_DONTWAIT。
        dest_addr:指向目标地址的结构体指针,包括目标IP地址和端口号等信息。
        addrlen:表示目标地址结构体的长度。
    */
    sendto(sock,buff, strlen(buff), 0, (struct sockaddr *)&s,  sizeof(s));
    return 0;
}

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值