基于socket网络编程技术实现TCP和UDP的流程详解及实例

具体函数讲解太多,根据程序自行分析。

可以参考这篇文章:

https://blog.csdn.net/qq_41687938/article/details/119102328?spm=1001.2014.3001.5501

https://blog.csdn.net/weixin_42193813/article/details/105666316

目录

一、socket

1.1 socket概述

1.2 socket接口简介

1.3 Linux系统使用socket过程

二、基于socket的TCP通信

2.1 基于socket技术的TCP通信流程

2.2 基于socket技术的TCP通信流程框图

2.3 TCP socket编程实例

2.3.1 服务端(service.c)

2.3.2 客户端(client.c)

三、基于socket的UDP通信

3.1  基于socket技术的UDP通信流程框图

3.2  基于socket技术的UDP通信流程

3.2.1 服务器流程主要分为下述6个部分。

3.2.2 UDP协议的客户端流程

3.3 UDPSocket客户服务器通信实例

3.3.1 服务端(service.c)

3.3.2 客户端(client.c)

3.4 UDP编程注意:


一、socket

1.1 socket概述

socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

1.2 socket接口简介

socket():创建socket

bind():绑定socket到本地地址和端口,通常由服务端调用

listen():TCP专用,开启监听模式

accept():TCP专用,服务器等待客户端连接,一般是阻塞态

connect():TCP专用,客户端主动连接服务器

send():TCP专用,发送数据

recv():TCP专用,接收数据

sendto():UDP专用,发送数据到指定的IP地址和端口

recvfrom():UDP专用,接收数据,返回数据远端的IP地址和端口

close():关闭socket

1.3 Linux系统使用socket过程

在Linux下进行程序运行的方法:(假设已经编写好服务端程序(service.c),和客户端程序(client.c))

(需要开启两个终端)

1. 先分别对两个文件进行编译(分别执行以下两条命令)

gcc service.c -o service
gcc client.c -o client

2. 然后先运行服务器端

./service

3.  再在另一个终端中运行客户端

./client

4. 执行完成!

二、基于socket的TCP通信

2.1 基于socket技术的TCP通信流程

服务端(被动连接,需创建自己的地址信息)

创建一个套接字 ———socket()
绑定IP地址、端口等信息到socket上——bind()
监听套接字——listen()
等待客户端的连接请求——accept()
发送、接收数据——send()和recv(),或者read()和write()
关闭网络连接——close()

客户端

创建一个套接字——socket()
连接服务器——connect()
接收、发送数据——send()和recv(),或者read()和write()
关闭网络连接——close()

2.2 基于socket技术的TCP通信流程框图

基于socket网络编程技术实现TCP和UDP的流程详解及实例

2.3 TCP socket编程实例

2.3.1 服务端(service.c)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(){
        //创建套接字
        int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
        //将套接字和IP、端口绑定
        struct sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
        serv_addr.sin_family = AF_INET;  //使用IPv4地址
        serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
        serv_addr.sin_port = htons(1258);  //端口
        bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
        //进入监听状态,等待用户发起请求
        listen(serv_sock, 20);
        //接收客户端请求
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size = sizeof(clnt_addr);
        int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
        //向客户端发送数据
        char str[] = "Hello World!";
        write(clnt_sock, str, sizeof(str));
 
        //关闭套接字
        close(clnt_sock);
        close(serv_sock);
        return 0;
}

2.3.2 客户端(client.c)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
int main(){
        //创建套接字
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        //向服务器(特定的IP和端口)发起请求
        struct sockaddr_in serv_addr;
        memset(&serv_addr, 0, sizeof(serv_addr));  //每个字节都用0填充
        serv_addr.sin_family = AF_INET;  //使用IPv4地址
        serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
        serv_addr.sin_port = htons(1258);  //端口
        connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
 
        //读取服务器传回的数据
        char buffer[40];
        read(sock, buffer, sizeof(buffer)-1);
 
        printf("Message form server: %s\n", buffer);
 
        //关闭套接字
        close(sock);
        return 0;
}

三、基于socket的UDP通信

3.1  基于socket技术的UDP通信流程框图

UDP协议的程序设计框架,客户端和服务器之间的差别在于服务器必须使用bind()函数来绑定侦听的本地UDP端口,而客户端则可以不进行绑定,直接发送到服务器地址的某个端口地址。框图如图所示。

基于socket网络编程技术实现TCP和UDP的流程详解及实例
基于socket的UDP通信流程示意图

3.2  基于socket技术的UDP通信流程

3.2.1 服务器流程主要分为下述6个部分。

(1)建立套接字文件描述符,使用函数socket(),生成套接字文件描述符。

(2)设置服务器IP地址和端口,初始化要绑定的网络地址结构。

