第6课.网络编程

1.TCP和UDP的区别

(1)TCP是面向连接的协议,UDP是面向无连接的协议。
(2)TCP对系统资源要求较多,UDP对系统资源要求较少。
(3)TCP是数据流模式,UDP是数据报模式。
(4)TCP保证数据顺序及数据的正确性,UDP可能会丢包。

2.简述TCP/UDP服务器端创建流程与客户端创建流程

TCP服务器端创建流程:

创建通信用文件描述符(socket)-->设置端口号和IP地址(为绑定做准备)-->绑定(bind)-->监听(listen)-->接受请求,建立连接(accept)-->发送与接收消息(send/recv)-->关闭文件(close)

TCP客户端创建流程:

创建通信用文件描述符(socket)-->设置端口号和IP地址-->发起连接请求(connect)-->接受与发送消息(send/recv)-->关闭文件(close)

UDP服务器端创建流程:

创建通信用文件描述符(socket)-->设置端口号和IP地址(为绑定做准备)-->绑定(bind)-->接受和发送消息(sendto && recvfrom)-->关闭文件(close)

UDP客户端创建流程:

创建通信用文件描述符(socket)-->设置端口号和IP地址-->接受与发送消息(sendto && recvfrom)-->关闭文件

3.函数解析

socket函数

获得一个文件描述符(文件句柄)。为了执行网络输入输出,一个进程必须做的一件事就是调用socket函数获得一个文件描述符(文件句柄)
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
成功:return 费负描述符        失败:-1
    domain: 指明协议簇,目前支持5中协议簇,最常用的有AF_INET (IPv4协议)和AF_INET6 (IPv6协议)
    type  : 指明套接口类型,有3种类型可选:
                SOCK_STREAM:字节流套接口
                SOCK_DGRAM :数据报套接口
                SOCK_RAW   :原始套接口;如果套接口不是原始套接口,那么第三个参数就为0

bind函数

把文件句柄和IP,端口捆绑在一起。为套接口分配一个本地IP和协议端口,对于网络协议,协议地址是32位IPv4或128位IPv6地址与16位的TCP/UDP端口的组合。如指定一个通用的通配端口为0,调用bind时内核将选择一个临时端口。如果指定一个通用通配IP地址,则要等建立连接后内核才能选择一个本地IP地址。
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
成功:0                        失败:-1
    sockfd :文件句柄
    addr   :指向特定协议的地址结构的指针
        这里不使用sockaddr这个结构体,而是使用下面这个结构体
        struct sockaddr_in { 
            short int sin_family;             /* 地址族,AF_xxx 在socket编程中只能是AF_INET */ 
            unsigned short int sin_port;      /* 端口号 (使用网络字节顺序) */ 
            struct in_addr sin_addr;          /* 存储IP地址 4字节 */ 
            unsigned char sin_zero[8];        /* 总共8个字节,实际上没有什么用,只是为了和struct sockaddr保持一样的长度,设为0就好 */ 
        }; 
    addrlen:第二个参数结构体的长度

listen函数

监听。listen函数仅被TCP服务器调用,它的作用是将用socket创建的主动套接口转换成被动套接口,并等待来自客户端的连接请求
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int listen(int sockfd, int backlog);
成功:0                        失败:-1
    sockfd :文件句柄
    backlog:最多可以一次监听多少路。这个参数规定了内核为此套接口排队的最大连接个数。由于listen函数第二个参数的原因。内核要维护两个队列。以完成连接队列和未完成连接队列。未完成队列中存放的是TCP连接的三路握手未完成的连接。accept函数是从以连接队列中取连接返回给进程,当已连接队列为空时,进程将进入睡眠状态。

accept函数

等待连接。accept函数由TCP服务器调用,从已完成连接队列头返回一个已完成连接,如果完成连接队列为空则进程进入睡眠状态
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
成功:非负描述符                失败:-1
    sockfd :文件句柄
    addr   :接收到的数据存储在这里。
    addrlen:同bind函数

connect函数

建立联系。当用connect建立了套接口后可以用connect为这个套接口指明远程端地址。如果字节流套接口(TCP),connect就使用三次握手建立一个链接。如果是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据区握手。
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
成功:0                        失败:-1
sockfd :客户端文件句柄
addr   :见bind函数
addrlen:同上

