套接字编程(一)——TCP和UDP协议的CS简单实现

发布于 2023-09-08  296 次阅读


环境说明:是在ubuntu下的Linux环境中使用C语言进行编程,但是重要的不是采用的什么语言,重要的是如何进行套接字编程的思想及实现的过程。

相关库及函数介绍:

  1. stdlib.h
  2. stdio.h
  3. sys/socket.h
  4. netdb.h
  5. string.h
  6. unistd.h
  7. netinet/in.h
  8. arpa/inet.h

TCP套接字的实现

流程分析:对于TCP套接字来说,其流程主要分为服务器端和客户端,在服务器端中,创建套接字——>绑定套接字——>监听信道——>三次握手连接——>接收请求——>处理请求——>关闭连接。而在服务器端中,创建套接字——>绑定套接字——>请求连接——>发送请求——>接收回显——>关闭连接。

其实现代码如下:

tcpserver.c:


#include<stdlib.h>
#include<stdio.h>
#include<sys/socket.h>
#include<netdb.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define PORT 8900
int main(int argc,char** argv)
{
	struct sockaddr_in server;
	struct sockaddr_in client;
	int len;
	int port;
	int listend;
	int connectd;
	int sendnum;
	int opt;
	int recvnum;
	char send_buf[2048];
	char recv_buf[2048];
      //if (2==argc)
	port= PORT;
	memset(send_buf,0,2048);
	memset(recv_buf,0,2048);
      opt = SO_REUSEADDR;
      if (-1==(listend=socket(AF_INET,SOCK_STREAM,0)))
      {
	 perror("create listen socket error\n");
	 exit(1);
      }
      setsockopt(listend,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
      #ifdef DEBUG
	printf("the listen id is %d\n",listend);
      #endif
     memset(&server,0,sizeof(struct sockaddr_in));
     server.sin_family = AF_INET;
     server.sin_addr.s_addr = htonl(INADDR_ANY);
     server.sin_port = htons(port);
     if (-1==bind(listend,(struct sockaddr *)&server,sizeof(struct sockaddr)))
     {
	perror("bind error\n");
	exit(1);
     }
    if (-1==listen(listend,5))
    {
	perror("listen error\n");
	exit(1);
    }
    while (1)
    {
        if (-1==(connectd=accept(listend,(struct sockaddr*)&client,&len)))
    	{
		perror("create connect socket error\n");
		continue;
    	}
   	#ifdef DEBUG
		printf("the connect id is %d",connect);
		printf("the client ip addr is %s",inet_ntoa(client.sin_addr));
   	#endif
	sendnum = sprintf(send_buf,"hello,the guest from %s\n",inet_ntoa(client.sin_addr));
       if ( 0 >send(connectd,send_buf,sendnum,0))
	{
		perror("send error\n");
		close(connectd);
		continue;
	}
   	#ifdef DEBUG
		printf("the send num is %d",sendnum);
		printf("the client ip addr is %s",inet_ntoa(client.sin_addr));
   	#endif
        if (0>(recvnum = recv(connectd,recv_buf,sizeof(recv_buf),0)))
	{
		perror("recv error\n");
		close(connectd);
		continue;
	}
	recv_buf[recvnum]='\0';
	printf ("the message from the client is: %s\n",recv_buf);
	if (0==strcmp(recv_buf,"quit"))
  {
		perror("the client break the server process\n");
		close(connectd);
		break;
	}
	sendnum = sprintf(send_buf,"byebye,the guest from %s\n",inet_ntoa(client.sin_addr));
  send(connectd,send_buf,sendnum,0);
 
	close(connectd);
	continue;
   }
    close(listend);
    return 0;
}

tcpclient.c:

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

#define PORT 8900

void print_usage(char * cmd)
{
	fprintf(stderr," %s usage:\n",cmd);
	fprintf(stderr,"%s IP_Addr [port]\n",cmd);

}


int main(int argc,char** argv)
{
	struct sockaddr_in server;
	int ret;
	int len;
	int port;
	int sockfd;
	int sendnum;
	int recvnum;
	char send_buf[2048];
	char recv_buf[2048];

	if ((2>argc)|| (argc >3))
	{
		print_usage(argv[0]);
		exit(1);

	}

       if (3==argc) 
       {
		port = atoi(argv[2]);
       }

    	if (-1==(sockfd=socket(AF_INET,SOCK_STREAM,0)))
	{
		perror("can not create socket\n");
		exit(1);
	}

	memset(&server,0,sizeof(struct sockaddr_in));
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = inet_addr(argv[1]);
	server.sin_port = htons(port);

	if (0>(ret=connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))))
	{
		perror("connect error");
		close(sockfd);
		exit(1);

	}

	//memset(send_buf,0,2048);
	//memset(recv_buf,0,2048);

	printf("what words do  you want to tell to server:\n");
	//gets(send_buf);
	fgets(send_buf,2048,stdin);

        #ifdef DEBUG
		printf("%s\n",send_buf);
  	#endif 

	//sprintf(send_buf,"i am lg,thank for your servering\n");

	if (0>(len=send(sockfd,send_buf,strlen(send_buf),0)))
	{
		perror("send data error\n");
		close(sockfd);
		exit(1);

	}

	if (0>(len=recv(sockfd,recv_buf,2048,0)))
	{
		perror("recv data error\n");
		close(sockfd);
		exit(1);
	}
	
	recv_buf[len]='\0';

	printf("the message from the server is:%s\n",recv_buf);
	close(sockfd);

}

