学习笔记 day20 系统编程:网络编程

3、同步

/*
进行多线程编程,因为无法知道哪个线程会在哪个时候对共享资源进行操作,因此让如何保护共享资源变得复杂,通过下面这些技术的使用,可以解决
线程之间对资源的竞争:
    1 互斥量mutex
    2 信号灯semaphore
    3 条件变量conditions
*/
3.1 互斥锁
/*
互斥量使用步骤:创建、初始化、上锁、使用资源、解锁、销毁

在Linux中, 互斥量使用类型pthread_mutex_t表示.在使用前, 要对它进行初始化:
       对于静态分配的互斥量, 可以把它设置为默认的mutex对象PTHREAD_MUTEX_INITIALIZER
       对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy
*/
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr)
int pthread_mutex_destroy(pthread_mutex_t *mutex)
/*
mutex:使用时取地址
attr:互斥锁的属性,默认为NULL或PTHREAD_MUTEX_TIMED_NP为普通锁,只能对同一个资源上锁一次
						   PTHREAD_MUTEX_RECURSIVE_NP为嵌套锁,允许同一个线程对同一个资源多次上锁
						   PTHREAD_MUTEX_ERRORCHECK_NP为检错锁
						   PTHREAD_MUTEX_ADAPTIVE_NP为自适应锁
*/
int pthread_mutex_lock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t *mutex)
/*
lock对共享资源的访问, 要使用互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁。
trylock是非阻塞调用模式, 
如果互斥量没被锁住, trylock函数将对互斥量加锁, 并获得对共享资源的访问权限; 
如果互斥量被锁住了,trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态

mutex:使用时取地址
返回值: 成功则返回0, 出错则返回错误编号。
*/
int pthread_mutex_unlock(pthread_mutex_t *mutex)
/*
在操作完成后,必须给互斥量解锁,也就是前面所说的释放。
这样其他等待该锁的线程才有机会获得该锁,否则其他线程将会永远阻塞。
mutex:使用时取地址
*/
    
    
    
/*
总结:
	凡是访问全局变量时,必须上锁
	涉及到共享资源访问,必须上锁
*/
3.2条件变量
/*
1、创建条件变量
	pthread_cond_t  cond; 
2、条件变量初始化
	a、静态初始化
		PTHREAD_COND_INITIALIZER
	b、动态初始化
      	int pthread_cond_init(pthread_cond_t * cond,pthread_condattr_t * cond_attr);
3、条件变量撤销
	int pthread_cond_destroy(pthread_cond_t *cond);
4、条件变量等待
	int pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t *mutex);
		unlock -> wait -> lock
		
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

*/

//表示生产方与购买方的关系
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define BUFFERSIZE 16		//表示货架的最大物品量
typedef struct product{		//线程需要用到的资源 定义为结构体类型
    int buff[BUFFERSIZE];	//货架
    int readpos,writepos;	//使用队列表示货架,有双方使用到的位置
    pthread_mutex_t lock;	//互斥锁
    pthread_cond_t notfull;	//判断不空的条件变量
    pthread_cond_t notempty;//判断不满的条件变量
}product;

product buffer;

