网络编程之创建TCP协议通讯

一.需要认识的函数

1.创建套接字

头文件:

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

接口声明:int socket(int domain, int type, int protocol);

参数:

        domain:域。

                AF_INET/PF_INET: 网际协议

                AF_UNIX/PF_UNIX:本地协议,可写成 AF_LOCAL/PF_LOCAL

        type:类型。

                SOCK_STREAM:流式套接字     TCP

                SOCK_DGRAM:数据包套接字    UDP

        protocol:协议。  一般为 0

返回值:

        成功:待连接套接字

        失败:-1

备注:在网际协议中,选择流式套接字就代表 TCP 协议,选择数据包套接字就代表 UDP 协议,

第三个参数 protocol 一般都不用。

2.绑定套接字与网络地址

头文件:

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

接口声明:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

        sockfd:待连接套接字

        addr:包含本地地址(IP+PORT)的通用地址结构体的指针

        addrlen:地址结构体大小

返回值:

        成功:0

        失败:-1

备注:

通用地址结构体的定义:

struct sockaddr
{
    sa_family_t sa_family
    char sa_data[14];
};

特殊地址结构体 —— IPv4 地址结构体:

struct sockaddr_in
{
    u_short   sin_family; // 地址族
    u_short   sin_port; // 端口,0-----1023, 1024-----5000,5000------65535
    struct in_addr  sin_addr; // IPV4 地址
    char sin_zero[8];
};

struct in_addr
{
    in_addr_t s_addr; // 无符号 32 位网络地址
    sockaddr. sin_addr. s_addr = htons(192.168.124.195)
};

特殊地址结构体 —— IPv6 地址结构体:

struct sockaddr_in6
{
    u_short sin6_family; // 地址族
    __be16 sin6_port; // 端口
    __be32 sin6_flowinfo; // 流信息
    struct in6_addr sin6_addr; // IPv6 地址
    __u32 sin6_scope_id;
};

特殊地址结构体 ——UNIX 域地址结构体:

struct sockaddr_un
{
    u_short sun_family; // 地址族
    char sun_path[108]; // 套接字文件路径
};

3.监听

 将待连接套接字设置为监听套接字,并设置最大同时接收连接请求个数

头文件:

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

接口声明:int listen(int sockfd, int backlog);

参数:

        sockfd:待连接套接字

        backlog:最大同时接收连接请求个数

返回值:

        成功:0,并将 sockfd 设置为监听套接字

        失败:-1

备注:

        由于历史原因,各种系统对 backlog 的理解并不一致,以 LINUX 为例,监听端能同时接收的最大连接请求个数为 0+4。

 4.等待对端连接请求

头文件:

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

