网络编程笔记(根据遇到的问题随时更新)

函数详解

https://blog.csdn.net/seraphsky/article/details/1856191

https://www.jianshu.com/p/066d99da7cbd

可能需要的头文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>

宏定义

#define SERV_PORT 5001 必须>=5000
#define SERV_IP_ADDR “192.168.7.246”
#define BACKLOG 5
#define QUIT_STR “quit”

tcp框体之client

int main(int argv,const char * argv[])
{

int fd = -1;
struct sockaddr_in sin;
/*
优化:判断是否传入文件名、IP、端口号
if(argv != 3)
{
	usage(argv[0]);
	exit(0);
}
usage(char *s){
	printf("\n%s serv_ip serv_port\n",s);
	printf("\tserv_ip:server ip address");
	printf("\tserv_port"server port(>5000)");
}
*/

//1、建立套接字

if((fd =  socket(AF_INET,SOCK_STREAM,0)) < 0){
	perror("socket");
	exit(1);
}	

/*
AF_INET ------>IPV4
AF_INET6 ----->IPV6
SOCK_STREAM--->流式套接字,用于密码一类,可靠,效率低,用于TCP
SOCK_DGRAM---->数据报套接字,用于发送消息等,不可靠,效率高,用于UDP
*/

//2、填充消息结构体

bzero(&sin,sizeof(sin));
sin.sin_faily = AF_INET;
sin.sin_port = htons(SERV_PORT);  //本地字节序转网络字节序
sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); //将点分形式ip地址转换为2进制32位地址。
/*优化方案之端口号port:
	**改为由用户输入的端口号:**
	int port = -1;
	port = atoi(argv[2]);//将字符串转化为整形数,若为字母转化结果为0
	if(port < 5000)
	{
		printf("server port( > 5000)\n");
		exit(1);
	}
	sin.sin_port = htons(port);
优化方案之ip:
	if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr) != 1)
	//将ipv4/ipv6转换为2进制数并存入sin.sin_addr.s_addr
	{
		perror("inet_pton");
		exit(1);
	}
*/

//3、连接

if(connet(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0){
	perror("connect");
	exit(1);
}

//4、无限循环中开始办事
//4.1client最基础框架

char buf[BUFSIZ];
int ret = -1;
while(1)
{
	bzero(buf,BUFSIZ);
	if(fgets(buf,BUFSIZ-1,stdin) == NULL)
	//从键盘输入字符串存入缓存区buf中,若不成功继续执行
	{
		continue;
	}
	do{
		ret = write(fd,buf,strlen(buf));
		//将缓存区buf中数据循环写入套接字fd中
	}while(ret < 0 && EINTR == errno);
	if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
	{
		printf("CLIENT EXTIING\N");
		break; //跳出循环
	}
}

//4.2client端多进程并发
同4.1
//4.3client端多线程并发
同4.1
//4.4client端http

#define HTTP_RCV_LEN 50000
const char getstr[] = "GET /index.html HTTP/1.1\r\n"
"Host:www.baidu.com\r\n"
"\r\n";
struct hostent *phostent;
char *http_msg;
char *ipaddr = NULL;
if((phostent = gethostbyname("www.baidu.com")) == NULL)
{
	perror("gethostbyname");
	exit(1);
}else{
	for(i=0;phostent->h_addr_list[i];i++)
	{
		ipaddr = inet_ntoa(*(struct in_addr*)(phostent->h_addr_list[i]));
		if(ipaddr != NULL)
			break;
	}
	if(ipaddr == NULL)
	{
		exit(1);
	}
}
http_msg = zalloc(HTTP_RCV_LEN);
send(fd,getstr,strlen(getstr),0);
int ret = -1;
while(1)
{
	do
	{
		ret = recv(fd,http_msg,50000,0);
		if(ret > 0)
		{
			printf("%s",http_msg);
		}else{
			printf(" no http server data");
		}
	}(ret = -1);
}
free(htttp_msg);

//4.5io多路复用client端:用于处理阻塞问题
具体方法为先构造一张描述符集合的表,将多个文件描述符加入其中,调用函数,函数返回值表示就绪。

int ret = -1;
fd_set rset;	//文件描述符集合名称
int maxfd = -1;	//文件描述符个数
struct timeval tout;	//超时设置。NULL:阻塞至就绪或出错;0:仅检测状态,立即返回;!=0:指定时间内无事发生便返回/
char buf[BUFSIZ];
while(1)
{
	FD_ZERO(&rset);	//清空集合
	FD_SET(0,&rset);	//加入标准输入流
	FD_SET(fd,&rset);	//加入套接字fd
	maxfd = fd;	//一个时候为3,因为有标准输入输出报错012
	tout.tv_sec = 5;	//秒
	tou.tv_usec = 0;	//微妙
	select(maxfd+1,&rset,NULL,NULL,&tout);
	if(FD_ISSET(0,&rset))	//若是标准键盘输入
	{
		bzero(buf,BUFSIZ);
		do{
			ret = read(0,buf,BUFSIZ -1);
		}while(ret < 0 && EINTR == errno);
		if(!ret)
			continue;
		if(ret < 0)
		{
			perror("read");
			continue;
		}
		if(write(fd,buf,strlen(buf)) < 0){
			perror("write");
			continue;
		}
		if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
		{
			printf("client is exiting\n")
			break;
		}
	}
	if(FD_ISSET(fd,&rset))	//若是客户端发的数据
	{
		bzero(buf,BUFSIZ);
		do{
			ret = read(fd,buf,BUFSIZ-1);
		}while(ret < 0 && EINTR == errno);
		if(ret < 0){
			perror("read");
			continue;
		}
		if(!ret)
			break;
		printf("server said:%s\n",buf);
	}
	
}

//5、关闭套接字

close(fd);

}