void init(product *pro){		//资源的初始化
    pthread_mutex_init(&pro->lock,NULL);
    pthread_cond_init(&pro->notempty,NULL);
    pthread_cond_init(&pro->notfull,NULL);
    pro->readpos=0;
    pro->writepos=0;
}
int get(product *pro){		//购买方通过此函数实现
    int data;
    pthread_mutex_lock(&pro->lock);//上锁
    if(pro->readpos==pro->writepos){
        pthread_cond_wait(&pro->notempty,&pro->lock);//如果货架为空,则等待接收不空的信号
    }
    data=pro->buff[pro->readpos];//取出该位置的数据
    pro->readpos++;				//数据被覆盖,已取出
    if(pro->readpos >= BUFFERSIZE){//形成循环队列
        pro->readpos=0;
    }
    pthread_cond_signal(&pro->notfull);//发送不满的信号
    pthread_mutex_unlock(&pro->lock);//解锁
    return data;
}
void *customer(void *arg){
    int d;
    while(1){
        d=get(&buffer);
        if(-1==d){
            break;
        }
        else{
            printf("--->%d\n",d);
        }
    }
    return NULL;		//不可少,返回值一定有
}
void put(product *pro,int data){
    pthread_mutex_lock(&pro->lock);
    if((pro->writepos+1)%BUFFERSIZE==pro->readpos){
        pthread_cond_wait(&pro->notfull,&pro->lock);
    }
    pro->buff[pro->writepos]=data;
    pro->writepos++;
    if(pro->writepos >= BUFFERSIZE){
        pro->writepos=0;
    }
    pthread_cond_signal(&pro->notempty);
    pthread_mutex_unlock(&pro->lock);
}
void *producter(void *arg){
    int i;
    for(i=0;i<20;i++){
        printf("%d--->\n",i+1);
        put(&buffer,i+1);
    }
    put(&buffer,-1);
    return NULL;

}
int main(int argc, char const *argv[]){
    
    pthread_t thc,thp;
    void * retval=NULL;			//用来接收join函数的参数
    int ret;
    init(&buffer);
    ret=pthread_create(&thp,NULL,producter,NULL);
    ret=pthread_create(&thc,NULL,customer,NULL);
    pthread_join(thc,&retval);		//两种写法一样
    //pthread_join(thc,NULL);
    pthread_join(thp,NULL);
    //pthread_join(thp,&retval);
    return 0;
}

网络编程(C/S)

一、简介

/*
linux的优点之一就是在于它丰富而稳定的网络协议栈,其范围是从协议无关层(如通用的socket层接口和设备层)到各种网络协议的实现
ISO/OSI七层网络模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
TCP/IP四层概念模型:应用层、传输层、网际层、网络接口层

TCP/IP实际上是一个协同工作的通信家族,为网络数据通信提供通路。协议族大体分为三部分:
1 Internet协议
2 传输控制协议TCP和用户数据报协议UDP
3 处于TCP和UDP之上的一组应用协议

IP主要有四个功能:数据传送、寻址、路由选择、数据报文的分段
*/

/*
linux中的网络编程通过socket接口实现,socket是一种文件描述符。
是一种多机通讯的机制、是一个函数、是一种文件描述符

套接字socket有三种类型:
流式套接字(SOCK_STREAM),可以提供可靠的、面向连接的通讯流,使用了TCP协议,保证了传输的正确性和顺序性
数据报套接字(SOCK_DGRAM),无连接,无序,不保证可靠,无差错,使用UDP
原始套接字,原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议的测试等

*/
struct in_addr{
    unsigned long s_addr;			//32位整数,需要转换为ip地址
}
struct sockaddr_in{
    short int sin_family;			//internet地址族  AF_INET
    unsigned short int sin_port;	//端口号
    struct in_addr sin_addr;		//32位ip地址
    unsigned char sin_zero[8];		//填0
}
/*

*/


地址转换与字节序转换
//地址转换
int inet_aton(const char *cp,struct in_addr *inp)
char *inet_ntoa(struct in_addr in)
/*
a代表ascii,n代表network。前者将ip地址转换为32位整数,存放在inp指针里。后者将32位ip转换位点分十进制形式,都需要字节序转换
通常使用下面的inet_addr进行转换
*/
unsigned long ip=inet_addr("192.168.1.1");
/*
参数简单,返回值为unsigned long,不需要做字节序转换
*/



//字节序转换
/*
不同类型的CPU对变量的字节存储顺序可能不同,有的系统高位在前,低位在后;而有的低位在前,高位在后。而网络中传输的数据顺序是要统一的。所以当内部字节存储顺序和网络字节顺序不同时,就一定要转换。

网络字节顺序是TCP/IP中规定好的一种表示格式,与具体的CPU无关。网络字节顺序采用大端排序方式。
*/
#include<arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
/*
h表示host,n表示network,l表示32位长整型,s表示16位短整型
htonl将32位长整型从主机字节序转换为网络字节序。
*/


/*
在网络程序里面,一般来说都是许多客户对应一个服务器,为了处理客户的请求, 对服务端的程序就提出了特殊的要求。目前最常用的服务器模型有:
循环服务器:服务器在同一个时刻只可以响应一个客户端的请求
并发服务器:服务器在同一个时刻可以响应多个客户端的请求

*/


