Linux----TCP/UDP编程

一.TCP-----服务器端

在TCP编程中,client端涉及以下五个过程:

  •        socket            
  •        bind                     绑定
  •        listen                   监听
  •        accept                 等待连接
  •        send/recv            收发数据

1. 首先使用socket()函数创建一个套接字,在socket()函数中不涉及,任何三大元素(源,端口,地址)

2.然后使用bind()函数,将绑定的端口和地址进行绑定。代码如下:

tSocketServerAddr.sin_family      = AF_INET;
tSocketServerAddr.sin_port        = htons(SERVER_PORT);
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0 , 8 );
	
iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr,
                sizeof(struct sockaddr));

 此处我们将sin_addr 设置为 INADDR_ANY 表示可以和任何的主机通信。

3.  使用listen()函数开启数据监听,宣告服务器可以接受连接请求。

#define    BACKLOG    10
iRet = listen(iSocketSever, BACKLOG);
if(iRet == -1)
{
	printf("listen error!\n");
	return -1;
}

 BACKLOG表示最多可以监听10路连接。

4. 然后使用accept()函数,获得连接请求,并且建立连接。

5. 最后使用send()/recv()函数进行数据接收和发送。

    将接收到的数据打印在终端显示处理,此处我们整一个高级的操作。当有一个新的链路连接时,我们就使用fork()函数创建子进程,我们就可以实现打开多个进程。代码如下:

while(1){
		iAddrLen = sizeof(struct sockaddr);
		iSocketClient = accept(iSocketSever, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
		if(iSocketClient != -1)
		{
			++iClientNum;
			printf("Get connect From client %d : %s\n",iClientNum , inet_ntoa(tSocketClientAddr.sin_addr));

			/*执行到fork,会复制子进程*/
			/*子执行fork()==0,父进程执行fork()!=0内的语句*/
			if(!fork())
			{
				/*子进程源码*/
				while(1){
					/*接口客户端发来的数据并打印*/
					iRecvLen = recv(iSocketClient , ucRecvBuf , 999 , 0);
					if(iRecvLen <= 0 ){
						close(iSocketClient);
						return -1;
					}
					else {
						ucRecvBuf[iRecvLen] = '\0';
						printf("Get Msg From Client %d : %s\n",iClientNum , ucRecvBuf);
					}
				}
			}

			/*父进程*/
			//else{}
		}
	}

二.TCP-----client端

在TCP编程中,client端涉及以下三个过程:

  •          socket            
  •          connect               建立连接
  •          send/recv            收发数据

1.首先使用socket()函数创建一个套接字

2.然后使用connect()函数建立一个连接,在 connect 中所指定的地址是想与之通信的服务器的地址。

        我们使用结构体sockaddr_in封装端口,地址等。

#define SERVER_PORT 8888 //绑定的端口
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family      = AF_INET;
tSocketServerAddr.sin_port        = htons(SERVER_PORT);
if(inet_aton(argv[1], &tSocketServerAddr.sin_addr) == 0)
{
		printf("invalid server_ip\n");
		return -1;
}
memset(tSocketServerAddr.sin_zero , 0 , 8 );
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr,
				sizeof(struct sockaddr));

        在给sin_port赋值时要使用htons()函数将端口转换为网络字节序。htons全称host to net shorted。与此同时,由于我没有将程序的目标地址写死,所以还需要将运行程序时以字符串形式输入的IP地址,使用inet_aton()函数转换为二进制形式的 IPv4 地址。

3.最后在while循环中通过按键输入,传输数据。

while(1)
	{
		if(fgets(ucSendBuf, 999 , stdin))
		{
			iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
			if(iSendLen <= 0)
			{
				printf("Send error!\n");
			}
		}
	}

三. TCP编程总结流程图

四. UDP----服务器端

        相较于TCP传输,服务器端不需要监听和等待连接。只需要将TCP编程中与listen()和accept()相关的删除即可。只需要将接受和发送的代码进行修改。如下:

while (1)
{
	iAddrLen = sizeof(struct sockaddr);
	iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, 
                    (struct sockaddr*)&tSocketClientAddr, &iAddrLen);
    if (iRecvLen > 0)
    {
        ucRecvBuf[iRecvLen] = '\0';
        printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
	}
}

