l8-d7 实现TCP通信

一、TCP服务器的实现(理论)

#include <sys/types.h>

#include <sys/socket.h>

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

        -domain: 指定通信域(通信地址族);

        -type: 指定套接字类型;

        -protocol: 指定协议;

套接字类型与协议

-type: 指定套接字类型

TCP唯一对应流式套接字,所以选择SOCK_STREAM(数据报套接字:SOCK_DGRAM)

-protocol: 指定协议

流式套接字唯一对应TCP,所以无需要指定协议,设为0即可

bind函数与通信结构体

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

        -sockfd:socket函数生成的套接字

        -addr:通信结构体

        -addrlen:通信结构体的长度

IPv4地址族结构体

struct sockaddr_in {

        sa_family_t    sin_family; /* 地址族: AF_INET */

        in_port_t      sin_port;   /* 网络字节序的端口号 */

        struct in_addr sin_addr;   /*IP地址结构体 */

};

/* IP地址结构体 */

struct in_addr {

        uint32_t       s_addr;     /* 网络字节序的IP地址 */

};

二、实现代码

优化前

服务端

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 5001
#define BACKLOG 5

int main(int argc, char *argv[])
{
	int fd, newfd;
	char buf[BUFSIZ] = {}; //BUFSIZ 8142
	struct sockaddr_in addr;
	/*创建套接字*/
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	addr.sin_addr.s_addr = 0;
	/*绑定通信结构体*/
	if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
		perror("bind");
		exit(0);
	}
	/*设置套接字为监听模式*/
	if(listen(fd, BACKLOG) == -1){
		perror("listen");
		exit(0);
	}
	/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
	newfd = accept(fd, NULL, NULL);
	if(newfd < 0){
		perror("accept");
		exit(0);
	}
	printf("BUFSIZ = %d\n", BUFSIZ);
	read(newfd, buf, BUFSIZ);
	printf("buf = %s\n", buf);
	close(fd);
	return 0;
}

客户端

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 5001
#define BACKLOG 5
#define STR "Hello World!"

int main(int argc, char *argv[])
{
	int fd;
	struct sockaddr_in addr;
	/*创建套接字*/
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	/*向服务端发起连接请求*/
	if(connect(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){
		perror("connect");
		exit(0);
	}
	write(fd, STR, sizeof(STR) );
	printf("STR = %s\n", STR);
	close(fd);
	return 0;
}

优化后

服务端

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

#define BACKLOG 5    //BACKLOG为5,表示服务器端可以排队的最大客户端连接数

int main(int argc, char *argv[])
{
	int fd, newfd, ret;
	char buf[BUFSIZ] = {}; //BUFSIZ 8142  用于存储从客户端读取的数据
	struct sockaddr_in addr;    //存储服务器的地址信息
	
	if(argc < 3){    //检查命令行参数的数量是否小于3,如果是,则打印错误信息并退出程序。这是因为服务器需要两个命令行参数:服务器的IP地址和端口号。
		fprintf(stderr, "%s<addr><port>\n", argv[0]);
		exit(0);
	}

	/*创建套接字*/
	fd = socket(AF_INET, SOCK_STREAM, 0);    //创建一个新的套接字
	if(fd < 0){                //AF_INET表示使用IPv4协议,SOCK_STREAM表示使用TCP协议
		perror("socket");
		exit(0);
	}
	addr.sin_family = AF_INET;        //使用IPv4协议
	addr.sin_port = htons( atoi(argv[2]) );    //将命令行参数中的端口号转换为整数,并使用htons函数将其转换为网络字节序,然后设置到addr结构体中的sin_port字段。
	if ( inet_aton(argv[1], &addr.sin_addr) == 0) {    //将命令行参数中的IP地址转换为网络字节序并设置到addr结构体中的sin_addr字段。如果转换失败,则打印错误信息并退出程序。
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}

	/*绑定通信结构体*/
	if(bind(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){    //调用bind函数将套接字fd绑定到地址信息addr
		perror("bind");
		exit(0);
	}
	/*设置套接字为监听模式*/
	if(listen(fd, BACKLOG) == -1){    //用listen函数将套接字fd设置为监听模式,并设置最大客户端连接数为BACKLOG
		perror("listen");
		exit(0);
	}
	/*接受客户端的连接请求,生成新的用于和客户端通信的套接字*/
	newfd = accept(fd, NULL, NULL);    //调用accept函数接受客户端的连接请求,并返回一个新的套接字描述符newfd用于和客户端通信
	if(newfd < 0){
		perror("accept");
		exit(0);
	}
	while(1){
		memset(buf, 0, BUFSIZ);    //调用memset函数将buf数组中的所有元素都设置为0
		ret = read(newfd, buf, BUFSIZ);    //调用read函数从客户端套接字newfd读取数据,并将数据存储到buf数组中。read函数返回读取到的字节数,并将其存储到ret变量中
		if(ret < 0)    //如果read函数返回值小于0,表示读取数据失败
		{    
			perror("read");
			exit(0);
		}
		else if(ret == 0)    //如果read函数返回值等于0,表示客户端已经关闭了连接
			break;
		else
			printf("buf = %s\n", buf);
	}
	close(newfd);    //关闭客户端套接字和服务器套接字
	close(fd);
	return 0;
}

客户端

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

#define BACKLOG 5

int main(int argc, char *argv[])
{
	int fd;
	struct sockaddr_in addr;
	char buf[BUFSIZ] = {};

	if(argc < 3){
		fprintf(stderr, "%s<addr><port>\n", argv[0]);
		exit(0);
	}

	/*创建套接字*/
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd < 0){
		perror("socket");
		exit(0);
	}

	addr.sin_family = AF_INET;
	addr.sin_port = htons( atoi(argv[2]) );
	if ( inet_aton(argv[1], &addr.sin_addr) == 0) {
		fprintf(stderr, "Invalid address\n");
		exit(EXIT_FAILURE);
	}

	/*向服务端发起连接请求*/
	if(connect(fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1){    //调用connect函数向服务器发起连接请求
		perror("connect");
		exit(0);
	}
	while(1){
		printf("Input->");
		fgets(buf, BUFSIZ, stdin);    //调用fgets函数从标准输入读取一行数据,并将其存储到buf数组中。
		write(fd, buf, strlen(buf) );    //调用write函数将buf数组中的数据发送给服务器。
	}
	close(fd);
	return 0;
}

makefile 

CC=gcc
CFLAGS=-Wall
all:client server

clean:
    rm client server

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值