函数
/*
进行Socket编程的常用函数有:
    socket:		创建一个socket (c/s)
    bind:		用于绑定IP地址和端口号到socket (c/s)
    connect:	该函数用于绑定之后的client端与服务器建立连接 (c)
    listen:		设置能处理的最大连接要求,Listen()并未开始接收连线,只是设置socket为listen模式。(s)
    accept:		用来接受socket连接。(s)
    send:		发送数据
    recv:		接收数据

*/
#include <sys/socket.h>
int socket(int family, int type, int protocol);
/*
family:对于IPv4,family参数指定为AF_INET
type:对于TCP协议,type参数指定SOCK_STREAM,表示面向流的传输协议;如果是UDP协议,则type参数指定为SOCK_DGRAM,表示面向数据报的传输协议
protocol:参数的介绍从略,指定为0即可
返回值:
socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1
*/
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen); 
/*
功能:bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号
sockfd:socket返回的文件描述符
myaddr:地址的起始位置,根据第三个参数来适应ipv4和ipv6
addrlen:myaddr的长度
返回值:成功返回0,失败返回-1

struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度


无法绑定?
由于关闭服务器时,系统内核会让连接延迟一段时间后关闭,以保证最后的信息传输完成,所以在关闭服务器进程时,需要等待一段时间后在此bind。

client终止时自动关闭socket描述符,server的TCP连接收到client发的FIN段后处于TIME_WAIT状态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止了server,所以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在linux上一般经过半分钟后就可以再次启劢server了。
在server的TCP连接没有完全断开之前不允许重新监听是不合理的,因为,TCP连接没有完全断开指的是connfd(127.0.0.1:8000)没有完全断开,而我们重新监听的是listenfd(0.0.0.0:8000),虽然是占用同一个端口,但IP地址不同,connfd对应的是某个客户端通讯的一个具体的IP地址,而listenfd对应的是wildcard address。解决这个问题的方法是使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。

解决方法:
在server代码的socket()之后和bind()之前调用之间插入如下代码:
int opt = 1; //相当于开关,设置套接口的属性,1为开,0为关
//在SOL_SOCKET(应用层)设置SO_REUSEADDR(允许重复使用地址)为 1
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

*/

/*
sockaddr_in初始化

bzero(&servaddr, sizeof(servaddr)); 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
servaddr.sin_port = htons(SERV_PORT);
首先将整个结构体清零,然后设置地址类型为AF_INET,网络地址为INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到不某个客户端建立了连接时才确定下来到底用哪个IP地址,端口号为SERV_PORT,我们定义为8000 

*/
int listen(int sockfd,int backlen);
/*
sockfd:
backlen:缓冲区大小
返回值:成功为0,失败为-1
*/
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); 
/*
功能:三方插手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来
sockfd:
cliaddr:地址起始位置,如果给cliaddr参数传NULL,表示不关心客户端的地址(输出参数),struct sockaddr_in cliaddr;使用时&cliaddr
addrlen:地址长度(输入输出参数)socklen_t attrlen;addrlen=sizeof(cliaddr);使用时&attrlen
返回值:成功返回一个正整数(链接套接口),失败返回-1

三方插手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来,
cliaddr是一个传出参数,accept()返回时传出客户端的地址和端口号.addrlen参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区cliaddr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。

*/
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen); 
/*
sockfd:
servaddr:地址起始位置
addrlen:地址长度
返回值:成功返回0,出错返回-1
*/

int send(int sockfd,void *buf,size_t len,int flags)
int recv(int sockfd,void *buf,size_t len,int flags)
/*
sockfd:
buf:缓冲区
len:缓冲区大小
flags:
	一般写0,此时send/recv就是read/write
	MSG_DONTROUTE:不查找路由表,send(局域网之内通信)使用
	MSG_OOB:允许发送/接收带外数据(插队数据)send/recv使用
	MSG_PEEK:查看数据以后,不从缓冲区删除(多个客户端都可接收)recv使用
	MSG_WAIT:阻塞等待至指定长度的所有数据,recv使用
返回值:成功返回实际接收/发送的字节数,失败返回-1
	read返回0表示客户端下线
*/
int sendto(int sockfd,void *buf,size_t len,int flags,struct sockaddr *toaddr,int tolen);
/*
toaddr:需要发送给 对方的地址起始位置
tolen:需要发送给 对方的地址长度
返回值:同上
*/
int recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *fromaddr,int *fromlen);
/*
fromaddr:如果为NULL,表示不关心客户端的地址,struct sockaddr_in fromaddr;使用时&fromaddr
fromlen:地址长度,socklen_t fromlen;fromlen=sizeof(fromaddr);使用时&fromlen
返回值:同上
*/