UDP套接字的实现

UDP套接字和TCP套接字实现的不同之处在于,UDP是无连接的,因此不需要三次握手连接的过程,因此在UDP套接字编程过程中,服务器端:创建套接字——>绑定套接字——>等待接收请求——>处理请求——>发送回显——>关闭连接。客户端:创建套接字——>绑定套接字——>发送请求——>接收回显——>关闭连接。

udpserver.c:


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

#define PORT 8700

int main(int argc,char** argv)
{
	//储存客户端和服务器地址信息的结构体
	struct sockaddr_in server;
	struct sockaddr_in client;
	//存储客户端和服务器端发送和接收的信息
	char send_buf[2048];
	char recv_buf[2048];
	//存储客户端地址信息的结构体client的大小,并将其存储在len中
	socklen_t len = sizeof(client);
	int sockfd;
	int opt;
	int port = PORT;

	opt = SO_REUSEADDR;
	//初始化缓存区内容,全部设置为0
	memset(recv_buf, 0, 2048);
	memset(send_buf, 0, 2048);
	//创建套接字,并采用UDP协议作为传输协议,将函数调用结果赋值给sockfd,如果值为-1,则表示创建套接字失败。
	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
	{
		perror("create socket error\n");
		exit(1);
	}
	//设置套接字地址可立即重复使用
	setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	//配置服务器的地址信息,并将其存储在命名为server的sockaddr_in结构体中
	//初始化结构体,全部置0,大小是结构体大小
	memset(&server, 0, sizeof(struct sockaddr_in));
	//表示采用IPv4的地址族
	server.sin_family = AF_INET;
	//表示服务器监听的IP地址,INADD_ANY表示所有可用的网络接口中的IP地址,htonl函数将主机字节序的IP地址转化为网络字节序,从而正确设置sin_addr.s_addr
	server.sin_addr.s_addr = htonl(INADDR_ANY);
	//表示服务器监听的端口号,htons是将主机字节序的端口号转化为网络字节序的端口号
	server.sin_port = htons(port);
	//将套接字绑定到特定的地址(IP地址和端口号)
	//绑定相关地址和套接字是为了告诉操作系统将传入数据包路由到该套接字
	if(bind(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1)
	{
		perror("bind error\n");
		exit(1);
	}

	while(1)
	{
		//通过recvfrom函数从UDP套接字来接收数据,并将数据内容存储在recv_buf中,数据的字节数存储在recv_num中,客户端的地址信息存储在client中,同时更新len以表示client的大小。
		ssize_t recv_num = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&client, &len);
		
		if(recv_num < 0)
		{
			perror("recvfrom error\n");
			continue;
		}
		//将接收的信息截断
		recv_buf[recv_num] = '\0';

		printf("Received message from %s: %s\n",inet_ntoa(client.sin_addr),recv_buf);
		//如果接收信息为quit,则表示断开连接。
		if(strcmp(recv_buf, "quit\n") == 0)
		{
			printf("Client from %s requested to quit.\n",inet_ntoa(client.sin_addr));
			//将格式化之后的数据存储在send_buf中,其中inet_ntoa函数是将client结构体中的ip地址转化为字符串表示。
			sprintf(send_buf,"Hello, the guest from %s", inet_ntoa(client.sin_addr));
			//利用UDP套接字将send_buf中的信息发送给客户端,其中send_num代表发送数据的字节数
			ssize_t send_num = sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&client, len);

			if(send_num < 0)
			{
			perror("sendto error\n");
			}
			break;
		}
		//将格式化之后的数据存储在send_buf中,其中inet_ntoa函数是将client结构体中的ip地址转化为字符串表示。
		sprintf(send_buf,"Hello, the guest from %s", inet_ntoa(client.sin_addr));
		//利用UDP套接字将send_buf中的信息发送给客户端,其中send_num代表发送数据的字节数
		ssize_t send_num = sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&client, len);

		if(send_num < 0)
		{
			perror("sendto error\n");
		}
	}

	close(sockfd);
	return 0;
}

