实验六(6) 线程及网络编程实验 (笔记)
(一)线程实验
实验目的:
了解线程定义、工作特点;掌握线程的创建、同步和互斥、条件变量的应用等编程实现方法,分析程序的执行流程和理解代码含义。
实验内容:
(1)线程创建
说明:
int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void*(*start_routine)(void *), void *arg);
功能:创建线程
int pthread_join(thread_t tid, void **status);
功能:等待线程结束
pthread_t pthread_self(void);
功能:得到线程id
示例1:(指定线程处理函数)
#include <stdio.h>
#include <pthread.h>
void thread(void)
{
int i;
for(i=0;i<3;i++)
printf("This is a pthread.\n");
}
int main(void)
{
pthread_t id;
int i,ret;
ret=pthread_create(&id,NULL,(void *) thread,NULL);
if(ret!=0){
printf ("Create pthread error!\n");
exit (1);
}
for(i=0;i<3;i++)
printf("This is the main process.\n");
pthread_join(id,NULL);
return (0);
}
示例2:(带参数)
/*pass a parameter to a thread*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
int *num;
num=(int *)arg;
printf("create parameter is %d \n",*num);
return (void *)0;
}
int main(int argc ,char *argv[])
{
pthread_t tidp;
int error;
int test=4;
int *attr=&test;
error=pthread_create(&tidp,NULL,create,(void *)attr);
if(error)
{
printf("pthread_create is created is not created ... \n");
return -1;
}
sleep(1);
printf("pthread_create is created ...\n");
return 0;
}
示例3:返回码
//A example showing a thread to exit and with a return code.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *create(void *arg)
{
printf("new thread is created ... \n");
return (void *)8;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int error;
void *temp;
error = pthread_create(&tid, NULL, create, NULL);
if( error )
{
printf("thread is not created ... \n");
return -1;
}
error = pthread_join(tid, &temp);
if( error )
{
printf("thread is not exit ... \n");
return -2;
}
printf("thread is exit code %d \n", (int )temp);
return 0;
}
//请实验填入相关语句,通过线程执行函数返回temp值给线程阻塞函数pthread_join //的第二个参数c
//return a complex structure
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
struct menber
{
int a;
char *b;
}temp={8,"zieckey"};
void *create(void *arg)
{
printf("new thread ... \n");
return _______________;
}
int main(int argc,char *argv[])
{
int error;
pthread_t tid;
struct menber *c;
error = pthread_create(&tid, NULL, create, NULL);
if( error )
{
printf("new thread is not created ... \n");
return -1;
}
printf("main ... \n");
error=pthread_join(tid,____________);
if( error )
{
printf("new thread is not exit ... \n");
return -2;
}
printf("c->a = %d \n",c->a);
printf("c->b = %s \n",c->b);
sleep(1);
return 0;
}
//程序中两个getpid()得到的值的结果说明:
//Showing how to get the thread's tid and the process's pid.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> /*getpid()*/
void *create(void *arg)
{
printf("New thread .... \n");
printf("This thread's id is %u \n", (unsigned int)pthread_self());
printf("The process pid is %d \n",getpid());
return (void *)0;
}
int main(int argc,char *argv[])
{
pthread_t tid;
int error;
printf("Main thread is starting ... \n");
error = pthread_create(&tid, NULL, create, NULL);
if(error)
{
printf("thread is not created ... \n");
return -1;
}
printf("The main process's pid is %d \n",getpid());
sleep(1);
return 0;
}
(2)互斥锁
说明:
1)int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
功能:互斥锁初始化
参数 mutex :一个指向要初始化的互斥锁的指针
参数 attr :传递 NULL初始化带有默认属性的互斥锁,否则用类似于线程属性对象所使用的方法,先创建互斥锁属性对象,再用该属性对象来创建互斥锁。
2)int pthread_mutex_lock (pthread_mutex_t *mutex);
功能:加锁
参数 mutex :需要加锁的互斥锁
3)int pthread_mutex_unlock (pthread_mutex_t *mutex);
功能:解锁
参数 mutex :需要解锁的互斥锁
4)int pthread_mutex_trylock( pthread_mutex_t *mutex );
功能:非阻塞的锁定互斥锁
参数mutex:互斥锁
5)int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁互斥锁
参数 mutex :指向要销毁的互斥锁指针
6)int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
功能:调用者线程首先释放mutex;然后阻塞,等待被别的线程唤醒;当调用者线程被唤醒后,调用者线程会再次获取mutex
参数 mutex :互斥锁
7)int pthread_cond_signal(pthread_cond_t * cond);
功能:唤醒因cond指向的条件变量而阻塞的线程
参数cond:条件变量
(a)互斥锁1
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t mt=PTHREAD_MUTEX_INITIALIZER;;
char *a="this is a test!";
playa(){
int i;
pthread_mutex_lock(&mt);
i=0;
printf("now a thread is printing:");
while (*(a+i)!='\0')
{
printf("%c",*(a+i));
i++;
if(i==10)
sleep(1);
}
printf("\n");
pthread_mutex_unlock(&mt);
}
playb(){
int i;
pthread_mutex_lock(&mt);
i=0;
printf("now b thread is printing:");
while (*(a+i)!='\0')
{
printf("%c",*(a+i));
i++;
if(i==10)
sleep(1);
}
printf("\n");
pthread_mutex_unlock(&mt);
}
main(){
pthread_t tida,tidb;
pthread_mutex_init(&mt,NULL);
pthread_create(&tida,NULL,(void *)&playa,NULL);
pthread_create(&tidb,NULL,(void *)&playb,NULL);
pthread_join(tida,NULL);
pthread_join(tidb,NULL);
pthread_mutex_destroy(&mt);
printf("main thread=%lu\n",pthread_self());
}
上面互斥锁相关函数如果注释掉,执行结果说明:
(b)互斥锁2
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
void* t1()
{
pthread_mutex_lock(&mutex1);
//sleep(2);
pthread_mutex_lock(&mutex2);
printf("new thread didn't dead lock?\n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return (void*)0;
}
void* t2()
{
pthread_mutex_lock(&mutex2);
/*while(pthread_mutex_trylock(&mutex1) == EBUSY)
{
printf("thread t2 is trying to get lock!\n");
usleep(100000);
}*/
pthread_mutex_lock(&mutex1);
printf("new thread didn't dead lock?\n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1);
return (void*)0;
}
int main()
{
pthread_t ptd1,ptd2;
pthread_create(&ptd1, NULL, t1, NULL);
pthread_create(&ptd2, NULL, t2, NULL);
pthread_join(ptd1, NULL);
pthread_join(ptd2, NULL);
printf("main thread quit!\n");
return 0;
}
(c)生产者和消费者1:使用互斥锁实现两个线程同步,一个线程从标准输入设备读取数据,另一个线程将读入的数据输出到标准输出设备
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <string.h>
void *thread_function(void *arg);
pthread_mutex_t work_mutex; // 互斥锁定义
#define WORK_SIZE 1024 // 共享数据尺寸
char work_area[WORK_SIZE];
int time_to_exit = 0;
int main(int argc,char *argv[]){
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_mutex_init(&work_mutex, NULL); //init mutex 初始化互斥锁
if (res != 0){
perror("Mutex initialization failed");
exit(EXIT_FAILURE);
}
res = pthread_create(&a_thread, NULL, thread_function, NULL); //create new thread创建新线程
if (res != 0){
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
pthread_mutex_lock(&work_mutex); //lock the mutex 互斥锁上锁
printf("Input some text. Enter 'end' to finish\n");
while(!time_to_exit){
fgets(work_area, WORK_SIZE, stdin); //get a string from stdin读取一行信息
pthread_mutex_unlock(&work_mutex); //unlock the mutex解锁
while(1){
pthread_mutex_lock(&work_mutex); //lock the mutex
if (work_area[0] != '\0'){ //该条件成立说明
pthread_mutex_unlock(&work_mutex);
//解锁后,主线程进入死循环,是等待
sleep(1);
}
else
break;
}
}
pthread_mutex_unlock(&work_mutex); //解锁
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread,&thread_result);//等待另一个线程结束
if (res != 0){
perror("Thread join failed");
exit(EXIT_FAILURE);
}
printf("Thread joined\n");
pthread_mutex_destroy(&work_mutex);//销毁互斥锁
exit(EXIT_SUCCESS);
}
void *thread_function(void *arg){
sleep(1);
pthread_mutex_lock(&work_mutex); //上锁,抢占资源
while(strncmp("end", work_area, 3) != 0){ //判断是不是结束信息end
printf("You input %d characters\n", strlen(work_area) -1);
printf("the characters is %s",work_area); //输出输入的字符内容
work_area[0] = '\0'; //设置第一位为\0,重新准备输入
pthread_mutex_unlock(&work_mutex); //输出完成以后就解锁
sleep(1);
pthread_mutex_lock(&work_mutex); //上锁
while (work_area[0] == '\0' ){ //如果该循环一直在运行,说明
pthread_mutex_unlock(&work_mutex); //解锁后等待
sleep(1);
pthread_mutex_lock(&work_mutex); //上锁再次循环
}
}
time_to_exit = 1; //设置结束标志
work_area[0] = '\0';
pthread_mutex_unlock(&work_mutex);
pthread_exit(0);
}
(d)生产者和消费者2
#include<stdio.h>
#include<pthread.h>
#define BUFFER_SIZE 8
struct prodcons {
int buffer[BUFFER_SIZE];
pthread_mutex_t lock; //互斥LOCK
int readpos , writepos;
pthread_cond_t notempty; //缓冲区非空条件判断
pthread_cond_t notfull; //缓冲区未满条件判断
};
void init(struct prodcons * b){
pthread_mutex_init(&b->lock,NULL);
pthread_cond_init(&b->notempty,NULL);
pthread_cond_init(&b->notfull,NULL);
b->readpos=0;
b->writepos=0;
}
void put(struct prodcons* b,int data){
pthread_mutex_lock(&b->lock);
if((b->writepos + 1) % BUFFER_SIZE == b->readpos)
{ pthread_cond_wait(&b->notfull, &b->lock) ;
}
printf("produce:");
printf("%d\n", data) ;
b->buffer[b->writepos]=data;
b->writepos++;
if(b->writepos >= BUFFER_SIZE)
b->writepos=0;
pthread_cond_signal(&b->notempty);
pthread_mutex_unlock(&b->lock);
}
int get(struct prodcons *b){
int data;
pthread_mutex_lock(&b->lock);
if(b->writepos == b->readpos)
{ pthread_cond_wait(&b->notempty, &b->lock);
}
printf("consume:");
data = b->buffer[b->readpos];
b->readpos++;
if(b->readpos >= BUFFER_SIZE)
b->readpos=0;
pthread_cond_signal(&b->notfull);
pthread_mutex_unlock(&b->lock);
return data;
}
#define OVER (-1)
struct prodcons buffer;
void *producer(void *data)
{
int n;
for(n = 0; n < 50; n++)
{
put(&buffer, n);
}
put(&buffer, OVER);
return NULL;
}
void *consumer(void * data)
{ int d;
while(1)
{
d = get(&buffer);
if(d == OVER)
break;
printf("%d\n", d);
}
return NULL;
}
int main(void)
{
pthread_t th_a, th_b;
void *retval;
init(&buffer);
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
printf("\n");
return 0;
}
(e)出租车实例分析
代码1
#include <stdio.h>
#include <pthread.h>
void *traveler_arrive(void *name);
void *taxi_arrive(void *name);
// 提示出租车到达的条件变量
pthread_cond_t taxiCond=PTHREAD_COND_INITIALIZER;
//同步锁
pthread_mutex_t taxiMutex=PTHREAD_MUTEX_INITIALIZER;
//旅客到达等待出租车
void *traveler_arrive(void *name){
printf("Traveler: %s, needs a taxi now! \n" ,(char *)name);
pthread_mutex_lock(&taxiMutex);
pthread_cond_wait(&taxiCond, &taxiMutex);
pthread_mutex_unlock(&taxiMutex);
printf("Traveler: %s,now got a taxi!\n",(char *)name);
pthread_exit((void *)0);
}
// 出租车到达
void *taxi_arrive(void *name){
printf(" Taxi %s,arrives.\n",(char *)name);
pthread_cond_signal(&taxiCond);
pthread_exit((void *)0);
}
int main(){
pthread_t thread;
pthread_create(&thread,NULL,(void *)taxi_arrive,(void *)("Jack"));
sleep(1);
pthread_create(&thread,NULL,(void *)traveler_arrive,(void *)("Susan"));
sleep(1);
pthread_create(&thread,NULL,(void *)taxi_arrive,(void *)(" Mike"));
sleep(1);
return 0;
}
代码2
#include <stdio.h>
#include <pthread.h>
void *traveler_arrive(void *name);
void *taxi_arrive(void *name);
// 提示出租车到达的条件变量
pthread_cond_t taxiCond=PTHREAD_COND_INITIALIZER;
//同步锁
pthread_mutex_t taxiMutex=PTHREAD_MUTEX_INITIALIZER;
//旅客到达等待出租车
int travelerCount=0;
void *traveler_arrive(void *name){
printf("Traveler: %s, needs a taxi now! \n" ,(char *)name);
pthread_mutex_lock(&taxiMutex);
travelerCount++;
pthread_cond_wait(&taxiCond, &taxiMutex);
pthread_mutex_unlock(&taxiMutex);
printf("Traveler: %s,now got a taxi!\n",(char *)name);
pthread_exit((void *)0);
}
// 出租车到达
void *taxi_arrive(void *name){
printf(" Taxi %s,arrives.\n",(char *)name);
while(1)
{
pthread_mutex_lock(&taxiMutex);
// 当发现已经有旅客在等待时,才触发条件变量
if(travelerCount>0)
{
pthread_cond_signal(&taxiCond);
pthread_mutex_unlock(&taxiMutex);
break;
}
pthread_mutex_unlock (&taxiMutex);
}
pthread_exit((void *)0);
}
int main(){
pthread_t thread;
pthread_create(&thread,NULL,(void *)taxi_arrive,(void *)("Jack"));
sleep(1);
pthread_create(&thread,NULL,(void *)traveler_arrive,(void *)("Susan"));
sleep(1);
pthread_create(&thread,NULL,(void *)taxi_arrive,(void *)(" Mike"));
sleep(1);
return 0;
}
(二)网络实验
实验目的:
了解网络基础知识应用,掌握tcp、udp编程的实现方法和函数使用。
实验内容:
(A)得到机器硬件地址
(1)使用函数说明
int socket函数:int socket(int domain, int type, int protocol);
Domain :使用协议族,通常为PF_INET-----互联网协议族(TCP/IP协议族)
Type socket类型:SOCK_STREAM 或SOCK_DGRAM,或原始socket(SOCK_RAW)
Protocol :值0
返回:整型socket描述符
int ioctl函数:int ioctl(int fd, ind cmd, …);
fd:文件描述符
cmd :具体命令
这里fd文件描述符:套接字sock,cmd命令:SIOCGIFHWADDR-获取接口地址
(2)使用结构体数据说明
结构体struct ifreq
struct ifreq {
union
{
char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */
} ifr_ifrn;
union
{
……
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
……
} ifr_ifru;
};
# define ifr_name ifr_ifrn.ifrn_name /* interface name */
# define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
# define ifr_addr ifr_ifru.ifru_addr /* address */
# define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
# define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
# define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
……
结构体struct sockaddr
struct sockaddr {
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
(3)程序代码:
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <string.h>
int main(int argc, char *argv[]){
struct ifreq ifreq;
int sock;
if(argc!=2){
printf("Usage : ethname\n");
return 1;
}
if((sock=socket(AF_INET,SOCK_STREAM,0))<0){
perror("socket");
return 2;
}
strcpy(ifreq.ifr_name,argv[1]);
if(ioctl(sock,SIOCGIFHWADDR,&ifreq)<0){ //获得MAC地址
perror("ioctl");
return 3;
}
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
(unsigned char)ifreq.ifr_hwaddr.sa_data[0],
(unsigned char)ifreq.ifr_hwaddr.sa_data[1],
(unsigned char)ifreq.ifr_hwaddr.sa_data[2],
(unsigned char)ifreq.ifr_hwaddr.sa_data[3],
(unsigned char)ifreq.ifr_hwaddr.sa_data[4],
(unsigned char)ifreq.ifr_hwaddr.sa_data[5]);
return 0;
}
(B)通过ip地址得到机器名
(1)使用函数说明
inet_aton()函数:int inet_aton(const char * string, struct in_addr *addr);
输入参数string:包含ASCII表示的IP地址
输出参数addr:包含IP地址的结构体
功能:将字符串IP地址转换为32位网络序列IP地址。
gethostbyaddr函数:struct hostent gethostbyaddr(const char * addr, int len, int type);
addr:指向网络字节顺序地址的指针。参数addr实际不是char类型, 而是一个真正指向含有IPv4地址的结构体in_addr。
len: 地址的长度,在AF_INET类型地址中为4。
type:地址类型,应为AF_INET。
(2)使用结构体数据说明
hostent结构体
struct hostent {
char *h_name; /* 主机域名 */
char **h_aliases; /* 一个以NULL结尾的主机别名数组 */
int h_addrtype; /* 返回地址类型,Internet环境下 AF-INET */
int h_length; /* 地址的字节长度 */
char **h_addr_list; /* 以0结尾的数组,含该主机所有地址*/
};
struct in_addr结构体
struct in_addr {
unsigned long s_addr; //32位IP地址。
};
(3)程序代码:
#include <netdb.h>
#include <sys/socket.h>
int main(int argc, char **argv){
char *ptr,**pptr;
struct hostent *hptr;
char str[32];
char ipaddr[16];
struct in_addr *hipaddr;
ptr = argv[1];
if(!inet_aton(ptr,hipaddr)) {printf("inet_aton error\n");return 1;}
if( (hptr = gethostbyaddr(hipaddr, 4, AF_INET) ) == NULL ) {
printf("gethostbyaddr error for addr:%s\n", ptr);
return 1;
}
printf("official hostname:%s\n",hptr->h_name);
for(pptr = hptr->h_aliases; *pptr != NULL; pptr++)
printf(" alias:%s\n",*pptr);
}
(C)得到主机名称和ip地址
(1)使用函数说明
gethostbyname函数:struct hostent * gethostbyname(const char * name);
输入参数name:指向主机名的指针
功能:返回对应于给定主机名的主机信息。
inet_ntoa函数:char* inet_ntoa(struct in_addr in);
输入参数in:一个网络上的IP地址
功能:将一个十进制网络字节序转换为点分十进制IP格式的字符串。
(2)使用结构体数据说明
hostent结构体
struct hostent {
char *h_name; /* 主机域名 */
char **h_aliases; /* 一个以NULL结尾的主机别名数组 */
int h_addrtype; /* 返回地址类型,Internet环境下 AF-INET */
int h_length; /* 地址的字节长度 */
char **h_addr_list; /* 以0结尾的数组,含该主机所有地址*/
};
struct in_addr结构体
struct in_addr {
unsigned long s_addr; //32位IP地址。
};
(3)程序代码:
#include "stdio.h"
#include "sys/types.h"
#include "sys/socket.h"
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
int main(int argc, char *argv[]) //主函数
{
struct hostent *ht = NULL;
ht = (struct hostent *)gethostbyname(argv[1]);
if (ht)
{
int i = 0;
printf("Host:%s \n", argv[1]); /* 原始域名 */
printf("Name:%s\n", ht->h_name); /* 名称 */
/*协议族AF_INET为IPv4或者AF_INET6为IPv6 */
printf("Type:%s\n", ht->h_addrtype == AF_INET ? "AF_INET" : "AF_INET6");
/* IP地址的长度 */
printf("Legnth:%d\n", ht->h_length);
/* 打印IP地址 */
printf("\n");
printf("IP Address:\n");
for (i = 0;; i++)
{
if (ht->h_addr_list[i] != NULL)
{ /* 不是IP地址数组的结尾 */
printf("IP:%s\n",inet_ntoa(*(struct in_addr *)ht->h_addr_list[i])); /*打印IP地址 */
}
else
{ /*达到结尾 */
break; /*退出for循环 */
}
}
/* 打印域名地址 */
printf("\n");
printf("Domain Name:\n");
for (i = 0;; i++)
{ /*循环 */
if (ht->h_aliases[i] != NULL)
{ /* 没有到达域名数组的结尾 */
printf("alias %d:%s\n", i, ht->h_aliases[i]); /* 打印域名 */
}
else
{ /*结尾 */
printf("\n");
break; /*退出循环 */
}
}
}
return 0;
}
(D)udp 协议实验,包括客户端和服务端
udp 协议编程操作步骤
(1)函数说明
htons()函数:uint16_t htons(uint16_t hostshort);
功能:把16位值从主机字节序转换成网络字节序
connect函数:int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd ------socket函数返回的socket描述符
serv_addr----包含远端主机IP地址和端口号的指针
addrlen------远端地址结构的长度
返回----------错误返回-1,并设置errno为相应错误码
sendto函数:int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to, int tolen);
to------------目地机IP地址和端口号
tolen---------sizeof (struct sockaddr)
flags---------0
返回----------实际发送数据字节长度或发生错误时返回-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
bind函数:int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
Sockfd: 调用socket函数返回的socket描述符
my_addr: 指向包含本机IP地址及端口号等sockaddr类型指针
Addrlen: 设置为sizeof(struct sockaddr)
sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该socket的IP地址和端口号
(2)数据结构说明
结构体struct sockaddr
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14个字节的协议地址 */
};
结构体struct sockaddr_in
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0保持与struct sockaddr大小相同 */
};
sin_zero: 将sockaddr_in 填充到与struct sockaddr同样长度,可用bzero()或memset()函数置零。
(3)程序代码
uclt.c
#include <sys/types.h>
#include <sys/socket.h>
#include<pthread.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv){
if (argc != 3){
printf("Usage: %s ip port", argv[0]);
exit(1);}
printf("This is a UDP client\n");
struct sockaddr_in addr;
int sock;
if ( (sock=socket(AF_INET, SOCK_DGRAM, 0)) <0){
perror("socket");
exit(1);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2])); /* 把字符串转换成长整型数*/
addr.sin_addr.s_addr = inet_addr(argv[1]);
if (addr.sin_addr.s_addr == INADDR_NONE){
printf("Incorrect ip address!");
close(sock);
exit(1);
}
char c;
char buff[512];
int i=0;
int len = sizeof(addr);
while (1)
{
while ((c = getchar()) != '\n')
buff[i++]=c;
buff[i]='\0';
i=0;
int n;
n = sendto(sock, buff, strlen(buff), 0, (struct sockaddr *)&addr, sizeof(addr));
if (n < 0){
perror("sendto");
close(sock);
break;
}
n = recvfrom(sock, buff, 512, 0, (struct sockaddr *)&addr, &len);
if (n>0){
buff[n] = 0;
printf("received:");
puts(buff);
}
else if (n==0)
{
printf("server closed\n");
close(sock);
break;
}
else if (n == -1)
{
perror("recvfrom");
close(sock);
break;
}
}
return 0;
}
usvr.c
#include <sys/types.h>
#include <sys/socket.h>
#include<pthread.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv){
if (argc != 2){
printf("Usage: %s port\n", argv[0]);
exit(1);}
printf("Welcome! This is a UDP server, I can only received message from client and reply with same message\n");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[1])); /* 把字符串转换成长整型数*/
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int sock;
if ( (sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
perror("socket");
exit(1);
}
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0){
perror("bind");
exit(1);
}
char buff[512];
struct sockaddr_in clientAddr;
int n;
int len = sizeof(clientAddr);
while (1)
{
n = recvfrom(sock, buff, 511, 0, (struct sockaddr*)&clientAddr, &len);
if (n>0)
{
buff[n] = 0;
printf("%s %u says: %s\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port), buff);
n = sendto(sock, buff, n, 0, (struct sockaddr *)&clientAddr, sizeof(clientAddr));
if (n < 0)
{
perror("sendto");
break;
}
}
else
{
perror("recv");
break;
}
}
return 0;
}
(D)tcp 协议实验,包括客户端和服务端
tcp协议编程操作步骤
(1)函数说明
htons()函数:uint16_t htons(uint16_t hostshort);
功能:把16位值从主机字节序转换成网络字节序
connect函数:int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd ------socket函数返回的socket描述符
serv_addr----包含远端主机IP地址和端口号的指针
addrlen------远端地址结构的长度
返回----------错误返回-1,并设置errno为相应错误码
send( ) 函数:int send(int sockfd, const void *msg, int len, int flags);
sockfd------传输数据的socket描述符
msg---------指向要发送数据的指针
len-----------以字节为单位的数据长度
flags--------一般为0
recv()函数:int recv(int sockfd,void *buf,int len,unsigned int flags);
sockfd-------接受数据socket描述符
buf ----------存放接收数据缓冲区
Len---------- 缓冲区长度
flags---------为0
返回----------实际接收字节数,错误,返回-1并置相应errno值
bind函数:int bind(int sockfd,struct sockaddr *my_addr, int addrlen);
Sockfd: 调用socket函数返回的socket描述符
my_addr: 指向包含本机IP地址及端口号等sockaddr类型指针
Addrlen: 设置为sizeof(struct sockaddr)
sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该socket的IP地址和端口号
listen函数: int listen(int sockfd,int backlog);
功能:为socket建立输入数据队列,将到达的服务请求保存在此队列,直到程序处理。
sockfd-----Socket调用返回的socket 描述符
backlog----指定请求队列允许最大请求数,进入的连接请求将在队列中等待accept( )接收。缺省值20。如服务请求到来时,输入队列已满,socket将拒绝连接请求,客户收到出错信息。错误listen( )函数返回-1,并置errno错误码
accept函数:int accept(int sockfd, void *addr, int *addrlen);
sockfd----- 被监听的socket描述符
addr--------指向sockaddr_in变量指针,该变量存放提出连接请求服务的主机的信息(即某台主机从某个端口发出该请求)
addrten----指向值为sizeof(struct sockaddr_in)的整型指针变量
返回---------新的套接字描述符
inet_ntoa函数: char *inet_ntoa(struct in_addr in);
功能:将网络地址转换成“.”点隔的字符串格式。
(2)数据结构说明
结构体struct sockaddr
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14个字节的协议地址 */
};
结构体struct sockaddr_in
struct sockaddr_in {
short int sin_family; /* 地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填充0保持与struct sockaddr大小相同 */
};
sin_zero: 将sockaddr_in 填充到与struct sockaddr同样长度,可用bzero()或memset()函数置零。
(3)程序代码
tclt.c
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 3333
#define MAXDATASIZE 100 /*每次最大数据传输量 */
main(int argc, char *argv[]){
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if (argc < 2) {
fprintf(stderr,"Please enter the server's hostname!\n");
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL) {
herror("gethostbyname出错!");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket创建出错!");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero( &(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *) &serv_addr, \
sizeof(struct sockaddr)) == -1) {
perror("connect出错!");
exit(1);
}
if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {
perror("recv出错!");
exit(1);
}
buf[recvbytes] = '\0';
printf( "Received: %s",buf);
close(sockfd);
}
tsvr.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERVPORT 3333 /*服务器监听端口号 */
#define BACKLOG 10 /* 最大同时连接请求数 */
main()
{
int sockfd,client_fd,sin_size; /*sock_fd监听socket;client_fd数据传输socket */
struct sockaddr_in my_addr; /* 本机地址信息 */
struct sockaddr_in remote_addr; /* 客户端地址信息 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror( "socket创建出错!"); exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero( &(my_addr.sin_zero),8);
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {
perror( "bind出错!");
exit(1); }
if (listen(sockfd, BACKLOG) == -1) {
perror( "listen出错!");
exit(1);
}
while(1) {
sin_size = sizeof(struct sockaddr_in);
if ((client_fd = accept(sockfd, (struct sockaddr *) &remote_addr, &sin_size)) == -1) {
perror( "accept出错");
continue; }
printf( "received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork()) { /* 子进程代码段 */
if (send(client_fd, "Hello, you are connected!\n", 26, 0) == -1){
perror( "send出错!");
close(client_fd);
exit(0);
}
close(client_fd);
}
}
}
(E)访问邮件服务器,读取邮件。
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define POP3SERVPORT 110
#define MAXDATASIZE 4096
main(int argc, char *argv[]){
int sockfd;
struct hostent *host;
struct sockaddr_in serv_addr;
char *POPMessage[]={
"USER um1\r\n",
"PASS 123456\r\n",
"STAT\r\n",
"LIST\r\n",
"RETR 1\r\n",
"DELE 1\r\n",
"QUIT\r\n",
NULL
};
int iLength;
int iMsg=0;
int iEnd=0;
char buf[MAXDATASIZE];
if((host=gethostbyname("localhost"))==NULL) {
perror("gethostbyname error");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket error");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(POP3SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
perror("connect error");
exit(1);
}
do {
send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);
printf("have sent: %s",POPMessage[iMsg]);
iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);
iEnd+=iLength;
buf[iEnd]='\0';
printf("received: %s,%d\n",buf,iMsg);
iMsg++;
} while (POPMessage[iMsg]);
close(sockfd);
}
(F)测试建立一个WEB服务器。
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#define PORT 8100
#define BACKLOG 10
#define LENGTH 1024
char *httpweb="HTTP/1.0 200 OK\r\nDate: Mon, 17 Oct 2014 2014:10:00 GMT\r\nServer: tinyHttp/1.0 0891141 Corporation\r\nAccept-Ranges: bytes\r\nConnection: Keep-Close\r\nContent-Type: text/html\r\n\"\"\r\n";
char *web="<HTML>\r\n<HEAD>\r\n<TITLE>WEB网页实验</TITLE>\r\n<BODY aLink=green bgColor=#f1f1dd link=red\r\n vLink=#321afd>\r\n <H1>HELLO! WELCOME TO MY WEBSERVER</H1>\r\n <UL>\r\n <LI><A HREF=\"http://linux.bsuc.cn/\">学号:0891141</A>\r\n<LI><A HREF=\"http://linux.bsuc.cn/\">姓名:学生</A>\r\n</BODY>\r\n</HTML>\r\n";
int main (){
int sockfd;
int nsockfd;
int i,num;
int flag= 1;
int sin_size;
char revbuf[LENGTH];
struct sockaddr_in addr_local;
struct sockaddr_in addr_remote;
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
printf ("ERROR: Cannot obtain Socket Despcritor\n");
return (0);}
else {
printf ("OK: Obtain Socket Despcritor sucessfully\n");
}
addr_local.sin_family = AF_INET;
addr_local.sin_port = htons(PORT);
addr_local.sin_addr.s_addr = INADDR_ANY;
bzero(&(addr_local.sin_zero), 8);
if( bind(sockfd, (struct sockaddr*)&addr_local, sizeof(struct sockaddr)) == -1 ) {
printf ("ERROR: Cannot bind Port %d\n",PORT);
return (0);
} else {
printf("OK: Bind the Port %d sucessfully\n",PORT);
}
if(listen(sockfd,BACKLOG) == -1) {
printf ("ERROR: Cannot listen Port %d\n", PORT);
return (0);
} else {
printf ("OK: Listening the Port %d sucessfully\n", PORT);
}
while(1){
sin_size = sizeof(struct sockaddr_in);
if ((nsockfd = accept(sockfd, (struct sockaddr *)&addr_remote, &sin_size)) == -1){
printf ("ERROR: Obtain new Socket Despcritor error\n");
continue;
} else {
printf ("OK: Server has got connect from %s\n",inet_ntoa(addr_remote.sin_addr));
}
num = recv(nsockfd, revbuf, LENGTH, 0);
if(!vfork()) {
printf("OK: Http web is servering.\n");
send(nsockfd,httpweb, strlen(httpweb), 0);
send(nsockfd,web, strlen(web), 0);
}
close(nsockfd);
while(waitpid(-1, NULL, WNOHANG) > 0); /*等待任何子进程结束,若返回=0,子进程结束*/
printf("OK:Web page is had sended\n");
}
close(sockfd);
}