struct sembuf
{
int sem_num;//下標
int sem_op;
int sem_flg;//建議為0.
}一.信號量(同步) 1.回顧: 一個進程控制另外一個進程. 邏輯變量+pause/sleep+信號 2.信號量(semaphore)信號燈 三個數據:紅燈/綠燈/黃燈 60 90 10 信號量是共享內存整數數組.根據需要定義指定的數組長度 信號量就是根據數組中的值,決定阻塞還是解除阻塞 3.編程 3.1.創建或者得到信號量 semget 3.2.初始化信號量中指定下標的值 semctl 3.3.根據信號量阻塞或者解除阻塞 semop 3.4.刪除信號量 semctl案例: A: B 創建信號量 得到信號量 初始化信號量 根據信號量阻塞 解除阻塞 刪除信號量 semget函數說明
int semget(key_t key,
int nums,//信號量數組個數
int flags);//信號量的創建標記
//創建IPC_CREAT|IPC_EXCL|0666
//打開0
返回: -1:失敗 >=0:成功返回信號量的IDint semop(
int semid,//信號量ID
struct sembuf *op,//對信號量的操作.操作可以是數組多個
size_t nums,//第二個參數的個數
);
返回: -1:時失敗 0:成功 int semctl(
int semid,
int nums,
//對IPC_RMID無意義
int cmd,
//SETVAL IPC_RMID

);
//對IPC_RMID無意義 sem_op: 前提條件信號量是unsigned short int; 不能<0. -:夠減,則semop馬上返回,不夠減,則阻塞. +:執行+操作 0:判定信號量>0,則阻塞,直到為0 控制進程的搭配方式: +(解除阻塞) -(阻塞) 0(阻塞) -(解除阻塞)#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//2.1.定義一個聯合體
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
main()
{
key_t key;
int semid; //信號量ID
union semun v;//2.2.定義初始化值
int r;
struct sembuf op[1];
//1.創建信號量
key=ftok(".",99);
if(key==-1) printf("ftok err:%m\n"),exit(-1);
//semid=semget(key,1/*信號量數組個數*/,
// IPC_CREAT|IPC_EXCL|0666);
semid=semget(key,1,0);//得到信號量
if(semid==-1) printf("get err:%m\n"),exit(-1);
printf("id:%d\n",semid);
//2.初始化信號量
v.val=2;
r=semctl(semid,0,SETVAL,v);//2.3設置信號量的值
if(r==-1) printf("初始化失敗!\n"),exit(-1);
//3.對信號量進行阻塞操作
//3.1.定義操作
op[0].sem_num=0;//信號量下標
op[0].sem_op=-1;//信號量操作單位與類型
op[0].sem_flg=0;
while(1)
{
r=semop(semid,op,1);
printf("解除阻塞!\n");
}
//4.刪除(可以不刪除)
}
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//2.1.定義一個聯合體
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
main()
{
key_t key;
int semid; //信號量ID
union semun v;//2.2.定義初始化值
int r;
struct sembuf op[2];
//1.創建信號量
key=ftok(".",99);
if(key==-1) printf("ftok err:%m\n"),exit(-1);
semid=semget(key,1,0);//得到信號量
if(semid==-1) printf("get err:%m\n"),exit(-1);
printf("id:%d\n",semid);
//3.對信號量進行阻塞操作
//3.1.定義操作
op[0].sem_num=0;//信號量下標
op[0].sem_op=1;//信號量操作單位與類型
op[0].sem_flg=0;
op[1].sem_num=0;//信號量下標
op[1].sem_op=1;//信號量操作單位與類型
op[1].sem_flg=0;
while(1)
{
r=semop(semid,op,2);
sleep(1);
}
//4.刪除(可以不刪除)
//semctl(semid,0,IPC_RMID);
}
二.網絡
1.基礎(ip)
1.1.網絡工具
ping
ping ip地址
ping -b ip廣播地址
ifconfig -a
netstat -a
netstat -u
netstat -t
netstat -x
netstat -n
route
lsof
1.2.網絡的基本概念
網絡編程采用socket模型.
網絡通信本質也是進程之間的IPC。
是不同主機之間。
識別主機:4字節整數:IP地址
識別進程:2字節整數:端口號
IP地址的表示方法: 內部表示:4字節整數
外部表示:數點字符串
結構體
1 2 3 4 分段表示,每個段使用.分割
"192.168.0.26"
ip地址的轉換:
struct sockaddr_in
{
int sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
}
struct in_addr
{
in_addr_t s_addr;
}
//總結: IP地址的表示 字符串表示"192.168.0.26" 整數表示:in_addr_t; 字結構表示struct in_addr; 連接點:endpoint struct sockaddr_in
{
in_port_t sin_port;
struct in_addr sin_addr;
};
1.3.IP地址的轉換 inet_addr //把字符串IP轉換為二進制整數IP(網絡字節序) inet_aton //把字符串IP轉換為struct in_addr;(網絡字結序) #inet_network//把字符串IP轉換為二進制整數IP(本地字節序) inet_ntoa //把結構體struct in_addr轉換為字符串IP
4個本地主機字節序與網絡序轉換函數:
h表示主機字節序,n表示網絡字節序,s表示兩個2字節,l表示4個字節
uint16_t htons(uint16_t) uint32_t htonl(uint32_t) uint16_t ntohs(uint16_t) uint32_t ntohl(uint32_t) ps:所以發送時候,端口轉換可以用htons,而ip可以用htonl
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
/*
in_addr_t nip=192<<24 | 168 <<16 | 0<<8 | 26;
char *ip="192.168.0.26";
//把整數轉換為字符串inet_ntoa
struct in_addr sip;
int myip;
sip.s_addr=nip;
printf("nip:%u\n",nip);
printf("%s\n",inet_ntoa(sip));
myip=inet_addr(ip);
printf("%u\n",myip);
printf("%hhu.%hhu.%hhu.%hhu\n", myip>>24 & 255,
myip>>16 & 255,
myip>>8 & 255,
myip>>0 & 255);
*/
/*
char ip[4]={192,168,0,26};
printf("%d\n",*(int*)ip);
*/
char *ip="10.45.8.1";
struct in_addr addr;
in_addr_t net;
in_addr_t host;
struct in_addr tmp;
inet_aton(ip,&addr);
net=inet_lnaof(addr);
host=inet_netof(addr);
tmp.s_addr=net;
printf("%s\n",inet_ntoa(tmp));
tmp.s_addr=host;
printf("%s\n",inet_ntoa(tmp));
}
1.4.IP地址的意義 IP地址的位表達不同意義: IP地址組建網絡:網絡標識/主機標識 網絡 主機 A類 7 24 網絡少 主機 B類 14 16 C類 21 8 D類 組播 E類 沒有使用 1.5.計算機系統中的網絡配置 /etc/hosts文件 配置IP,域名,主機名 gethostbyname gethostbyaddr /etc/protocols文件 配置系統支持的協議 /etc/services文件 配置服務 get***by***; gethostbyname getprotobyname
#include <stdio.h>
#include <netdb.h>
main()
{
struct hostent *ent;
/*打開主機配置數據庫文件*/
sethostent(1);
while(1)
{
ent=gethostent();
if(ent==0) break;
printf("主機名:%s\t",ent->h_name);
printf("IP地址:%hhu.%hhu.%hhu.%hhu\t",
ent->h_addr[0],
ent->h_addr[1],
ent->h_addr[2],
ent->h_addr[3]);
printf("別名:%s\n",ent->h_aliases[0]);
}
endhostent();
}
#include <stdio.h>
#include <netdb.h>
main()
{
struct hostent *ent;
ent=gethostbyname("bbs.tarena.com.cn");
//printf("%s\n",ent->h_aliases[0]);
printf("%hhu.%hhu.%hhu.%hhu\n",
ent->h_addr_list[0][0],
ent->h_addr_list[0][1],
ent->h_addr_list[0][2],
ent->h_addr_list[0][3]);
}
#include <stdio.h>
#include <netdb.h>
#include <sys/utsname.h>
main()
{
struct protoent *ent;
struct utsname name;
ent=getprotobyname("tcp");
printf("%d\n",ent->p_proto);
uname(&name);
printf("%s\n",name.machine);
printf("%s\n",name.nodename);
printf("%s\n",name.sysname);
printf("%s\n",name.domainname);
}
2.TCP/UDP編程 對等模型:AF_INET SOCK_DGRAM 0:UDP C/S 模型:AF_INET SOCK_STREAM 0:TCP 2.0.網絡編程 ISO的7層模型: 物理層 數據鏈路層 數據鏈路層(數據物理怎么傳輸) 網絡層 IP層 (數據的傳輸方式) 傳輸層 傳輸層 (數據傳輸的結果) 會話層 應用層 (數據傳遞的含義) 表示層 應用層 2.1.UDP編程的數據特點 UDP采用對等模型SOCK_DGRAM socket socket:socket 綁定IP地址bind 連接目標(可選) conncect read/recv/recvfrom 發送數據 write/send/sendto 關閉close 案例: A: B 接收用戶的數據 發送數據 打印數據與發送者IP 接收數據并打印 返發一個信息
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
main()
{
int fd;//socket描述符號
struct sockaddr_in ad;//本機的IP地址
char buf[100];//接收數據緩沖
struct sockaddr_in ad_snd;//發送者IP地址
socklen_t len;//發送者IP的長度
int r;
fd=socket(AF_INET,SOCK_DGRAM,17);
if(fd==-1) printf("socket:%m\n"),exit(-1);
printf("建立socket成功!\n");
ad.sin_family=AF_INET;
ad.sin_port=htons(11111);
inet_aton("192.168.180.92",&ad.sin_addr);
r=bind(fd,(struct sockaddr*)&ad,sizeof(ad));
if(r==-1) printf("bind err:%m\n"),exit(-1);
printf("綁定成功!\n");
while(1)
{
len=sizeof(ad_snd);
r=recvfrom(fd,buf,sizeof(buf)-1,0,
(struct sockaddr*)&ad_snd,&len);
if(r>0){
buf[r]=0;
printf("發送者IP:%s,端口:%hu,數據:%s\n",
inet_ntoa(ad_snd.sin_addr),
ntohs(ad_snd.sin_port),buf);
sendto(fd,"古怪!",strlen("古怪!"),0,
(struct sockaddr*)&ad_snd,sizeof(ad_snd));
}
if(r==0)
{
printf("關閉!\n");
break;
}
if(r==-1)
{
printf("網絡故障!\n");
break;
}
}
close(fd);
}
總結: 1.問題: connect + send == sendto 2.問題: recvfrom的作用不是專門從指定IP接收 而是從任意IP接收數據,返回發送數據者的IP 3.問題: 為什么要bind,bind主要目的告訴網絡發送數據的目標. 是否一定綁定才能發送數據? 否:只要知道你的IP與PORT,就能發送數據. 4.問題: 為什么發送者沒有綁定IP與端口,他也有端口? 底層網絡驅動,幫我們自動生成IP與端口. 5.缺陷: 接收方不區分發送者的. send函數 sendto函數int sendto(
int fd,//socket描述符號
const void *buf,//發送的數據緩沖
size_t size,//發送的數據長度
int flags,//發送方式MSG_NOWAIT MSG_OOB
const struct sockaddr *addr,//發送的目標的IP與端口
socklen_t len//sockaddr_in的長度
);
返回: -1:發送失敗 >=0:發送的數據長度 recv函數 recvfrom函數 int recvfrom(
int fd,
void *buf,
size_t size,
int flags,
struct sockaddr*addr,//返回發送者IP與端口
socklen_t *len);//輸入返回IP的緩沖大小,返回實際IP的大小
2.2.TCP編程的數據特點 2.3.TCP服務器的編程 3.TCP的服務器編程模型 4.IP協議與處理(SOCK_RAW,SOCK_PACKET) 5.pcap編程 6.HTTP協議與網頁搜索 作業: 1.重新編寫UDP網絡通信 2.使用gethostbyname的得到bbs.tarena.com.cn