send函数

用来发送数据
#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
成功:返回写出的字数            失败:-1
sockfd:文件句柄。对于服务器是accept函数返回的已连接的文件句柄。对于客户端是调研员socket函数返回的文件句柄
buf   :指向一个用于发送信息的数据缓冲区
len   :指明传送数据的大小
flags :传输标志

sendto函数

用于无连接(没有connect函数)的数据报,socket方式下进行传输。由于本地socket并没有与远端服务器建立联系,所以在发送数据时应指明目的地址
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
成功:返回写出的字数            失败:-1
dest_addr:用法和connect中一样
addrlen  :同上

recv函数

用来接收数据
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
成功:返回读入的字节数            失败:-1
参数同上

recvfrom函数

简介同sendto
#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                    struct sockaddr *src_addr, socklen_t *addrlen);
参数同sendto

close函数

结束传输。当所有数据操作结束后,你可以调用close函数来释放socket,从而停止改socket上的任何操作
#include <unistd.h>

int close(int fd);
你也可以调用shutdown()函数来关闭socket。该函数允许你至停止在某个方向上的数据传输,而另一个方向上的数据传输继续进行。如你可以关闭socket的写操作而允许改socket继续接受数据直到读入所以数据。
int shutdown(int sockfb, int how);
成功:0                        失败:-1
sockfb:需要关闭的socket的文件句柄。
how   :允许shutdown操作选择
        0:不允许继续接收操作
        1:不允许继续发送操作
        3:不允许继续发送和接收操作

4.代码解析

TCP

server.c

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.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

int main(int argc, char **argv)
{
	int iSocketServer;
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	struct sockaddr_in tSocketClientAddr;
	int iRet;
	int iAddrLen;

	int iRecvLen;
	unsigned char ucRecvBuf[1000];

	int iClientNum = -1;

	signal(SIGCHLD,SIG_IGN);	//防止子进程僵死

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

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	memset(tSocketServerAddr.sin_zero, 0, 8);

	iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
	if (-1 == iRet)
	{
		printf("bind error!\n");
		return -1;
	}

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

	while (1)
	{
		iAddrLen = sizeof(struct sockaddr);
		iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
		if (-1 != iSocketClient)
		{
			iClientNum++;
			printf("Get connect from client %d : %s\n",  iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
			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);
					}
				}				
			}
		}
	}
	
	close(iSocketServer);
	return 0;

}

client.c

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


/*	socket
 *	connect
 *	send/recv
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	
	int iRet;
	unsigned char ucSendBuf[1000];
	int iSendLen;

	if (argc != 2)
	{
		printf("Usage:\n");
		printf("%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, short */
	if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
 	{
		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)
			{
				close(iSocketClient);
				return -1;
			}
		}
	}
	
	return 0;
}

UDP

server.c

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

/* socket
 * bind
 * sendto/recvfrom
 */

#define SERVER_PORT 8888
 
int main(int argc, char **argv)
{
	int iSocketServer;
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	struct sockaddr_in tSocketClientAddr;
	int iRet;
	int iAddrLen;

	int iRecvLen;
	unsigned char ucRecvBuf[1000];

	int iClientNum = -1;

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

	tSocketServerAddr.sin_family	   = AF_INET;
	tSocketServerAddr.sin_port 	   = htons(SERVER_PORT);  /* host to net, short */
	tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	memset(tSocketServerAddr.sin_zero, 0, 8);

	iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
	if (-1 == iRet)
	{
		printf("bind error!\n");
		return -1;
	}


	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);
		}
	}

	close(iSocketServer);
	return 0;
}

client.c

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

/* socket
 * connect
 * send/recv
 */

#define SERVER_PORT 8888

int main(int argc, char **argv)
{
	int iSocketClient;
	struct sockaddr_in tSocketServerAddr;
	
	int iRet;
	unsigned char ucSendBuf[1000];
	int iSendLen;

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

	iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);

	tSocketServerAddr.sin_family      = AF_INET;
	tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
	//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
	{
		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)
			{
				close(iSocketClient);
				return -1;
			}
		}
	}
	
	return 0;
}
posted @ 2020-02-25 15:52  人民广场的二道贩子  阅读(221)  评论(0编辑  收藏  举报