二、TCP(建立连接)

/*
TCP是重要的传输层协议,目的是允许数据同网络上的其他节点进行可靠的交换。它能提供端口编号的译码,以识别主机的应用程序,而且完成数据的可靠传输TCP 协议具有严格的内装差错检验算法确保数据的完整性TCP 是面向字节的顺序协议,这意味着包内的每个字节被分配一个顺序编号,并分配给每包一个顺序编号

*/

1、循环服务器

1、S
/*
1.创建一个socket,用函数socket()
2.绑定IP地址、端口等信息到socket上,用函数bind()
3.设置允许的最大连接数,用函数listen()
4.接收客户端上来的连接,用函数accept()
5.收发数据,用函数send()和recv(),或者read()和write()
6.关闭网络连接

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8000
int main(int argc, char const *argv[])
{
    int listenfd, connfd;
    struct sockaddr_in servaddr, cliaddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    listen(listenfd, 1024);

    int clilen;
    int i=0;
    char message[100];
    int n_recv;

    while(1)
    {
        clilen = sizeof(servaddr);
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);

        while (1)
        {
            n_recv = recvfrom(connfd, message, 100, 0, (struct sockaddr *)&cliaddr, &clilen);
            if (0 == n_recv)
            {
                printf("client is offonlie!\n");
                break;
            }
            for (i = 0; i < n_recv; i++)
            {
                message[i] = toupper(message[i]);
            }

            sendto(connfd, message, n_recv, 0, (struct sockaddr *)&cliaddr, sizeof(servaddr));
        }
        close(connfd);
    }

    
    close(listenfd);

    return 0;
}

2、C
/*
1.创建一个socket,用函数socket()
2.设置要连接的对方的IP地址和端口等属性
3.连接服务器,用函数connect()
4.收发数据,用函数send()和recv(),或者ead()和write()
5.关闭网络连接


*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8000


int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("need server IP!\n");
        exit(0);
    }
    int sockfd;
    struct sockaddr_in servaddr;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(PORT);

    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    char sendline[100];
    char recvline[100];
    int n_recv;

    while(1)
    {
        fgets(sendline, 100, stdin);
        sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));

        n_recv = recvfrom(sockfd, recvline, 100, 0, NULL, NULL);
        

        recvline[n_recv] = '\0';
        printf("recv %d char!\n", n_recv);
        fputs(recvline, stdout);
    }

    close(sockfd);

    return 0;
}



2、并发服务器(进程)

1、S
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>

#define PORT 8000

void *func(void *arg)
{
    int n_recv;
    char message[100];
    int i;
    struct sockaddr_in servaddr, cliaddr;
    int connfd;
    connfd = *(int *)arg;
    socklen_t clilen;
    clilen = sizeof(cliaddr);
    while (1)
    {
        n_recv = recvfrom(connfd, message, 100, 0, (struct sockaddr *)&cliaddr, &clilen);
        if (0 == n_recv)
        {
            printf("client is offonlie!\n");
            pthread_exit(NULL);
        }
        for (i = 0; i < n_recv; i++)
        {
            message[i] = toupper(message[i]);
        }
        sendto(connfd, message, n_recv, 0, (struct sockaddr *)&cliaddr, sizeof(servaddr));
    }
    close(connfd);

    return NULL;
}

int main(int argc, char const *argv[])
{
    int listenfd, connfd;
    socklen_t clilen;
    struct sockaddr_in servaddr, cliaddr;
    pthread_t tid;
    int ret;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    
    listen(listenfd, 1024);
    while (1)
    {
        clilen = sizeof(servaddr);
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
        ret = pthread_create(&tid, NULL, func, (void *)&connfd);
    }
    close(listenfd);

    return 0;
}

2、C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#define PORT 8000


int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("need server IP!\n");
        exit(0);
    }
    int sockfd;
    struct sockaddr_in servaddr;
    char sendline[100];
    char recvline[100];
    int n_recv;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(PORT);

    connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    while(1)
    {
        fgets(sendline, 100, stdin);
        sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));

        n_recv = recvfrom(sockfd, recvline, 100, 0, NULL, NULL);
    
        recvline[n_recv] = '\0';
        printf("recv %d char!\n", n_recv);
        fputs(recvline, stdout);
    }

    close(sockfd);

    return 0;
}

3、并发服务器(线程)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>

#define PORT 8000

void *func(void *arg)
{
    int connfd = *(int *)arg;
    int real_recv;
    char recv[100];
    struct sockaddr_in cliaddr;
    int clilen;
    while (1)
    {
        clilen = sizeof(cliaddr);
        real_recv = recvfrom(connfd, recv, 100, 0, (struct sockaddr *)&cliaddr, &clilen);
        if (real_recv == 0)
        {
            printf("client offonline!\n");
            break;
        }

        sendto(connfd, recv, real_recv, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
    }

    close(connfd);
    return NULL;
}

int main(int argc, char const *argv[])
{
    int listenfd, connfd;
    struct sockaddr_in servaddr, cliaddr;
    int clilen;

    pthread_t tid;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero((struct sockaddr *)&servaddr, sizeof(servaddr));
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_family = AF_INET;

    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, 1024);

    while (1)
    {
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
        pthread_create(&tid, NULL, func, (void *)&connfd);
    }
    close(listenfd);

    return 0;
}

4、三次握手

/*
		client					servet
		socket()				socket()
		//bind()				bind()
		connect()阻塞---------> listen()
				<--------------accept()阻塞
				--------------->确认信息
		send()					recv()
		recv()					send()
*/