接口声明:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

        sockfd:监听套接字

        addr:通用地址结构体,用以存储对端地址(IP+PORT,结构体如上同理

        addrlen:参数 addr 的存储区域大小

返回值:

        成功:已连接套接字(非负整数)

        失败:-1

5.连接对端监听套接字 

头文件:

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

接口声明:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

        sockfd:待连接套接字

        addr:包含对端地址(IP+PORT)的通用地址结构体的指针,结构体如上同理

        addrlen:地址结构体大小

返回值:

        成功:0

        失败:-1

6.向 TCP 套接字发送数据

头文件:

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

接口声明:ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数:

        sockfd:已连接套接字

        buf:即将被发送的数据

        len:数据长度

        flags:发送标志

        {

                MSG_NOSIGNAL:当对端已关闭时,不产生 SIGPIPE 信号

                MSG_OOB:发送紧急(带外)数据,只针对 TCP 连接

        }

返回值:

        成功:已发送字节数

        失败:-1

备注:

        当 flags 为 0 时,send 与 write 作用相同。

7.从 TCP 套接字接收数据

头文件:

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

接口声明: ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:

        sockfd:已连接套接字

        buf:存储数据缓冲区

        len:缓冲区大小

        flags:接收标志

        MSG_OOB:接收紧急(带外)数据

返回值:

        成功:已接收字节数

        失败:-1

备注:

        当 flags 为 0 时,recv 与 read 作用相同。阻塞等待

二.代码演示 

1.一个客户端给一个服务端发送消息

①服务器

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


int main(int argc,char **argv)//主函数传参,(先传IP再传端口)argv[1]IP,ARGV[2]端口号
{
	//1.创建套接字
	int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
			//网际协议,套接字类型TCP选择流式套接字
	if(-1 == sock_fd)
	{
		perror("creat sock_fd failed");
		return -1;
	}		
	//2.绑定服务器的IP地址+端口号
	
	//定义结构体
	struct sockaddr_in serve;
	serve.sin_family = AF_INET;//地址族成员
    //0-------65535 (0----5000,大型企业,5000-----10000,留给将来的大型企业
    //10000-----65535,才是留给我们的)
	serve.sin_port =  htons(atoi(argv[2]));
    //需要把点分式字符串转换为无符号的32位网络地址in_addr_t,
	serve.sin_addr.s_addr =  inet_addr(argv[1]);
	//绑定函数
	int ret = bind(sock_fd, (struct sockaddr *)&serve, sizeof(serve));
	if(ret == -1)
	{
		perror("bind failed");
	}
	
	//3.监听
	int listen_ret = listen(sock_fd, 4);//可以设置监听的套接字的最大个数
	
	printf("等待连接....\n");
	int	acc_fd = accept(sock_fd, NULL, NULL);//阻塞等待
	printf("连接成功!!!\n");
	
	
	char *buf = malloc(100);

	//5.畅聊
	while(1)
	{
	
		memset(buf,0,100);
		//接收消息
		recv(acc_fd, buf, 100, 0);
		printf("客户端:%s\n",buf);
		if(strcmp(buf,"gun\n")==0)
		{
			break;
		}
	}
	
	close(sock_fd);
	close(acc_fd);
}

②客户端

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

int main(int argc,char **argv)
{
	//1.创建套接字
	int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
			//网际协议,套接字类型TCP选择流式套接字
	if(-1 == sock_fd)
	{
		perror("creat sock_fd failed");
		return -1;
	}		
	
	//2.绑定服务器地址
	struct sockaddr_in serve;
	serve.sin_family = AF_INET;//地址族成员
    //0-------65535 (0----5000,大型企业,5000-----10000留给将来的大型企业
    //10000-----65535,才是留给我们的)
	serve.sin_port =  htons(atoi(argv[2]));
    //需要把点分式字符串转换为无符号的32位网络地址in_addr_t,
	serve.sin_addr.s_addr =  inet_addr(argv[1]);
	//绑定客户端的地址
	/*int ret = bind(sock_fd, NULL, 0);
	if(ret == -1)
	{
		perror("bind failed");
	}*/
	
	//3.发起连接(connect)
	
	connect(sock_fd,(struct sockaddr *)&serve,sizeof(serve));
	
	char *buf = malloc(100);
	while(1)
	{
		
		memset(buf,0,100);
		fgets(buf,100,stdin);
		//4.发送消息(send)
		send(sock_fd,buf,100,0);
        //发送byebye退出程序
		if(strcmp(buf,"byebye\n")==0)
		{
			break;
		}
	}
	close(sock_fd);
	
}

2.多个客户端给一个服务器发送消息,并且服务器显示客户端的IP地址和端口号

①服务器

#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<time.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define JIESHOUSIEZ 100
#define IPPORT 50
#define SOCKET_N 4

//定义已连接套接字和通用地址的结构体
struct client_accept_fp
{
	int _accept_fp;
	struct sockaddr_in client;
};

void * FWQ(void *fp);

int main(int argc,char**argv)
{
	if(argc != 3)//判断参数是否输入正确
	{
		printf("请输入IP地址和端口号\n");
		return -1;
	}
	//创建套接字文件
	int socket_fp = socket(AF_INET, SOCK_STREAM, 0);
	
	//给通用地址的结构体赋值
	struct sockaddr_in server,client;        
	server.sin_family = AF_INET;	//地址族
	//atoi函数:将字符串转换为int型
	//htons函数:将主机的无符号短整形数转换成网络字节顺序
	server.sin_port = htons(atoi(argv[2])); //端口号  
	//inet_addr函数:将IP地址的主机字节序(节点式)转换为网络字节序
	server.sin_addr.s_addr = inet_addr(argv[1]);   //IPV4地址

	socklen_t server_size = sizeof(server);//计算server结构体的大小
	socklen_t client_size = sizeof(client);//计算client结构体的大小
	
	//绑定服务器地址
	bind(socket_fp, (struct sockaddr *)&server, server_size);
	
	//监听:可以设置监听的套接字的最大个数
	listen(socket_fp, SOCKET_N);
	
	pthread_t tid;//定义一个线程号
	
	char* buf = malloc(JIESHOUSIEZ);

	//开始聊天
	while(1)
	{
		//等待连接
		printf("等待连接......\n");
		//等待对端连接请求
		int accept_fp = accept(socket_fp, 
            (struct sockaddr *)&client, &client_size);
		//inet_ntoa函数:将IP地址的网络字节序转换为主机字节序(节点式)
		//ntohs函数:将网络字节顺序转换成主机的无符号短整形数
		printf("客户端[IP:%s PORT:%d]连接成功\n",
            inet_ntoa(client.sin_addr),
            ntohs(client.sin_port));
		
		//定义已连接套接字和通用地址的结构体变量并且赋值
		struct client_accept_fp client_fp;
		client_fp._accept_fp = accept_fp;
		client_fp.client = client;
		
		//创建线程
		pthread_create(&tid, NULL, &FWQ, (void*)(&client_fp));
	}
}

//线程执行函数
void * FWQ(void *fp)
{
	//不想改变值,不使用指针
	struct client_accept_fp client_ret = *(struct client_accept_fp *)fp;
	char* buf = malloc(JIESHOUSIEZ);

	//开始聊天
	while(1)
	{
		memset(buf,0,JIESHOUSIEZ);//清空
		//接收消息
		recv(client_ret._accept_fp, buf, JIESHOUSIEZ, 0);
		if(strlen(buf) !=0)//如果buf的有效长度为0就不打印
		printf("客户端[IP:%s PORT:%d]:%s\n",
            inet_ntoa(client_ret.client.sin_addr),
            ntohs(client_ret.client.sin_port),buf);
	}
}

②客户端

#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<time.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define JIESHOUSIEZ 100
#define IPPORT 50

int main(int argc,char**argv)
{
	if(argc != 3)
	{
		printf("请输入IP地址和端口号\n");
		return -1;
	}
	//创建套接字文件
	int socket_fp = socket(AF_INET, SOCK_STREAM, 0);
	
	//给结构体赋值
	struct sockaddr_in server;        
	server.sin_family = AF_INET;	//地址族
	server.sin_port = htons(atoi(argv[2])); //端口号
	server.sin_addr.s_addr = inet_addr(argv[1]);   //IPV4地址
	
	//绑定客户端的地址
	//bind(socket_fp, NULL, 0);

	//发送连接
	printf("正在发送连接,请稍后\n");
	int ret = connect(socket_fp, (struct sockaddr *)&server, sizeof(server));
	if(ret == -1)
	{
		perror("连接失败\n");
		return -1;
	}
	printf("连接成功\n");
		
	char* buf = malloc(JIESHOUSIEZ);
	//开始聊天
	while(1)
	{
		memset(buf,0,JIESHOUSIEZ);//清空
		fgets(buf,JIESHOUSIEZ,stdin);
		//发送消息
		send(socket_fp, buf, strlen(buf), 0);
		if(strcmp(buf,"byebye\n") == 0) break;
	}
	close(socket_fp);
	
	return 0;
}

3.一个客户端与一个服务器进行双向通信

①服务器

#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<time.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define JIESHOUSIEZ 100
#define FASONGSIZE 100

void *sendMessage(void *sm);

int main(int argc,char**argv)
{
	if(argc != 3)
	{
		printf("请输入至少两个参数");
		return -1;
	}
	//创建套接字文件
	int socket_fp = socket(AF_INET, SOCK_STREAM, 0);
	
	//给结构体赋值
	struct sockaddr_in server,client;        
	server.sin_family = AF_INET;	//地址族
	server.sin_port = htons(atoi(argv[2])); //端口号
	server.sin_addr.s_addr = inet_addr(argv[1]);   //IPV4地址
	
	socklen_t server_size = sizeof(server);
	socklen_t client_size = sizeof(client);
	
	//绑定服务器地址
	bind(socket_fp, (struct sockaddr *)&server, server_size)	;
	
	//监听:可以设置监听的套接字的最大个数
	listen(socket_fp, 4);
	
	//等待连接
	printf("等待连接......\n");
	int accept_fp = accept(socket_fp, (struct sockaddr *)&client, &client_size);
	printf("连接成功\n");
	
	//创建一个线程
	pthread_t tid;
	pthread_create(&tid,NULL,sendMessage,(void *)&accept_fp);
	
	char* r_buf = malloc(JIESHOUSIEZ);
	//开始聊天
	while(1)
	{
		memset(r_buf,0,JIESHOUSIEZ);//清空
		//接收消息
		recv(accept_fp, r_buf, JIESHOUSIEZ, 0);
		if(strlen(r_buf) !=0)
			printf("客户端:%s\n",r_buf);
	}
}

//线程执行函数
void *sendMessage(void *sm)
{
	int* socket = (int*)sm;
	char* s_buf = malloc(FASONGSIZE);
	while(1)
	{
		memset(s_buf,0,FASONGSIZE);//清空
		fgets(s_buf,FASONGSIZE,stdin);
		//发送消息
		send(*socket, s_buf, strlen(s_buf), 0);
	}
}

②客户端

#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<time.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define JIESHOUSIEZ 100
#define FASONGSIZE 100

void *receiveMessage(void *rm);

int main(int argc,char**argv)
{
	if(argc != 3)
	{
		printf("请输入至少两个参数");
		return -1;
	}
	//创建套接字文件
	int socket_fp = socket(AF_INET, SOCK_STREAM, 0);
	
	//给结构体赋值
	struct sockaddr_in server;        
	server.sin_family = AF_INET;	//地址族
	server.sin_port = htons(atoi(argv[2])); //端口号
	server.sin_addr.s_addr = inet_addr(argv[1]);   //IPV4地址
	
	socklen_t server_size = sizeof(server);
	
	//绑定客户端的地址
	//bind(socket_fp, NULL, 0);

	//发送连接
	printf("正在发送连接,请稍后\n");
	int ret = connect(socket_fp, (struct sockaddr *)&server, server_size);
	if(ret == -1)
	{
		perror("连接失败\n");
		return -1;
	}
	printf("连接成功\n");
	
	//创建一个线程
	pthread_t tid;
	pthread_create(&tid,NULL,receiveMessage,(void *)&socket_fp);
	
	char* buf = malloc(FASONGSIZE);
	//开始聊天
	while(1)
	{
		memset(buf,0,FASONGSIZE);//清空
		fgets(buf,FASONGSIZE,stdin);
		//发送消息
		send(socket_fp, buf, strlen(buf), 0);
	}
}

//线程执行函数
void *receiveMessage(void *rm)
{
	int* socket = (int*)rm;
	char* r_buf = malloc(FASONGSIZE);
	while(1)
	{
		memset(r_buf,0,JIESHOUSIEZ);//清空
		//接收消息
		recv(*socket, r_buf, JIESHOUSIEZ, 0);
		if(strlen(r_buf) !=0)
			printf("服务器:%s\n",r_buf);
	}
}

4.客户端发送普通文件内容给服务器,服务器接收文件内容,并且创建一个普通文件,将接收的文件内容写进创建的普通文件中

①服务器

#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<time.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define JIESHOUSIEZ 100
int openFile(char *path);

int main(int argc,char**argv)
{
	//创建套接字文件
	int socket_fp = socket(AF_INET, SOCK_STREAM, 0);
	int file_fd = openFile("./1.txt");
	//给结构体赋值
	struct sockaddr_in server;        
	server.sin_family = AF_INET;	//地址族
	server.sin_port = htons(atoi(argv[2])); //端口号
	server.sin_addr.s_addr = inet_addr(argv[1]);   //IPV4地址
	
	//绑定服务器地址
	bind(socket_fp, (struct sockaddr *)&server, sizeof(server));
	
	//监听:可以设置监听的套接字的最大个数
	listen(socket_fp, 4);
	
	//等待连接
	printf("等待连接......\n");
	int accept_fp = accept(socket_fp, NULL, NULL);
	printf("连接成功\n");
	
	char* buf = malloc(JIESHOUSIEZ);
	//开始聊天
	while(1)
	{
		memset(buf,0,JIESHOUSIEZ);//清空
		//接收消息
		recv(accept_fp, buf, JIESHOUSIEZ, 0);
		if(strlen(buf) !=0)
		{
			printf("%s\n",buf);
			write(file_fd,buf,strlen(buf));
		}
		else
		{
			printf("接收完毕\n");
			break;
		}
	}
	close(file_fd);
	close(accept_fp);
	return 0;
}

int openFile(char *path)
{
	int fd = open(path,O_WRONLY|O_CREAT|O_TRUNC,0777);
	if(fd == -1)
	{
		perror("普通文件打开失败!");
		return -1;
	}
	return fd;
}

②客户端

#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<time.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define JIESHOUSIEZ 100
int openFile(char *path);

int main(int argc,char**argv)
{
	//创建套接字文件
	int socket_fp = socket(AF_INET, SOCK_STREAM, 0);
	
	int file_ret = openFile(argv[3]); //打开普通文件
	
	//给结构体赋值
	struct sockaddr_in server;        
	server.sin_family = AF_INET;	//地址族
	server.sin_port = htons(atoi(argv[2])); //端口号
	server.sin_addr.s_addr = inet_addr(argv[1]);   //IPV4地址
	
	//绑定客户端的地址
	//bind(socket_fp, NULL, 0);
		
	//发送连接
	printf("正在发送连接,请稍后\n");
	int ret = connect(socket_fp, (struct sockaddr *)&server, sizeof(server));
	if(ret == -1)
	{
		perror("连接失败\n");
		return -1;
	}
	printf("连接成功\n");
		
	char* buf = malloc(JIESHOUSIEZ);
	//开始聊天
	while(1)//读取文件
	{	
		memset(buf,0,JIESHOUSIEZ);//清空
		//发送消息
		int read_ret = read(file_ret,buf,JIESHOUSIEZ-1);
		send(socket_fp, buf, strlen(buf), 0);
		if(read_ret == 0)
		{
			break;
		}
	}
	close(file_ret);
	close(socket_fp);
	return 0;
}

int openFile(char *path)
{
	int fd = open(path,O_RDONLY);
	if(fd == -1)
	{
		perror("普通文件打开失败!");
		return -1;
	}
	return fd;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值