实验六(6) 线程及网络编程实验 (笔记)

实验六(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);
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值