三、UDP(无连接)

/*
UDP也是传输协议,无连接的,不可靠的传输服务。当接收数据时它不向发送方提供确认信息,不提供输入包的顺序,如果出现丢失包或重份包的情况,也不会向发送方发出差错报文。由于执行功能时具有较低的开销,因而执行速度比TCP快。
*/

1、S

/*
1.创建一个socket,用函数socket()
2.绑定IP地址、端口等信息到socket上,用函数bind()
3.循环接收数据,用函数recvfrom()
4.关闭网络连接
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 8000
int main(int argc, char const *argv[])
{
    int listenfd;
    struct sockaddr_in servaddr, cliaddr;

    listenfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

    int clilen;
    int i=0;
    char message[100];
    int n_recv;
    
    clilen = sizeof(servaddr);
    while(1)
    {
        n_recv = recvfrom(listenfd, message, 100, 0, (struct sockaddr *)&cliaddr, &clilen);
        for (i = 0; i < n_recv; i++)
        {
            message[i] = toupper(message[i]);
        }
        sendto(listenfd, message, n_recv, 0, (struct sockaddr *)&cliaddr, sizeof(servaddr)); 
    }
    close(listenfd);
    return 0;
}

2、C

/*
1.创建一个socket,用函数socket()
2.绑定IP地址、端口等信息到socket上,用函数bind()
3.设置对方的IP地址和端口等属性
4.发送数据,用函数sendto()
5.关闭网络连接
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define PORT 8000

int main(int argc, char const *argv[])
{
    if (argc != 2)
    {
        printf("need server IP!\n");
        exit(0);
    }
    int sockfd;
    struct sockaddr_in servaddr;
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(argv[1]);
    servaddr.sin_port = htons(PORT);

    char sendline[100];
    char recvline[100];
    int n_recv;

    while(1)
    {
        fgets(sendline, 100, stdin);
        int addrlen=sizeof(servaddr);
        sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, addrlen);
        n_recv = recvfrom(sockfd, recvline, 100, 0, (struct sockaddr *)&servaddr, &addrlen);
        
        recvline[n_recv] = '\0';
        printf("recv %d char!\n", n_recv);
        fputs(recvline, stdout);
    }
    close(sockfd);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛奶奥利奥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值