tcp框体之server

int main(int argc,const char *argv[])
{

/*优化:避免阻塞
signal(SIGCHLD,sig_child_handle);//非阻塞等待子进程结束回收,SIGCHLD:子进程状态发生变化产生该信号
void sig_child_handle(int signo)
{
	if(signo == SIGCHLD){
		waitpit(-1,NULL,WNOHANG);
	}
}

*/	

//1、建立套接字

if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0){
	perror("socket");
	exit(1);
}
/*优化:允许绑定地址快速重用
 int b_reause = 1; 
 setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
*/设置与套接字相关选项的函数,总会出现在最上层套接字层

//2、填充sin消息结构

bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
sin.sin_addr.s_addr = htonl(INADDR_ANY);//让服务器可绑定任意ip
/*sin.sin_addr.s_addr的另一种表示
if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr) != 1)
{
	perror("inet_pton");
	exit(1);
}
*/

//3、将本地消息结构与套接字建立联系

if(bind(fd,(struct sockaddr *)&sin.sizeof(sin)) < 0)
{
	perror("bind");
	exit(1);
}	

//4、主动套接字变被动套接字

if(listen(fd,BACKLOG) < 0)
//BACKLOG:请求队列长度,表示能存放多少个客户端。
{
	perror("listen");
	exit(1);
}

//5、连接并在无限循环中处理数据
//5.1server最基础框架

int newfd;
newfd = accept(fd,NULL,NULL);//继承fd
if(newfd < 0){
	perror("accept");
	exit(1);
}
int ret = -1;
char buf[BUFSIZ];
while(1)
{
	bzero(buf,BUFSIZ);
	do{
		ret = read(newfd,buf,BUFSIZ-1);
	}while(ret < 0&& EINTR == errno);
	if(ret < 0){
		perror("read");
		exit(1);
	}
	if(!ret){   //表示对方已经关闭,则直接退出循环
		break;
	}
	printf("receive data :%s\n",buf);
	if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
	{
		printf("client is exting \n");
		break;
	}		
}
clise(newfd); //推出循环后关闭newfd

//5.2server端多进程并发

int newfd = -1;
struct sockaddr_in cin;//存放客户端消息结构
socklen_t addrlen = sizeof(cin);
while(1){
	pid_t pid = -1;
	if((newfd = accept(fd,(struct sockaddr *)&cin,&addrlen)) < 0)//继承fd,在cin中存放客户端信息,个人理解为改名
	{
		perror("accept");
		exit(1);
	}
	//创建一个子进程用于处理已经建立连接的客户的交互数据
	if((pid = fork()) < 0)
	{
		perror("fork");
		break;
	}
	if(pid > 0) //父进程关闭newfd
	{
		close(newfd);
	}
	if(pid == 0) //子进程关闭fd
	{
		clise(fd);
		char ipv4_addr[16];//显示接收到的客户端的IP&端口号
		if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin))) //32位地址转化为点分形式ip存入ipv4_addr缓存区
		{
			perror("inet_ntop");
			exit(1)				
		}
		printf("client(%s,%d)is connected\n",ipv4_addr,ntoh(cin.sin_port));
		**cli_data_handle(&newfd);**//具体操作函数
		return 0;
	}
}

