网络编程之TCP简单示例

转载地址

TCP网络编程关键函数:
socket()、struct sockaddr_in、htons()、htonl()、socklen_t、bind()、listen()、accept()、recv()、connect()、send()

传输层协议:TCP协议(打电话)面向于有连接的通信方式
例题: 使用网络通信TCP协议,实现不同主机之间的通信

主机A---主机B
Jack.c    Rose.c

核心代码 Rose.c 服务器

  1. 创建未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
  1. 准备好服务器IP地址,端口号,协议 --> 通通塞到结构体中
struct sockaddr_in

struct sockaddr_in srvaddr;

srvaddr.sin_family = AF_INET;  //网际协议
srvaddr.sin_port = htons(atoi(argv[1]));  //端口号,atoi是把字符串转换成整型数
/*
        之所以需要这些函数是因为计算机数据表示存在两种字节顺序:NBO与HBO
        网络字节顺序NBO(Network Byte Order):
              按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
        主机字节顺序(HBO,Host Byte Order):
              不同的机器HBO不相同,与CPU设计有关,数据的顺序是由cpu决定的,而与操作系统无关。
              
        如 Intelx86结构下,short型数0x1234表示为34 12, int型数0x12345678表示为78 56 34 
        12如IBM power PC结构下,short型数0x1234表示为12 34, int型数0x12345678表示为12 34 
        56 78。
            由于这个原因不同体系结构的机器之间无法通信,所以要转换成一种约定的数序,也就是网络字  节顺序,其实就是如同powerpc那样的顺序 。在PC开发中有ntohl和htonl函数可以用来进行网络字节和主机字节的转换。 
    */
    
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*
IP地址,由于服务器上可能有多个网卡,也就有多个ip地址,所以该ip地址的选项为INADDR_ANY,表示:在本服务器上无论是哪个ip地址接收到数据,只要是这个端口号,服务器都会处理。
*/

INADDR_ANY  --> 接收任何地址的数据信息
/* Address to accept any incoming messages. */
#define	INADDR_ANY		((unsigned long int) 0x00000000)
  1. 把地址绑定到未连接套接字上
socklen_t就是struct sockaddr_in大小的数据类型
socklen_t len = sizeof(srvaddr);

int ret = bind(fd,(struct sockaddr*)&srvaddr,len);
  1. 设置监听套接字
listen(fd,4);   是fd的本身从未连接套接字转换监听套接字
/*
backlog参数就是控制我们的已连接队列里等待accept()取走的连接的最大数目的.注意一点,backlog与这个已排队连接的最大数目未必是完全相等的,不同的系统的实现可能不同.比如backlog=1,系统允许的实际一排队数目可能为2.
*/
  1. 坐等对方的连接
int connfd = accept(fd,(strutc sockaddr*)&cliaddr,&len);
  1. 畅聊
recv(connfd,buf,sizeof(buf),0);
  1. 断开连接
close(connfd);
close(fd);

核心代码 Jack.c 客户端

  1. 创建未连接TCP套接字
int fd = socket(AF_INET,SOCK_STREAM,0);
  1. 发起连接
int ret = connect(fd,(struct sockaddr *)&srvaddr,len);
  1. 畅聊
send(fd,buf,strlen(buf),0);

运行步骤:

 同桌Ubuntu      你的Ubuntu
Rose.c              Jack.c
192.168.0.10    192.168.0.20
50001                50001

同桌: ping 192.168.0.20
你:   ping 192.168.0.10

同桌: ./Rose 50001
你:   ./Jack 192.168.0.10 50001

例子1:

Jack.c

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04  16.04  删除这个头文件
#include <strings.h>
#include <string.h>

int main(int argc,char *argv[])  //  ./Jack 服务器IP 端口号   ./Jack 192.168.0.2 50001
{
    
    
	//1. 创建未连接TCP套接字
	int fd = socket(AF_INET,SOCK_STREAM,0);  // 必须与服务器的类型一致
	
	//2. 准备对方Rose的IP地址,端口号,协议
	struct sockaddr_in srvaddr;
	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);
	
	//3. 发起连接
	socklen_t len = sizeof(srvaddr);
	int ret = connect(fd,(struct sockaddr *)&srvaddr,len);//连接成功后,fd自身就会变成已连接套接字
	if(ret == -1)
		printf("connect error!\n");
	else	
		printf("connect ok!\n");
	
	//4. 畅聊
	char buf[50];
	while(1)
	{
    
    
		bzero(buf,50);
		fgets(buf,50,stdin);
		send(fd,buf,strlen(buf),0);
		if(strncmp(buf,"quit",4) == 0)
			break;
	}
	
	//5. 挂断
	close(fd);
	
	return 0;
}

Rose.ccp

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04  16.04  删除这个头文件
#include <strings.h>

