一.复习GDB调试
链接:GDB常用
1.笔记上的基础操作
2.学习输出帧栈和局部变量的信息
---->首先是使用到的源代码
#include <stdio.h>
#include <string.h>
//用来测试bt等类似命令
void fun()
{
static int a = 1;
a++;
if(a == 10)
return ;
printf("a=%d\n",a);
fun();
}
int main()
{
fun();
return 0;
}
---->重要命令 bt,bt full,info f <编号> , frame <编号>
3. 在debug中修改变量和常亮的值
set variables a = 100 //修改常亮
4.寄存器相关
输出寄存器的值,输出寄存器的信息,按照指定格式输出(类似C语言)
5.断点补充
1. 指定文件的设置断点. 加入文件名和冒号
2. 条件断点
3. 使用示例:
b gdb_bt : fun if a >= 5
4.断点命令. 示例: command 2
print a
print a
end
6. 强制函数返回
7.多线程相关
1.查看多线程 info thread 或者 i thr
2.切换到指定线程 thr <编号>
8.在学完第5章后,学习用gdb调试信号,第26章后,用gdb调试线程 ,同时
二. strlen和sizeof的区别
1. 笔记背背,面试了用
测试用源代码
#include <stdio.h>
#include <string.h>
int
main()
{
char arr[100] = {'h','e','l','l','o','\0'};
//forcast the result
//sizeof = 100 * 1 = 100,strlen = 5
printf("sizeof(arr)=%d,strlen(arr)=%d\n",sizeof(arr),strlen(arr));
int arr1[20] = {1,2,3,4,5};
//forcast the result
//sizeof = 20 * 4, strlen = error.Answer isn't sure.
printf("sizeof(arr1)=%d,strlen(arr1)=%d\n",sizeof(arr1),strlen(arr1));
//dont have 0 in end
char arr_no_0[100] = {'h','e','l','l','o'};
printf("sizeof(arr_no_0)=%d,strlen(arr_no_0)=%d\n",sizeof(arr_no_0),strlen(arr_no_0));
const char* str = "hello world";
printf("sizeof(str)=%d,strlen(str)=%d\n",sizeof(str),strlen(str));
}
输出结果
三. 本章基础知识的学习
1.基本socket api :
socket,bind,listen,accept,connect
close,fork,exec,getsockname,getpeername
四. 修改TCP协议栈相关的连接参数
这些要会:
backlog,somaxconn,netdev_max_backlog,ip_local_port_range
socket读写buffer的4个参数,
tcp_max_tw_buckets,tcp_tw_reuse,tcp_tw_recycle
(在第6章IO复用开始时,学习/proc/net/下文件)
(在第6章开始时,学习修改内核对文件类默认参数的修改)
五. 服务器源码
从这一章开始,每一章都会基于所学的新内容,对如下服务器进行改进.其中会有5个比较重大的版本:
1.多进程服务器(第3章->第4章->第11章->第7章->最终在第5章完成)
2.多线程服务器(待定)
3.unp服务器
4.基于select的IO复用服务器
5.基于epoll的IO复用服务器.
本章源代码封装了第3章,第4章一些api,放在head.h头文件里.
client.c是客户端源代码,serv.c是服务器源代码
这是第一版,对各种错误无任何处理,边界条件毫无测试,仅仅是一个回射模型.
实现了客户发送数据到服务器,服务器按行读取,并显示在服务器这里.
head.h
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
//some universal value
#define MAXSIZE 8392
#define MAXLINE 8392
#define SERV_PORT 8888
#define LISTENQ 100
typedef struct sockaddr SA;
//wrong proceedure
void err_str(const char* str)
{
printf("<%s> is error!!\n",str);
exit(0);// !> we also exit !
}
//wrap function
//socket
int Socket(int family,int type,int protocol)
{
int sockfd;
if((sockfd = socket(family,type,protocol)) < 0)
err_str("socket");
return sockfd;
}
int Connect(int sockfd,const struct sockaddr* sa,socklen_t len)
{
int ret;
if((ret = connect(sockfd,sa,len)) < 0)
err_str("connect");
return ret;
}
int Bind(int sockfd,const struct sockaddr* sa,socklen_t len)
{
int ret;
if((ret = bind(sockfd,sa,len)) < 0)
err_str("bind");
return ret;
}
int Listen(int sockfd,int backlog)
{
int ret;
if((ret = listen(sockfd,backlog)) < 0)
err_str("listen");
return ret;
}
int Accept(int sockfd,struct sockaddr* cliaddr,socklen_t* len)
{
int connfd;
if((connfd = accept(sockfd,cliaddr,len)) < 0)
err_str("accept");
return connfd;
}
// IO
//....no buffer version
ssize_t readn(int sockfd,char* buf,size_t nbytes)
{
ssize_t nread;
size_t nleft = nbytes;
char* p = buf;
while(nbytes)
{
if((nread = read(sockfd,p,nleft)) < 0)
{
if(errno == EINTR)
nread = 0;
else
return -1;
}
else if(nread == 0)
break;
nleft -= nread;
p += nread;
}
return (nbytes - nleft);
}
ssize_t writen(int sockfd,const char* buf,size_t nbytes)
{
ssize_t nwrite;
size_t nleft = nbytes;
const char* bufp = buf;
while(nleft > 0)
{
if((nwrite = write(sockfd,bufp,nleft)) <= 0)
{
if(nwrite < 0 && errno == EINTR)
nwrite = 0;
else
return -1;
}
nleft -= nwrite;
bufp += nwrite;
}
return nbytes;
}
ssize_t readline(int sockfd,char* buf,size_t n)
{
ssize_t nread;
char* p = buf;
char c;
size_t i;
for(i = 1; i < n; ++i)
{
if((nread = read(sockfd,&c,1)) == 1)
{
*p++ = c;
if(c == '\n')
break;
}else if(nread == 0){
*p = '\0';
return (i - 1);
}else{
if(errno == EINTR)
continue;
return -1;
}
}
*p = '\0';
return i;
}
//change address
int Inet_pton(int family,const char* strptr,void* addrptr)
{
int ret;
if((ret = inet_pton(family,strptr,addrptr)) < 0)
err_str("inet_pton");
return ret;
}
//get information
int Getsockname(int sockfd,struct sockaddr* sa,socklen_t* addrlen)
{
int ret;
if((ret = getsockname(sockfd,sa,addrlen)) < 0)
err_str("getsockname");
return ret;
}
client.c
#include "head.h"
int main(int argc,char** argv)
{
if(argc < 2){
printf("Please enter IP adress.\n");
exit(0);
}
int sockfd; //client sockfd
struct sockaddr_in serv;
bzero(&serv,sizeof(serv));
//we want client enter ip address
Inet_pton(AF_INET,argv[1],&serv.sin_addr);
serv.sin_port = htons(SERV_PORT);
serv.sin_family = AF_INET;
sockfd = Socket(AF_INET,SOCK_STREAM,0);
Connect(sockfd,(const struct sockaddr*)&serv,sizeof(serv));
printf("******************************\nYou have connected!\n**********************");
// now do some echo
//demand client to enter some words
char buf[MAXSIZE];
printf("\n");
for( ; ;)
{
fgets(buf,MAXLINE,stdin);
if(( writen(sockfd,buf,strlen(buf))) < 0)
err_str("writen");
bzero(buf,sizeof(buf));
}
return 0;
}
serv.c
#include "head.h"
int main()
{
struct sockaddr_in serv,cli;
socklen_t len;
int sockfd,connfd;
char buf[MAXSIZE];
bzero(&serv ,sizeof(serv));
serv.sin_family = AF_INET;
//inet_pton(AF_INET,ADDR,(void*)&serv.sin_addr);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
serv.sin_port = htons(SERV_PORT);
sockfd = Socket(AF_INET,SOCK_STREAM,0);
Bind(sockfd,(SA* )&serv,sizeof(serv));
Listen(sockfd,LISTENQ);
printf("*********************wait for connection*************\n");
len = sizeof(cli);
connfd = accept(sockfd,(SA* )&cli,&len);
Getsockname(connfd,(SA*)&cli,&len);
printf("There is someone connected!\n");
const char* family = cli.sin_family == 6 ? "AF_INET" : "AF_INET6";
printf("SA_FIMALY = %s\n",family);
while(1)
{
if((readline(connfd,buf,MAXLINE)) > 0)
{
fputs(buf,stdout);
bzero(buf,sizeof(buf));
}
else
break;
}
return 0;
}
效果展示
六. 课后习题
4.1 判别主机序
高字节在内存的低位就是大端,也是网络字节序
看课后题答案即可.
这里给出一个判断大小端的函数,并在随后的学习中,加入项目.
#include "string.h"
#include <stdio.h>
int main()
{
int n = 0x12345678;
char t = *(char*)&n; //只取第一个字节,并随后将其按照int来解释
if((int)t == 0x12) //也可以不用"(int)", 会自动执行整形提升
printf("Big Endian!\n");
else if((int)t == 0x78)
printf("Little Endian!\n");
else
printf("Some error happend!\n");
return 0;
}
4.2 临时端口的范围
1024-65535.
4.3 子进程先于父进程的fork返回前就结束
close(fd)是减少fd的引用计数,只有当fd的引用计数为0时才会彻底的关闭,而此时才会发送FIN,执行四次回收.
于是子进程调用close,使得引用计数从2变为1,随后父进程调用close,彻底的关掉connfd,此时给客户端发送FIN.
4.4 不listen
accept直接报错,第一个sockfd不是listenfd,EINVAL
4.5 不bind
没事,内核会自动分配ip地址和端口号