Liunx C 编程之多线程与Socket
多线程
pthread.h是linux特有的头文件,POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。Windows操作系统也有其移植版pthreads-win32。
创建线程
1.pthread_create 创建一个新线程并使之运行起来。该函数可以在程序的任何地方调用包括线程内,线程是没有依赖关系的。
2.一个进程可以创建的线程最大数量取决于系统实现
3. pthread_create参数:
thread:返回一个不透明的,唯一的新线程标识符。
attr:不透明的线程属性对象。可以指定一个线程属性对象,或者NULL为缺省值。
start_routine:线程将会执行一次的C函数。
arg: 传递给start_routine单个参数,传递时必须转换成指向void的指针类型。没有参数传递时,可设置为NULL。
pthread_create (threadid,attr,start_routine,arg)
结束线程
1.结束线程的方法有一下几种:
线程从主线程(main函数的初始线程)返回。
线程调用了pthread_exit函数。
其它线程使用 pthread_cancel函数结束线程。
调用exec或者exit函数,整个进程结束。
2.如果main()在其他线程创建前用pthread_exit()退出了,其他线程将会继续执行。否则,他们会随着main的结束而终止。
pthread_exit (status)
int pthread_cancel(pthread_t threadid);
等待线程状态
pthread_join (threadid,status)
例子:
#include <stdio.h>
#include <pthread.h> //liunx线程头文件
#include <stdlib.h>
//线程
void *thread1_proc(void *arg)
{
int i=*(int *)arg; //取出内容
free(arg);//释放空间
while(i<)
{
printf("thread1:%-5d",i);
sleep();//延时等待两秒
i++;
}
printf("Thread1 finished!\n");
pthread_exit(NULL);//终止当前线程
}
void main()
{
pthread_t thread1;
int *ixi=(int *)malloc(sizeof(int));//在堆中申请一块内容
*ixi=; //存在内容
if(pthread_create(&thread1,NULL,thread1_proc,(void *)ixi)!=)//创建线程1并传递参数
perror("Create thread failed:");//创建错误时执行
//终止当前线程,此时会子线程会执行完毕,相当于在此处join所有子线程一样
pthread_exit(NULL);//(1)结束主
// pthread_join(thread1,NULL);//(2)可替换上一条
printf("主线程已经退出,本条不执行"); //(1)不执行,(2)执行该条
}
多线程共享资源
共享资源时可能会出现操作未完成而被另一个线程打破,造成资源存取异常
锁
定义变量
#include <pthread.h>
pthread_mutex_t lockx;
初始化
pthread_mutex_init(&lockx,NULL);
上锁与解锁
pthread_mutex_lock(&lockx);//上锁
//独立资源
//代码块
pthread_mutex_unlock(&lockx);//解锁
信号量
实现循序控制
定义变量
#include <semaphore.h>
sem_t can_scanf;
初始化
sem_init(&can_scanf,,);
PV操作
sem_wait(&can_scanf);//等待信号量置位并进行减一操作
sem_post(&can_scanf); //信号量加一 操作
例子
主线程负责从键盘获取两个整数,子线程1负责对这两个整数完成求和运算并把结果打印出来,子线程2负责对这两个整数完成乘法运算并打印出来。三个线程要求遵循如下同步顺序:
1.主线程获取两个数;
2.子线程1计算;
3.子线程2计算;
4.转(1)
#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <stdlib.h>
sem_t can_add;//能够进行加法计算的信号量
sem_t can_mul;//能够进行输入的信号量
sem_t can_scanf;//能够进行乘法计算的信号量
int x,y;
void *thread_add(void *arg)//加法线程入口函数
{
while()
{
sem_wait(&can_add);
printf("%d+%d=%d\n",x,y,x+y);
sem_post(&can_mul);
}
}
void *thread_mul(void *arg)//乘法线程入口函数
{
while()
{
sem_wait(&can_mul);
printf("%d*%d=%d\n",x,y,x*y);
sem_post(&can_scanf);
}
}
int main()
{
pthread_t tid;
int arg[];
//信号量初始化
sem_init(&can_scanf,,);
sem_init(&can_add,,);
sem_init(&can_mul,,);
if(pthread_create(&tid,NULL,thread_add,NULL)<)
{
printf("Create thread_add failed!\n");
exit();
}
if(pthread_create(&tid,NULL,thread_mul,NULL)<)
{
printf("Create thread_mul failed!\n");
exit();
}
while()
{
sem_wait(&can_scanf);//等待信号量置位并进行减一操作
printf("Please input two integers:");
scanf("%d%d",&x,&y);
sem_post(&can_add);//信号量加一 操作
}
}
Socket编程
数据包的发送
(1)TCP(write/send)
基于流的,没有信息边界,所以发送的包的大小没有限制;但由于没有信息边界,就得要求要求应用程序自己能够把逻辑上的包分割出来。
(2)UDP(sendto)
基于包的,应用层的包在由下层包的传输过程中因尽量避免有分片——重组的发生,否则丢包的概率会很大,在以太网中,MTU的大小为46——1500字节,除掉IP层头、udp头,应用层的UDP包不要超过1472字节。鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时。 最好将UDP的数据长度控制在548字节(576-8-20)以内.
数据包的接收
(1)TCP(read/recv)
如果协议栈缓冲区实际收到的字节数大于所请求的字节数,则返回实际要读取的字节数,剩余未读取的字节数下次再读;
如果协议栈缓冲区实际收到的字节数小于所请求的字节数,则返回所能提供的字节数;
(2)UDP(recvfrom)
如果协议栈缓冲区实际收到的字节数大于所请求的字节数,在linux下会对数据报进行截段,并丢弃剩下的数据;
如果协议栈缓冲区实际收到的字节数小于所请求的字节数,则返回所能提供的字节数;
注意点
当发送函数返回时,并不表示数据包已经到达了目标计算机,仅仅说明待发送的数据包被协议栈给接收了;
UDP的数据包要么被接收,要么丢失;TCP的数据报一定会无差错按序交付给对方
TCP
服务端
1、连接WiFi或者开启AP,使模块接入网络
2、socket 创建一个套接字
socket可以认为是应用程序和网络之间信息传输通道,所以TCP编程服务端、客户端的第一步就是要建立这个信息传输的通道,主要通过socket函数完成。
3、 Bind socket信息
给在第一步中所创建的socket显式指定其ip地址和端口号(bind)
其中结构体为:
//设置server的详情信息
struct sockaddr_in server_addr,client_addr;
u32_t sock_size=sizeof(struct sockaddr_in);
server_addr.sin_family = AF_INET; //IPV4
server_addr.sin_port = htons(); //端口
//绑定本机的所有IP地址htonl(INADDR_ANY),确定某个inet_addr(“172.16.4.1”)
server_addr.sin_addr.s_addr =htonl(INADDR_ANY);
bind(connect_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
4、 listen确定请求队列的最大值
5、 accept等待接入
此函数为所有网络函数中最难理解的一个函数,它的调用将意味着服务端开始处理外来请求,如果没有外来请求(也就是没有listen到请求进来)默认情况下则阻塞;当有外来请求时会新产生一个soket,并返回其描述符,应用程序将在这个新的socket上和请求者进行会话(读、写该socket),原套接字sockfd则继续侦听
6、 send
当send返回时,并不是表示数据已经发送到了对方,而仅仅表示数据已经到了协议栈的缓冲区中。最后一个值在ESP32中不可用
7、 recv
默认情况下,当没有可接收的数据时则阻塞,参数len表示最多接收多少个字节数, 成功的接受的字节数完全可以小于len。最后一个值在ESP32中不可用
客户端
1、连接WiFi或者开启AP,使模块接入网络
2、socket 创建一个套接字,参考服务器
3、是指向服务端发起连接请求(请求成功的前提是服务端已经进入了accept状态)
结构体参数
//设置server的详情信息
struct sockaddr_in server_addr,client_addr;
u32_t sock_size=sizeof(struct sockaddr_in);
server_addr.sin_family = AF_INET; //IPV4
server_addr.sin_port = htons(); //端口
//绑定本机的所有IP地址htonl(INADDR_ANY),确定某个inet_addr(“172.16.4.1”)
server_addr.sin_addr.s_addr = inet_addr("192.168.43.21");
int ret=connect(client_fd,(struct sockaddr*)&server_addr,sock_size);//连接服务器
4、recv 和 send
服务器示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#define MAXCONN 8
int main()
{
int listen_fd,comm_fd;
int ret;
int i=;
struct sockaddr_in server_addr,client_addr;
int sock_size=sizeof(struct sockaddr_in);
listen_fd=socket(AF_INET,SOCK_STREAM,);//创建一个socket,参数(IPV4,TCP,0)
if(listen_fd<)
{
perror("Failed to create socket:");
return -;
}
bzero(&server_addr,sock_size);//清零server_addr
server_addr.sin_family=AF_INET;//IPV4
server_addr.sin_port=htons();//端口
server_addr.sin_addr.s_addr=INADDR_ANY;//绑定主机全部网络地址
setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));//设置套接字关联的选 项
ret=bind(listen_fd,(struct sockaddr*)&server_addr,sock_size);//网络主机绑定
if(ret==)
{
printf("Bind Successfully!\n");
}
ret=listen(listen_fd,MAXCONN);//确定最大监听数
if(ret==)
{
printf("Listen Successfully!\n");
}
while((comm_fd=accept(listen_fd,(struct sockaddr*)&client_addr,&sock_size))>=)//阻塞并等待接入
{
char ipaddr[];
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ipaddr,);//网络地址符转换
printf("连接进入:%s\n",ipaddr);
while()
{
char buff[];
int count;
count=read(comm_fd,buff,);//读数据,接收
if(count>)//判断接收的字节数是否大于零
{
buff[count]=;//截断字符串
printf("收到来自 %s 的数据:%s\n",ipaddr,buff);
if(strncmp(buff,"quit",)==)//判断退出条件
{
printf("%s已经退出退出,等待下一个连接\n",ipaddr);
break;//退出此个连接,进行下一个连接接入
}
write(comm_fd,buff,count);//写数据,发送
}
else
{
printf("A talking is over!\n");
break; //客户端断开
}
}
}
close(listen_fd);//关闭连接
return ; }
客户端示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <string.h>
int main(int argc,char **argv)
{
int client_fd;
int ret;
int count;
struct sockaddr_in server_addr;
char buf[];
char recv_buf[];
int sock_size=sizeof(struct sockaddr_in);
if(argc<)
{
printf("Usage:./client serverip\n");
return ;
}
bzero(&server_addr,sock_size);//清零server_addr
client_fd=socket(AF_INET,SOCK_STREAM,);//创建一个socket,参数(IPV4,TCP,0)
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons();
server_addr.sin_addr.s_addr=inet_addr(argv[]);
ret=connect(client_fd,(struct sockaddr*)&server_addr,sock_size);//连接服务器
if(ret<)
{
perror("Failed to connect:");
return -;
}
printf("Connect successfully!\n");
while()
{ printf("请输入要发送的内容:");
fgets(buf,,stdin);//从键盘获取字符串
ret=write(client_fd,buf,strlen(buf));//写数据,发送
if(ret<=)
break;
if(strncmp(buf,"quit",)==){
printf("程序退出\n");
break;
}
count=read(client_fd,recv_buf,);//读数据,接收
if(count>)
{
recv_buf[count]=;//截断接收的字符串
printf("Echo:%s\n",recv_buf);
}
else
{
break;//服务器断开
}
}
close(client_fd);//关闭连接
return ; }
UDP
服务器
1、 创建socket
2、 调用函数设置udp播
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
头文件:<sys/socket.h>
level : 选项级别(例如SOL_SOCKET)
optname : 选项名(例如SO_BROADCAST)
optval : 存放选项值的缓冲区的地址
optlen : 缓冲区长度
返回值:成功返回0 失败返回-1并设置errno
3、 绑定服务器信息bind
4、 数据收发
数据发送
int sendto(int sockfd, const void *msg, size_t len, int flags, const struct sockaddr *to, int tolen);
返回:大于0-成功发送数据长度;--出错;
UDP套接字使用无连接协议,因此必须使用sendto函数,指明目的地址;
msg:发送数据缓冲区的首地址;
len:缓冲区的长度;
flags:传输控制标志,通常为0;
to:发送目标;
tolen: 地址结构长度——sizeof(struct sockaddr)
数据接收
int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlen);
返回:大于0——成功接收数据长度;-——出错;
buf:接收数据的保存地址;
len:接收的数据长度
flags:是传输控制标志,通常为0;
from:保存发送方的地址
fromlen: 地址结构长度。
服务器示例
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
int main()
{
int sockfd;
int ret;
char buff[];
char ipaddr[];
struct sockaddr_in server_addr,client_addr;
int i=;
int sock_size=sizeof(struct sockaddr_in);
sockfd=socket(AF_INET,SOCK_DGRAM,);
if(sockfd<)
{
perror("Failed to socket:");
return -;
}
bzero(&server_addr,sock_size);
server_addr.sin_family=AF_INET;//服务器相关参数设置
server_addr.sin_port=htons();
server_addr.sin_addr.s_addr=INADDR_ANY;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
if(bind(sockfd,(struct sockaddr*)&server_addr,sock_size)<)//等待客户端接入,阻塞
{
perror("Failed to bind:");
return -;
}
while()
{
ret=recvfrom(sockfd,buff,,,(struct sockaddr*)&client_addr,&sock_size);//收到数据包
if(ret>)
{
buff[ret]=;
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ipaddr,);//网络地址符转换
printf("Receive a string from %s:%d,data:%s\n",ipaddr,client_addr.sin_port,buff);
if(strncmp(buff,"exit",)==){//退出
printf("Socket server exit ");
close(sockfd);//关闭socket
break;
}
sendto(sockfd,buff,ret,,(struct sockaddr*)&client_addr,sock_size);
}
}
close(sockfd);
}
客户端示例
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
int main(int argc,char **argv)
{
int client_fd;
int ret;
int count;
struct sockaddr_in server_addr,sock_addr;
char buf[];
char recv_buf[];
int sock_size=sizeof(struct sockaddr_in);
if(argc<)
{
printf("Usage:./udpclient serverip\n");
return ;
}
client_fd=socket(AF_INET,SOCK_DGRAM,);
bzero(&server_addr,sock_size);
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons();
server_addr.sin_addr.s_addr=inet_addr(argv[]);
while()
{
printf("In:");
fgets(buf,,stdin);
ret=sendto(client_fd,buf,strlen(buf),,(struct sockaddr*)&server_addr,sock_size);
if(ret<)
{
perror("Failed to sendto:");
break;
}
if(strncmp(buf,"exit",)==)
break;
count=recvfrom(client_fd,recv_buf,,,(struct sockaddr*)&sock_addr,&sock_size);
if(count>)
{
recv_buf[count]=;
printf("Echo:%s\n",recv_buf);
}
else
{
perror("Failed to recvfrom:");
break;
}
}
close(client_fd);
return ; }
参考:
https://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html
物联网网关开发技术(罗老师)
Liunx C 编程之多线程与Socket的更多相关文章
- 多线程Java Socket编程示例
package org.merit.test.socket; import java.io.BufferedReader; import java.io.IOException; import jav ...
- 并发编程~~~多线程~~~计算密集型 / IO密集型的效率, 多线程实现socket通信
一 验证计算密集型 / IO密集型的效率 IO密集型: IO密集型: 单个进程的多线程的并发效率高. 计算密集型: 计算密集型: 多进程的并发并行效率高. 二 多线程实现socket通信 服务器端: ...
- c/c++ 网络编程与多线程 编译参数
网络编程与多线程 编译参数 编译时要链接操作系统的pthread库 g++ -g socket01.cpp -std=c++11 -pthread 不加-pthread的话,出现下面的错误: term ...
- 《Unity 3D游戏客户端基础框架》多线程异步 Socket 框架构建
引言: 之前写过一个 demo 案例大致讲解了 Socket 通信的过程,并和自建的服务器完成连接和简单的数据通信,详细的内容可以查看 Unity3D -- Socket通信(C#).但是在实际项目应 ...
- 可扩展多线程异步Socket服务器框架EMTASS 2.0 (转自:http://blog.csdn.net/hulihui)
可扩展多线程异步Socket服务器框架EMTASS 2.0 (转自:http://blog.csdn.net/hulihui) 0 前言 >>[前言].[第1节].[第2节].[第3节]. ...
- TCP/IP网络编程之多线程服务端的实现(二)
线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...
- 网络编程之多线程——GIL全局解释器锁
网络编程之多线程--GIL全局解释器锁 一.引子 定义: In CPython, the global interpreter lock, or GIL, is a mutex that preven ...
- Python拾忆--多线程的socket服务器
阳光明媚的午后,想想最近要开始从写Java到写Python了,就随手打开电脑来体验一下Python与Java之间的不同吧~ 记得我还在上大二的时候,那个时候才开始学Java,最感兴趣的就是Java书最 ...
- 网络编程:Http通信与Socket通信
http://note.youdao.com/share/?id=f14d304548003f65e34255d3ddf9df31&type=note 网络编程:Http通信与Socket通信 ...
随机推荐
- jmeter性能测试前及测试后
压测前: 1.压力测试两种场景: 1)单场景,压测单个接口. 2)混合场景,多个接口关联压测. 2.压测时间: ...
- 半小时学会V语言
半小时学会V语言 1. V语言简介 V是一个静态类型.编译型的编程语言,目标是构建可维护软件.与Go语言相似,并受Oberon,Rust和Swift语言影响.V语言非常简单,只需要半小时就能学会这门语 ...
- python方法和函数集锦
方法的使用: 变量.方法名(参数) 函数的使用: 函数名(参数) 字符串 1.删除空白 rstrip(): 返回去掉尾部的空格后的字符串.(不改变原字符串) lstrip(): 去掉首部空格 stri ...
- Hive入门(一)
1 Hive中的数据定义 1.1 存储与创建 Hive会为每个数据库创建一个目录.数据库中的表以子目录的形式存储. 有一个例外是default中的表,因为这个库本身没有目录. 数据库的顶级目录是hiv ...
- mysql的数据存储
# pycharm 连接mysql import pymysql username = input("输入用户名:") pwd = input("输入密码:") ...
- LinkedList源码分析:JDK源码分析系列
如果本文中有不正确的地方请指出由于没有留言可以在公众号添加我的好友共同讨论. 1.介绍 LinkedList 是线程不安全的,允许元素为null的双向链表. 2.继承结构 我们来看一下LinkedLi ...
- 生产Server遭挖矿程序入侵,暴力占用CPU
区块链的火热,利益驱使必然导致不少PC或Server,被变成肉鸡,执行挖矿程序进行挖矿,进而导致我们正常的程序无法正常. (Centos7 Server)使用top命令查看服务器进程运行情况,发现几个 ...
- 奇袭(单调栈+分治+桶排)(20190716 NOIP模拟测试4)
C. 奇袭 题目类型:传统 评测方式:文本比较 内存限制:256 MiB 时间限制:1000 ms 标准输入输出 题目描述 由于各种原因,桐人现在被困在Under World(以下简称UW)中,而 ...
- 使用docker搭建gitlab服务器
简单记录Docker的使用和GitLab的搭建 Docker基础篇 没有sudo权限 安装docker 基础命令 docker安装mysql和配置 Dockerfile常用命令 制作镜像 发布镜像 容 ...
- [分享] 一款极简单的 BaseEntity CRUD 方法
前言 尝试过 ado.net.dapper.ef,以及Repository仓储,甚至自己还写过生成器工具,以便做常规CRUD操作. 它们日常操作不方便之处: 每次使用前需要声明,再操作: 很多人一个实 ...