资源描述
网络程序设计,一、winsocket (c/s) 二、 (b/s) 三、web service(windows客户端/web 服务器),AfxBeginThread(MyWorkerThread,(LPVOID)pString);,网络程序设计要考虑的问题,计算机网络程序设计因为涉及不同平台之间的信息交互(单点和多点),因此与单机上的程序设计有很大的不同。了解网络程序设计要注意的一些问题,有助于设计、编写高质量的网络应用程序。,1、并发环境下的网络编程,单进程应用与多进程或多线程的编程有着很大的区别。在多进程或多线程应用程序中,涉及到资源共享、进程或线程间的同步,因而要复杂的多。 在多进程或多线程应用中,使用的系统调用或函数必须是可重入的。哪些系统调用或系统函数是可重入的,这在不同的系统中是不同的。 在多线程应用中,对调用或函数的使用有很多限制。如,在Solaris操作系统中,在一个线程中关闭另一个线程正在使用的网络端口会导致应用程序“死掉”,而在Digital Unix中不会。,2、异构环境下的网络编程,网络通信常常是在多个平台之间进行,因此网络应用程序必须考虑不同平台之间的异构性,特别是不同平台上的数据格式的差异。,(1)字节顺序 不同的平台(cpu)以不同的方式存放一个二进制。最常见的有两种格式,大数字节序是高字节数据存放在低地址处,低字节数据存放在高地址处。 一个多字节值 0 xFECDBA98,内存从地址100开始存放。 例: FE | CD | BA | 98-对应地址100 | 101 | 102 | 103 小数字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处; 例: 98 | BA | CD | FE -对应地址100 | 101 | 102 |103,(2)字的长度,不同的实现对于相同的数据类型可能有不同的表示长度。如,64位操作系统与32位操作系统中,类型long int的长度是不一样的。对short,int的大小也没有明确的规定。不同的语言也不同。例:java中int是32位;标准c中int 是16位。,(3)字节定界问题(数据对齐),不同的平台上给结构(struct)或联合(union)打包的方式也是不同的。 例:struct char c;char hh; int i; s1; 语句sizeof(s1) (四字节对齐)在一些编译系统中是8;而在另一些编译系统中可能是12。,1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除; 2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding); 3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。,3、阻塞与非阻塞通信,可以将通信分为两种模式:阻塞和非阻塞。在网络编程时,选择通信模式也是一件很重要的事情。对于不同的通信协议,阻塞通信和非阻塞通信有不同的表现。在非阻塞模式下,不同操作系统不能完成的I/O操作返回的错误代码也是不一样的。 对于UDP协议而言,因为没有发送缓存,所有UDP协议即使在阻塞模式下也不会发生阻塞。 对于面向连接的协议,在连接建立阶段,被动方,在阻塞模式下,没有连接请求到达,则等待连接调用将阻塞直到有连接请求到达;在非阻塞模式下,如果没有连接请求到达,等待连接调用将直接返回;连接发起方总是会使调用它的进程阻塞。阻塞间隔最少等于到达服务器的一次往返时间。,通信模式对应用程序的设计方法也用直接的影响。在非阻塞模式下,应用程序不断地轮询查看是否有数据到达,这种方式效率低。可以采用多路复用技术(调用select函数或 poll函数),解决这个问题。在阻塞模式下,进程或线程在执行I/O操作时将被阻塞而不能执行其他工作。因此在单线程应用中不能使用这种模式。在多线程应用中比较适合采用阻塞模式,一个线程被阻塞不影响其他线程的工作 通信模式的选择与操作系统的类型也有关系。例如,在非占先的操作系统中,应用程序应量不要使用阻塞模式工作。,4、服务类型的选择 5、差错处理 任何应用程序都要进行差错处理,即检查函数返回的调用结果的正确性并作出相应的处理。,二、windows多线程,线程分为两种类型: 一种是工作者线程(worker thread,没有运行消息循环)和用户界面线程(user-interface thread,运行消息循环),windows forms应用程序,主线程是用户界面线程。 这两种线程类型MFC库都支持,都基于CwinThread类,均可使用AfxBeginThread函数来创建。,访问数据时,使用多线程应用程序比使用单线程应用程序要更加小心。因为在多线程应用程序中同时有多个独立的执行路径正在使用,算法或数据或两者都必须注意,可以有一个以上的线程同时使用数据。 由于大小和性能原因,MFC 对象在对象级别不是线程安全的,而只是在类级别线程安全。这表明可以有两个独立的线程操作两个不同的 CString 对象,但不能有两个线程操作同一个 CString 对象。如果一定要有多个线程操作同一个对象,请用适当的 Win32 同步机制(如临界区)保护此类访问权。,Class aa public: int x; set(int vx) x=vx; Int get()return x; 有一个线程函数要调用set对x进行赋值,然后调用get获取x。 假如有一个主程序调用该线程函数产生了两个子线程A,B,并且产生了aa的一个实例,这时,实例是被A,B共享的。而且当A线程调用了set之后,被挂起,而执行B线程也调用了set,在执行A,这时A得到的x的值是B设置,而不是A设置的。,创建和使用线程,首先引用Using system.threading 创建一个用户界面线程,要做四件事 1、需要定义一个入口点(一个不带参数和反回值的函数) 2、实例化引用该方法的委托 3、创建一个线程 4、使用上述线程的start()方法启动线程,1 、Class Workerclass public void dosomework() /code to do the work here Workerclass worker =new Workerclass(); 2、ThreadStart workerThreadstart =new ThreadStart(worker. dosomework(); 3、Thread workerthread = new Thread(workerThreadstart); 4、workerthread.Start();,终止线程 Workerthread.Abort(); 线程同步 其方法与操作系统一样,三、线程用于网络程序并发,线程的特点: 动态创建,并发执行,私有局部变量(每个线程都有自已的私有堆栈),共享全局变量,共享文件描述符。 服务器的设计方案可分为两类:循环地处理请求和并发地处理请求。 循环地处理请求:当处理请求的时间小于创建一个线程所需的时间,一般用循环地处理请求的方案,在windows中使用异步选择机制。,最后,以一个操作系统的例子结束本讲: #include #include Int sum; Main() Sum=0; Switch(fork() Case 0: Sum =4; Printf(“the value of sum is %dn”,sum); Break; Default : Sum =6; Printf(“the value of sum is %dn”,sum); Break; Exit(0); 这段程序的输出是什么?,返回值: 负数:如果出错,则fork()返回-1,此时没有创建新的进程。最初的进程仍然运行。 零:在子进程中,fork()返回0 正数:在父进程中,fork()返回正的子进程的PID,Pid=fork(); /*此处,执行fork调用,创建了一个新的进程, 这个进程共享父进程的数据和堆栈空间等,这之后的代码指令为子进程创建了一个拷贝。 fork 调用是一个复制进程,fork 不象线程需提供一个函数做为入口, fork调用后,新进程的入口就在 fork的下一条语句。*/,作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程。,Thread 类最重要的方法是 run(),它为Thread 类的方法 start() 所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!,方法一:继承 Thread 类,重写方法 run(),我们在创建Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。,public class TwoThread extends Thread public void run() for ( int i = 0; i 10; i+ ) System.out.println(New thread); public static void main(String args) TwoThread tt = new TwoThread(); tt.start();/启动派生类的实例线程 for ( int i = 0; i 10; i+ ) System.out.println(Main thread); ,方法二:实现 Runnable 接口 Runnable 接口只有一个方法 run(),我们声明自己的类实现 Runnable 接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是 Runnable 接口并没有任何对线程的支持,我们还必须创建 Thread 类的实例,这一点通过 Thread 类的构造函数public Thread(Runnable target);来实现。 参数Runnable target是一个实现了接口的那个类的实例。,public class MyThread implements Runnable int count=1, number; public MyThread(int num) number = num; System.out.println(创建线程 + number); public void run() /实现了接口的run()方法 while(true) System.out.println(线程 + number + :计数 + count); if(+count= 6) return; public static void main(String args) for(int i = 0; i 5; i+) new Thread(new MyThread(i+1).start(); ,1、研究各种系统的字节顺序和结构体变量的字节数的差异。 2、研究用多线程实现并发的程序设计,
展开阅读全文