五. UDP----client端

        相较于TCP传输,UDP的客户端传输更加简单,可以只需要一个socket()函数,然后进行发送和接收,也可以进行connect()后,再进行数据传输。但是connect()实则是一个虚假的连接,只是将目标地址和端口封装。使用sendto()和recvfrom()函数中已经封装了目标地址和端口。

while (1)
	{
		if (fgets(ucSendBuf, 999, stdin))
		{
			iAddrLen = sizeof(struct sockaddr);
			iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0,
			                      (const struct sockaddr *)&tSocketServerAddr, iAddrLen);
			if (iSendLen <= 0)
			{
				close(iSocketClient);
				return -1;
			}
		}
	}

六. UDP编程总结流程图

七. TCP附录代码

1. server.c

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

/*socket			
 *bind				绑定
 *listen			监听
 *accept			等待连接
 *send/recv			收发数据
 */

#define SERVER_PORT 8888 //绑定的端口
#define BACKLOG     10  //监听10路连接

int main(int argc, char **argv)

{
	int iSocketSever;
	struct sockaddr_in tSocketServerAddr;
	struct sockaddr_in tSocketClientAddr;//客户端地址信息
	int iSocketClient;
	int iRet;
	int iAddrLen;//目标地址长度
	unsigned char ucRecvBuf[1000];//数据接收缓冲区
	int iRecvLen ;//接收数据的长度

	int iClientNum = -1;//查看是来自哪个客户端

	signal(SIGCHLD, SIG_IGN);

	

	iSocketSever = socket(AF_INET, SOCK_STREAM , 0);
	if( iSocketSever == -1)
	{
		printf("socket error!\n");
		return -1;
	}

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);//host to net shorted 将端口8888转换为网络字节序
	tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	memset(tSocketServerAddr.sin_zero , 0 , 8 );
	
	iRet = bind(iSocketSever, (const struct sockaddr *)&tSocketServerAddr,
                sizeof(struct sockaddr));

	if(iRet == -1)
	{
		printf("bind error!\n");
		return -1;
	}


	iRet = listen(iSocketSever, BACKLOG);
	if(iRet == -1)
	{
		printf("listen error!\n");
		return -1;
	}

	while(1){
		iAddrLen = sizeof(struct sockaddr);
		iSocketClient = accept(iSocketSever, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
		if(iSocketClient != -1)
		{
			++iClientNum;
			printf("Get connect From client %d : %s\n",iClientNum , inet_ntoa(tSocketClientAddr.sin_addr));

			/*执行到fork,会复制子进程*/
			/*子执行fork()==0,父进程执行fork()!=0内的语句*/
			if(!fork())
			{
				/*子进程源码*/
				while(1){
					/*接口客户端发来的数据并打印*/
					iRecvLen = recv(iSocketClient , ucRecvBuf , 999 , 0);
					if(iRecvLen <= 0 ){
						close(iSocketClient);
						return -1;
					}
					else {
						ucRecvBuf[iRecvLen] = '\0';
						printf("Get Msg From Client %d : %s\n",iClientNum , ucRecvBuf);
					}
				}
			}

			/*父进程*/
			//else{}
		}
	}

	close(iSocketSever);
	return 0;
}

2. client.c


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

/*socket			
 *connect	 		建立连接
 *send/recv			收发数据
 */

#define SERVER_PORT 8888 //绑定的端口
#define BACKLOG     10  //监听10路连接

int main(int argc, char **argv)

{
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	
	int iRet;
	unsigned char ucSendBuf[1000];//发送缓冲区、
	int iSendLen;//发送数据的长度

	if(argc != 2)
	{
		printf("Usager : %s<server_ip>\n",argv[0]);
		return -1;
	}
	iSocketClient = socket(AF_INET, SOCK_STREAM, 0);

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);//host to net shorted 将端口8888转换为网络字节序
	//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	if(inet_aton(argv[1], &tSocketServerAddr.sin_addr) == 0)
	{
		printf("invalid server_ip\n");
		return -1;
	}
	memset(tSocketServerAddr.sin_zero , 0 , 8 );
	
	iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr,
				sizeof(struct sockaddr));
	if(-1 == iRet)
	{
		printf("connect error!\n");
		return -1;
	}

	while(1)
	{
		if(fgets(ucSendBuf, 999 , stdin))
		{
			iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
			if(iSendLen <= 0)
			{
				printf("Send error!\n");
			}
		}
	}
	return 0;
}

tips:关于各个函数的使用和相关作用,以及参数可以参考Linux---网络通信相关函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值