(3)绑定IP地址、端口等信息,使用bind()函数,将套接字文件描述符和一个地址进行绑定。

(4)循环接收客户端的数据,使用recvfrom()函数接收客户端的网络数据。

(5)向客户端发送数据,使用sendto()函数向服务器主机发送数据。

(6)关闭套接字,使用close()函数释放资源。UDP协议的客户端流程

3.2.2 UDP协议的客户端流程

UDP协议的客户端流程分为5个部分。

(1)建立套接字文件描述符,socket();

(2)设置服务器IP地址和端口,struct sockaddr;

(3)向服务器发送数据,sendto();

(4)接收服务器的数据,recvfrom();

(5)关闭套接字,close()。

3.3 UDPSocket客户服务器通信实例

3.3.1 服务端(service.c)

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

#define SERV_PORT 3000

int main()
{
	int sock_fd; //套接子描述符号
	int recv_num;
	int send_num;
	int client_len;
	char recv_buf[20];
	struct sockaddr_in addr_serv;
	struct sockaddr_in addr_client;//服务器和客户端地址
	sock_fd = socket(AF_INET,SOCK_DGRAM,0);
	if(sock_fd < 0){
		perror("socket");
		exit(1);
	} else{
		printf("sock sucessful\n");
	}
	//初始化服务器断地址
	memset(&addr_serv,0,sizeof(struct sockaddr_in));
	addr_serv.sin_family = AF_INET;//协议族
	addr_serv.sin_port = htons(SERV_PORT);
	addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);//任意本地址
	
	client_len = sizeof(struct sockaddr_in);
	/*绑定套接子*/
	if(bind(sock_fd,(struct sockaddr *)&addr_serv,sizeof(struct sockaddr_in))<0 ){
		perror("bind");
		exit(1);
	} else{	
		printf("bind sucess\n");
	}
	while(1){
		printf("begin recv:\n");
		recv_num = recvfrom(sock_fd,recv_buf,sizeof(recv_buf),0,(struct sockaddr *)&addr_client,&client_len);
		if(recv_num < 0){
			printf("bad\n");
			perror("again recvfrom");
			exit(1);
		} else{
			recv_buf[recv_num]='\0';
			printf("recv sucess:%s\n",recv_buf);
		}
		printf("begin send:\n");
		send_num = sendto(sock_fd,recv_buf,recv_num,0,(struct sockaddr *)&addr_client,client_len);
		if(send_num < 0){
			perror("sendto");
			exit(1);
		} else{
			printf("send sucessful\n");
		}
	}
	close(sock_fd);
	return 0;
}

3.3.2 客户端(client.c)

#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>

#define DEST_PORT 3000
#define DSET_IP_ADDRESS "192.168.1.103"

int main()
{
	int sock_fd;/*套接字文件描述符*/
	int send_num;
	int recv_num;
	int dest_len;
	char send_buf[20]={"hello tiger"};
	char recv_buf[20];
	struct sockaddr_in addr_serv;/*服务端地址,客户端地址*/
	
	sock_fd = socket(AF_INET,SOCK_DGRAM,0);//创建套接子
	//初始化服务器端地址
	memset(&addr_serv,0,sizeof(addr_serv));
	addr_serv.sin_family = AF_INET;
	addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);
	addr_serv.sin_port = htons(DEST_PORT);
	
	dest_len = sizeof(struct sockaddr_in);
	printf("begin send:\n");
	send_num = sendto(sock_fd,send_buf,sizeof(send_buf),0,(struct sockaddr *)&addr_serv,dest_len);
	if(send_num < 0){
		perror("sendto");
		exit(1);
	} else{
		printf("send sucessful:%s\n",send_buf);
	 }
	recv_num = recvfrom(sock_fd,recv_buf,sizeof(recv_buf),0,(struct sockaddr *)&addr_serv,&dest_len);
	if(recv_num <0 ){
		perror("recv_from");
		exit(1);
	} else{
		printf("recv sucessful\n");
	 }
	recv_buf[recv_num]='\0';
	printf("the receive:%s\n",recv_buf);
	close(sock_fd);
	return 0;
}

3.4 UDP编程注意:

1、UDP报文可能会丢失、重复

2、UDP报文可能会乱序

3、UDP缺乏流量控制

4、UDP协议数据报文截断

5、recvfrom返回0,不代表连接关闭,因为udp是无连接的。

6、ICMP异步错误

7、UDP connect

8、UDP外出接口的确定

9、太大的UDP包可能出现的问题

参考链接:

https://blog.csdn.net/lell3538/article/details/53335472

https://blog.csdn.net/dongyanxia1000/article/details/80743691

https://blog.csdn.net/weixin_42193813/article/details/105771978

上一篇:【SQL】Oracle分页查询的三种方法


下一篇:SQL 获取 IDENTITY 三种方法 SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY的区别