//5.3server端多线程并发

while(1)
{	
	if((newfd = accept(fd,(struct sockaddr *)&cin,&addrlen)) < 0)
	{
		perror("accept");
		exit(1);
	}
	char ipv4_addr[16];
	if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
	{
		perror("inet_ntop");
		exit(1);
	}
	printf("client(%s:%d) is connected\n",ipv4_addr,htons(cin.sin_port));
	**pthread_create(&tid,NULL,(void *)cli_data_handle,(void *)&newfd);**
}

/*客户端数据处理函数

void cli_data_handle(void *arg)
{	
	int newfd = *(int *)arg;
	printf("child process:newfd = %d\n",newfd);
	/*同5.1  while(1)*/
	close(newfd);
}

*/
//5.4server端http

int newfd = -1;
int ret = -1;
struct sockaddr cin;
socklen_t cin_len = sizeof(struct sockaddr);
char *http_msg;
char *send_msg;
http_msg = (char *)zalloc(sizeof(char)*1000);

const char *default_page = 
"<html>"
"<head>"
"<meta http-equiv=\"Content-Type\"content=\"text/html;charset = utf-8\"/>"
"<title></title>"
"/head"
"<body>"
"<p>1234556</p>"
"</body>"
"</thml>"	

while(1)
{
	if((newfd = accept(fd,(struct sockaddr *)&cin,&addrlen)) < 0)
	{
	   perror("accept");
		exit(1);
	}
	ret = recv(newfd,http_msg,1000,0);
	if(ret > 0)
	{
		printf("http_client recv\n");
		printf("%s\n",http_msg);
		**file_ok(newfd,strlen(default_page));**
		send(newfd,default_page,strlen(default_page), 0 );
	}else{
		printf("no data\0");
	}

}

int file_ok(int newfd , long flen)
{
	char *send_buf = zalloc(sizeof(char)*100);
	sprintf(send_buf,"HTTP/1.1 200 OK\r\n");
	send(newfd,send_buf,strlen(send_buf),0);
	sprintf(send_buf,"Connection:keeo-alive\r\n");
	send(newfd,send_buf,strlen(send_buf),0);
	sprintf(send_buf,"Content-Length:%ld\r\n",flen);
	send(newfd,send_buf,strlen(send_buf),0);
	sprintf(send_buf,"Content-Type:text/html\r\n");
	send(newfd,send_buf,strlen(send_buf),0);
	sprintf(send_buf,"\r\n");
	send(newfd,send_buf,strlen(send_buf),0);
	free(send_buf);
	return 0;
}	

//5.5 io多路复用server端:没有变化,正常处理数据
同5.2

//6、关闭套接字

close(fd);
return 0;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习MATLAB时,编写学习笔记是一种很好的方法,可以帮助您记录重要的概念、语法和技巧,以及解决问题的步骤。以下是一些关于编写MATLAB学习笔记的建议: 1. 组织结构:使用清晰的标题和分节来组织您的学习笔记。将不同的主题分成单独的部分,并使用子标题来进一步细分内容。 2. 内容概要:在每个部分或子标题下,写下该主题的概要。这样可以帮助您回顾和理解该主题的关键点。 3. 示例代码和输出:对于每个主题,包括一些示例代码和相应的输出结果。这有助于您理解和演示具体的MATLAB语法和功能。 4. 问题与解答:如果您在学习过程中遇到了一些困惑或问题,将其记录下来,并在笔记中提出并解答这些问题。这样可以帮助您深入思考并加深对该主题的理解。 5. 笔记补充:除了基本概念和语法外,您还可以添加一些额外的笔记,如最佳实践、编程技巧、常见错误等。这些额外的笔记可以提供更多的实用信息和提示。 6. 参考资料:在您的学习笔记中,包括引用的参考资料,如教程、文档、书籍或网站链接。这样,您可以随时回顾并深入研究相关的主题。 7. 总结和复习:在学习笔记的结尾,添加一个总结部分,回顾和总结您学到的重点和关键概念。此外,定期复习您的学习笔记也是加深理解和记忆的好方法。 以上是关于编写MATLAB学习笔记的一些建议。希望这对您有所帮助,并祝您在MATLAB学习过程中取得成功!如果有任何其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值