udpclient.c:

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

#define PORT 8700

void print_usage(char * cmd)
{
	fprintf(stderr," %s usage:\n",cmd);
	fprintf(stderr,"%s IP_Addr [port]\n",cmd);

}


int main(int argc,char** argv)
{
	struct sockaddr_in server;
	int ret;
	int len;
	int port;
	int sockfd;
	int sendnum;
	int recvnum;
	char send_buf[2048];
	char recv_buf[2048];

	if ((2>argc)|| (argc >3))
	{
		print_usage(argv[0]);
		exit(1);

	}

       if (3==argc) 
       {
		port = atoi(argv[2]);
       }

    	if (-1==(sockfd=socket(AF_INET,SOCK_DGRAM,0)))
	{
		perror("can not create socket\n");
		exit(1);
	}

	printf("what words do  you want to tell to server:\n");
	while(1)
	{
		memset(&server,0,sizeof(struct sockaddr_in));
		server.sin_family = AF_INET;
		server.sin_addr.s_addr = inet_addr(argv[1]);
		server.sin_port = htons(port);

	// if (0>(ret=connect(sockfd,(struct sockaddr*)&server,sizeof(struct sockaddr))))
	// {
	// 	perror("connect error");
	// 	close(sockfd);
	// 	exit(1);

	// }

	//memset(send_buf,0,2048);
	//memset(recv_buf,0,2048);

		//printf("what words do  you want to tell to server:\n");
	//gets(send_buf);
		fgets(send_buf,2048,stdin);

		//printf("%s \n",send_buf);
		//printf("%d",strcmp(send_buf,"quit"));
  	    #ifdef DEBUG
			printf("%s\n",send_buf);
  		#endif 

	//sprintf(send_buf,"i am lg,thank for your servering\n");

		if (0>(len=sendto(sockfd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&server, sizeof(struct sockaddr))))
		{
			perror("send data error\n");
			close(sockfd);
			exit(1);

		}
		if(strcmp(send_buf,"quit\n") == 0)
		{
			printf("thank you for your use, see you next time!\n");
			break;
		}

		if (0>(len=recvfrom(sockfd, recv_buf, 2048, 0, (struct sockaddr *)&server, &len)))
		{
			perror("recv data error\n");
			close(sockfd);
			exit(1);
		}
	
		recv_buf[len]='\0';

		printf("the message from the server is:%s\n",recv_buf);
		}
	close(sockfd);
        return 0;
}

一花一世界,一叶一菩提。