int main(int argc,char *argv[])  //  ./Rose 50001
{
    
      
	//1. 创建一个未连接TCP套接字
	int fd = socket(AF_INET,SOCK_STREAM,0);
	
	//2. 准备好服务器的结构体变量,再进行赋值
	struct sockaddr_in srvaddr;
	
	srvaddr.sin_family = AF_INET;  //网际协议
	srvaddr.sin_port = htons(atoi(argv[1]));  //端口号   
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP地址
	
	//3. 把服务器的IP地址,协议,端口号绑定到未连接套接字上
	socklen_t len = sizeof(srvaddr);
	int ret = bind(fd,(struct sockaddr*)&srvaddr,len);
	if(ret == -1)
		printf("bind error!\n");
	
	//4. 将未连接套接字转换为监听套接字
	listen(fd,4);
	
	//5. 坐等电话
	struct sockaddr_in cliaddr; //存放来电显示
	int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len); //阻塞等待
	if(connfd == -1)
		printf("accept error!\n");
	else
		printf("connect ok!\n");
	
	//6. 畅聊
	char buf[50];
	while(1)
	{
    
    
		bzero(buf,50);
		recv(connfd,buf,sizeof(buf),0);
		printf("from client:%s",buf);
		if(strncmp(buf,"quit",4) == 0)
			break;
	}
	
	//7. 挂断电话
	close(connfd);
	close(fd);
	
	return 0;
}

例子2:tcp_chat

Jack.c

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04  16.04  删除这个头文件
#include <strings.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

void *routine(void *arg)
{
    
    
	int fd = *(int *)arg;
	char buf[50];
	
	while(1)
	{
    
    
		bzero(buf,50);
		recv(fd,buf,sizeof(buf),0);
		printf("from Rose:%s",buf);
		if(strncmp(buf,"quit",4) == 0)
		{
    
    
			exit(0);
		}	
	}
}

int main(int argc,char *argv[])  //  ./Jack 服务器IP 端口号   ./Jack 192.168.0.2 50001
{
    
    
	//1. 创建未连接TCP套接字
	int fd = socket(AF_INET,SOCK_STREAM,0);  // 必须与服务器的类型一致
	
	//2. 准备对方Rose的IP地址,端口号,协议
	struct sockaddr_in srvaddr;
	srvaddr.sin_family = AF_INET;
	srvaddr.sin_port = htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&srvaddr.sin_addr);
	
	//3. 发起连接
	socklen_t len = sizeof(srvaddr);
	int ret = connect(fd,(struct sockaddr *)&srvaddr,len);//连接成功后,fd自身就会变成已连接套接字
	if(ret == -1)
		printf("connect error!\n");
	else	
		printf("connect ok!\n");
	
	pthread_t tid;
	pthread_create(&tid,NULL,routine,(void *)&fd);
	
	//4. 畅聊
	char buf[50];
	while(1)
	{
    
    
		bzero(buf,50);
		fgets(buf,50,stdin);
		send(fd,buf,strlen(buf),0);
		if(strncmp(buf,"quit",4) == 0)
			break;
	}
	
	//5. 挂断
	close(fd);
	
	return 0;
}

Rose.c

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
#include <linux/in.h> //18.04  16.04  删除这个头文件
#include <strings.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

void *routine(void *arg)
{
    
    
	int connfd = *(int *)arg;
	char buf[50];
	
	while(1)
	{
    
    
		bzero(buf,50);
		fgets(buf,50,stdin);
		send(connfd,buf,strlen(buf),0);
		if(strncmp(buf,"quit",4) == 0)
		{
    
    
			exit(0);
		}
	}
}

int main(int argc,char *argv[])  //  ./Rose 50001
{
    
      
	//1. 创建一个未连接TCP套接字
	int fd = socket(AF_INET,SOCK_STREAM,0);
	
	//2. 准备好服务器的结构体变量,再进行赋值
	struct sockaddr_in srvaddr;
	
	srvaddr.sin_family = AF_INET;  //网际协议
	srvaddr.sin_port = htons(atoi(argv[1]));  //端口号   
	srvaddr.sin_addr.s_addr = htonl(INADDR_ANY); // IP地址
	
	//3. 把服务器的IP地址,协议,端口号绑定到未连接套接字上
	socklen_t len = sizeof(srvaddr);
	int ret = bind(fd,(struct sockaddr*)&srvaddr,len);
	if(ret == -1)
		printf("bind error!\n");
	
	//4. 将未连接套接字转换为监听套接字
	listen(fd,4);
	
	//5. 坐等电话
	struct sockaddr_in cliaddr; //存放来电显示
	int connfd = accept(fd,(struct sockaddr*)&cliaddr,&len); //阻塞等待
	if(connfd == -1)
		printf("accept error!\n");
	else
		printf("connect ok!\n");
	
	//5.5 创建线程,用于实现服务器写功能
	pthread_t tid;
	pthread_create(&tid,NULL,routine,(void *)&connfd);
	
	//6. 畅聊
	char buf[50];
	while(1)
	{
    
    
		bzero(buf,50);
		recv(connfd,buf,sizeof(buf),0);
		printf("from client:%s",buf);
		if(strncmp(buf,"quit",4) == 0)
			break;
	}
	
	//7. 挂断电话
	close(connfd);
	close(fd);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/sinat_36002055/article/details/120563086