zoukankan      html  css  js  c++  java
  • Linux网络编程——TCP和UDP通信

    • TCP协议流程图、TCP建立即时聊天
    • TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天
    • UDP协议流程图、UDP建立即时连接
    • 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)需调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket,但也可以直接用setsockopt和reuse。
    • SO_RCVLOWAT设置接收缓冲区下限

    1、TCP协议的流程图

    服务端:socket---bind---listen---while(1){---accept---recv---send---close---}---close
    客户端:socket----------------------------------connect---send---recv-----------------close

    1556806258606

    TCP建立即时聊天

    tcp_client.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd=socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd,-1,"socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family=AF_INET;
    	ser.sin_port=htons(atoi(argv[2]));
    	ser.sin_addr.s_addr=inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret=connect(socketFd,(struct sockaddr*)&ser,sizeof(ser));
    	ERROR_CHECK(ret, -1, "connect");
    	printf("connect success
    ");
    	char buf[128]={0};
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(socketFd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(socketFd, buf ,strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(socketFd);
    }
    

    tcp_server.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	char buf[128] = {0};
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(new_fd, &rdset);
    		ret = select(new_fd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    2、TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天

    tcp_server.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	char buf[128] = {0};
    	fd_set rdset; 
    	fd_set needMonitorSet; //需要监听的描述符集合
    	FD_ZERO(&needMonitorSet);  
    	FD_SET(STDIN_FILENO, &needMonitorSet);
    	FD_SET(socketFd, &needMonitorSet);
    	while(1){
    		memcpy(&rdset, &needMonitorSet, sizeof(fd_set));
    		ret = select(11, &rdset, NULL, NULL, NULL); //设最大监控描述符为10
    		if(FD_ISSET(socketFd, &rdset)){ //如果监听到客户端则accept接受远程计算机的连接请求
    			new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen); //accept函数接受一个连接时,会返回一个新的socket标识符,以后数据的数据传输和读取就要通过这个新的socket编号来处理,原来的socket继续监听其他客户机的连接请求。
    			ERROR_CHECK(new_fd, -1, "accept");
    			printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    			FD_SET(new_fd, &needMonitorSet);	
    		}
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				FD_CLR(new_fd, &needMonitorSet);  //从needMonitorSet中删除new_fd
    				close(new_fd);
    				continue;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c和1同

    3、使用UDP协议的流程图

    服务端:socket---bind---recvfrom---sendto---close
    客户端:socket----------sendto---recvfrom---close

    1556806183309

    • sendto()函数原型:
      **int sendto(int sockfd, const void msg,int len,unsigned int flags,const struct sockaddr to, int tolen);

      该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。

    • recvfrom()函数原型:

      **int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr from,int fromlen);
      from是一个struct sockaddr类型的变量,该变量保存连接机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。

      UDP传输一个数据报示例:

      udp_server.c

    #include <func.h>
    
    int main(int argc, char **argv){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	char buf[128] = {0};
    	struct sockaddr_in client;
    	int addrlen = sizeof(client);
    	ret = recvfrom(socketFd, buf, 5, 0, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(ret, -1, "recvfrom");
    	printf("client ip = %s, port = %d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	printf("udp server gets %s
    ", buf);
    	//bzero(buf, sizeof(buf));
    	//ret = recvfrom(socketFd, buf, 5, 0, (struct sockaddr*)&client, &addrlen);
    	//ERROR_CHECK(ret, -1, "recvfrom");
    	//printf("udp server gets %s
    ", buf);
    	ret = sendto(socketFd, "world", 5, 0, (struct sockaddr*)&client, sizeof(client));
    	ERROR_CHECK(ret, -1, "sendto");
    	close(socketFd);
    	return 0;
    }
    

    udp_client.c

    #include <func.h>
    
    int main(int argc, char *argv[]){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);
    	int ret;
    	ret = sendto(socketFd, "helloworld", 10, 0, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "sendto");
    	char buf[128] = {0};
    	ret = recvfrom(socketFd, buf, sizeof(buf), 0, NULL, NULL);
    	ERROR_CHECK(ret, -1, "recvfrom");
    	printf("udp client gets %s
    ", buf);
    	close(socketFd);
    	return 0;
    }
    

    4、使用UDP协议建立即时连接

    udp_server.c

    #include <func.h>
    
    int main(int argc, char **argv){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	char buf[128] = {0};
    	struct sockaddr_in client;
    	int addrlen = sizeof(client);
    	printf("client ip = %s, port = %d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recvfrom(socketFd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &addrlen);
    			ERROR_CHECK(ret, -1, "recvfrom");
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(0 == ret){
    				printf("byeybye
    ");
    				break;
    			}
    			ret = sendto(socketFd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&client, sizeof(client));
    			ERROR_CHECK(ret, -1, "sendto");
    		}
    	}
    	close(socketFd);
    	return 0;
    }
    

    udp_client.c

    #include <func.h>
    
    int main(int argc, char *argv[]){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);
    	int ret;
    	char buf[128] = {0};
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recvfrom(socketFd, buf, sizeof(buf), 0, NULL, NULL);
    			ERROR_CHECK(ret, -1, "recvfrom");
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(0 == ret){
    				printf("byeybye
    ");
    				break;
    			}
    			ret = sendto(socketFd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&ser, sizeof(ser));
    			ERROR_CHECK(ret, -1, "sendto");
    		}
    	}
    	close(socketFd);
    	return 0;
    }
    

    5、如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)需调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket,但也可以直接用setsockopt和reuse。

    int reuse=1;
    setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&reuse,sizeof(int));
    

    tcp_server.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	int ret;
    	int reuse = 1;
    	ret = setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    	ERROR_CHECK(ret, -1, "setsockopt");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	char buf[128] = {0};
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(new_fd, &rdset);
    		ret = select(new_fd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c和1同

    6、SO_RCVLOWAT:接收缓冲区下限

    tcp_server_rcvlowat.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	int ret;
    	int reuse = 1;
    	ret = setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    	ERROR_CHECK(ret, -1, "setsockopt");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	char buf[128] = {0};
    	int rcvLowAt = 10;
    	ret=setsockopt(new_fd,SOL_SOCKET,SO_RCVLOWAT,&rcvLowAt,sizeof(int));
    
    	ERROR_CHECK(ret, -1, "setsockopt");
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(new_fd, &rdset);
    		ret = select(new_fd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c和1同

  • 相关阅读:
    [SQL]数据更新
    Hadoop之mapred
    hadoop之hdfs
    HTML —— 11.25日总结
    HTML —— 11.19日的总结
    三个爬虫的小栗子
    HTML —— video标签
    py爬虫 —— 三个爬虫的小栗子
    HTML —— img标签
    py爬虫 —— py爬虫requests
  • 原文地址:https://www.cnblogs.com/Mered1th/p/10803634.html
Copyright © 2011-2022 走看看