资源描述
资料内容仅供您学习参考,如有不当之处,请联系改正或者删除。
计算机网络
课程设计报告
网络连天程序的设计与实现
姓 名: 李坚
学 号:
班 级: 计算机002
指导老师: 文宏
湖南科技大学计算机科学与工程学院
9月
一、 课程设计题目
利用Windows Socket编程实现局域网的聊天程序, 要求能实现消息的发送和接收, 以及聊天软件的细节问题。
二、 题目分析
拿到题目之后先来了解windows socket连接的过程与相关的API函数。按照题目的要求, 我简单的分析了下并做了初步的设计: 利用tcp协议建立连接, 这样服务器和客户端分离, 服务端先启动并监听端口, 客户端启动之后连接服务端建立连接, 接着收发聊天信息。当任意一方连接断开的时候给出适当的提示并结束程序。
因为功能比较简单, 因此设计起来还是比较容易的。实际的操作中我遇到了很多新奇的问题并经过一些方案修改或者实现了最终的功能。
三、 设计步骤
1、 熟悉网络编程概念以及一些基本知识
2、 在windows 的编程环境下熟悉了常见socket函数
3、 先整体再局部顺序设计程序
4、 调试并修改程序, 使之实现设计要求
5、 测试程序, 从中找出程序缺陷和可改进内容
6、 重复修改和测试, 以达到自己理想的功能
7、 程序评定测试
8、 撰写设计报告
四、 设计过程
第一个版本并未实现收发同步, 只是简单的阻塞式通信。因为没有用到多线程, 因此在程序监听网络数据写入时不能监听键盘输入, 因此只能发一条后接一条, 其中的问题可想而知。
第二个版本用多线程实现了同时收发问题, 在连接建立后新建一个线程用来等待键盘输入, 而主体线程等待网络输入, 当网络输入错误时( 连接断开) , 结束线程并作下一步处理。这个版本就上个版本改进很多, 主要能够即时在屏幕输出接收到的消息, 同时也出现了另外一个缺陷: 当键盘输入到一半时程序收到了网络的信息, 这个时候程序的做法是直接输出这条信息, 这导致我们键盘输入的信息被切断, 很不人性化。
第三个版本, 也就是当前评测的版本, 这个版本利用临界值来锁定屏幕资源, 让程序在同一时刻只能一个程序拥有屏幕控制权, 这样保证了不会交叉显示。另外我用自己的输入输出代替了原有的scanf和printf, 这样使读写更安全可靠。同时改进的还有等待机制, 在服务器启动之后立即监听本机6000端口, 建立连接之后直接开辟线程等待输入。而客户端启动时要输入目的机的ip地址, 连接完成时打印欢迎信息并开始进入聊天。聊天结束(断开)之后能够重新输入目的机器ip地址以建立新连接。
五、 调用顺序图
服务端
Socket
Blind
Accept
Recv
Send
Close
Print
Listen
Socket
客户端
Connect
Recv
Print
Send
Close
六、 源代码
1、 共有文件soc.h ( 调试用文件)
#include <time.h>
void getime(char *s_tim){
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime (&rawtime );
sprintf(s_tim,"%02d:%02d:%02d",timeinfo->tm_hour,timeinfo->tm_min,timeinfo->tm_sec);
return ;
}
2、 服务端server.cpp
#include <stdio.h>
#include <Winsock2.h>
#include <windows.h>
#include <conio.h>
#include "socs.h"
#pragma comment(lib,"ws2_32.lib")
DWORD WINAPI gotsListen(LPVOID);
DWORD WINAPI setsListen(LPVOID);
void geta(char *s);
void puta(char*,int);
void getime(char *);
CRITICAL_SECTION g_cs;
CRITICAL_SECTION t_cs;
char bufer[1000];
int buflen;
bool linked;
void main(void){
HANDLE gotHandle;
HANDLE setHandle;
WORD wVerR;
WSADATA wsD;
wVerR=MAKEWORD(1,1);
if(WSAStartup(wVerR,&wsD))
return ;
if(LOBYTE(wsD.wVersion)!=1||HIBYTE(wsD.wVersion)!=1){
WSACleanup();
return ;
}
SOCKET scSr=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN adrSr;
adrSr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
adrSr.sin_family=AF_INET;
adrSr.sin_port=htons(6000);
bind(scSr,(SOCKADDR *)&adrSr,sizeof(SOCKADDR));
listen(scSr,5);
SOCKADDR_IN adrCl;
int len=sizeof(SOCKADDR);
while(true){
printf("bind[%d] success!\n",6000);
SOCKET scCon=accept(scSr,(SOCKADDR *)&adrCl,&len);
char s_adr[100];
strcpy(s_adr,inet_ntoa(adrCl.sin_addr));
linked=true;
printf("link[%s] be created!\n",s_adr);
InitializeCriticalSection(&t_cs);
gotHandle=CreateThread(NULL,0,&gotsListen,&scCon,0,NULL);
setHandle=CreateThread(NULL,0,&setsListen,&scCon,0,NULL);
while(linked){
Sleep(10);
}
TerminateThread(setHandle,NULL);
CloseHandle(gotHandle);
CloseHandle(setHandle);
DeleteCriticalSection(&t_cs);
closesocket(scCon);
printf("\nlink[%s] closed!\n",s_adr);
}
closesocket(scSr);
return ;
}
DWORD WINAPI gotsListen(LPVOID lpParam)
{
char gots[1000];
while(linked)
{
// EnterCriticalSection(&g_cs);
if(recv(*(SOCKET *)lpParam,gots,1000,0)==-1)break;
// LeaveCriticalSection(&g_cs);
puta(gots,1);
}
linked=false;
return 0;
}
DWORD WINAPI setsListen(LPVOID lpParam){
while(linked){
geta(bufer);
// EnterCriticalSection(&g_cs);
send(*(SOCKET *)lpParam,bufer,strlen(bufer)+1,0);
// LeaveCriticalSection(&g_cs);
puta(bufer,0);
}
ExitThread(NULL);
return 0;
}
void puta(char *s,int sta){
int i=1;
char s_tim[16];
getime(s_tim);
switch(sta){
case 0:
EnterCriticalSection(&t_cs);
while(i--)
printf("\b \b");
printf("[local] %s\n %s\n",s_tim,s);
LeaveCriticalSection(&t_cs);
break;
case 1:
EnterCriticalSection(&t_cs);
i+=buflen;
while(i--)
printf("\b \b");
printf("[NET] %s\n %s\n",s_tim,s);
printf(">%s",bufer);
LeaveCriticalSection(&t_cs);
break;
}
};
void geta(char *s){
char c;
EnterCriticalSection(&t_cs);
printf(">");
bufer[buflen=0]='\0';
LeaveCriticalSection(&t_cs);
while(c=getch()){
if(c>31&&c<127){
EnterCriticalSection(&t_cs);
s[buflen++]=c;
s[buflen]='\0';
printf("%c",c);
LeaveCriticalSection(&t_cs);
}else{
switch(c){
case 13:
EnterCriticalSection(&t_cs);
while(buflen--)
printf("\b \b");
LeaveCriticalSection(&t_cs);
return;
case 8:
EnterCriticalSection(&t_cs);
if(buflen){
buflen--;
printf("\b \b");
s[buflen]='\0';
}
LeaveCriticalSection(&t_cs);
break;
}
}
}
}
3、 客户端 client.cpp
#include <stdio.h>
#include <Winsock2.h>
#include <windows.h>
#include <conio.h>
#include "socs.h"
#pragma comment(lib,"ws2_32.lib")
DWORD WINAPI gotsListen(LPVOID);
DWORD WINAPI setsListen(LPVOID);
void geta(char *);
void puta(char*,int);
void getime(char *);
CRITICAL_SECTION t_cs;
char bufer[1000];
int buflen;
bool linked;
void main(void){
HANDLE gotHandle;
HANDLE setHandle;
char s_adr[10];
WSADATA wsD;
if(WSAStartup(MAKEWORD(1,1),&wsD))
return ;
if(wsD.wVersion!=MAKEWORD(1,1)){
WSACleanup();
return;
}
SOCKET scSr=socket(AF_INET,SOCK_STREAM,0);
while(true){
printf("Ready to Connect:");
scanf("%s",s_adr);
SOCKADDR_IN adrSr;
adrSr.sin_addr.S_un.S_addr=inet_addr(s_adr);
adrSr.sin_family=AF_INET;
adrSr.sin_port=htons(6000);
linked=!connect(scSr,(SOCKADDR *)&adrSr,sizeof(SOCKADDR));
if(linked){
printf("link[%s] created success!\n",s_adr);
InitializeCriticalSection(&t_cs);
gotHandle=CreateThread(NULL,0,&gotsListen,&scSr,0,NULL);
setHandle=CreateThread(NULL,0,&setsListen,&scSr,0,NULL);
while(linked){
Sleep(10);
}
TerminateThread(setHandle,NULL);
TerminateThread(gotHandle,NULL);
CloseHandle(gotHandle);
CloseHandle(setHandle);
DeleteCriticalSection(&t_cs);
}else{
printf("Failed to connect %s,check it and try again!\n",s_adr);
}
closesocket(scSr);
printf("\nlink[%s] be closed!\n",s_adr);
}
return ;
}
DWORD WINAPI gotsListen(LPVOID lpParam)
{
char gots[10000];
while(linked)
{
// puts("running");
// EnterCriticalSection(&g_cs);
if(recv(*(SOCKET *)lpParam,gots,1000,0)==-1)break;
// LeaveCriticalSection(&g_cs);
puta(gots,1);
}
linked=false;
ExitThread(NULL);
return 0;
}
DWORD WINAPI setsListen(LPVOID lpParam){
while(linked){
geta(bufer);
// EnterCriticalSection(&g_cs);
send(*(SOCKET *)lpParam,bufer,strlen(bufer)+1,0);
// LeaveCriticalSection(&g_cs);
puta(bufer,0);
}
ExitThread(NULL);
return 0;
}
void puta(char *s,int sta){
int i=2;
char s_tim[16];
getime(s_tim);
switch(sta){
case 0:
EnterCriticalSection(&t_cs);
while(i--)
printf("\b \b");
printf("[local] %s\n %s\n",s_tim,s);
LeaveCriticalSection(&t_cs);
break;
case 1:
EnterCriticalSection(&t_cs);
i+=buflen;
while(i--)
printf("\b \b");
printf("[NET] %s\n %s\n",s_tim,s);
printf(">%s",bufer);
LeaveCriticalSection(&t_cs);
break;
}
};
void geta(char *s){
char c;
EnterCriticalSection(&t_cs);
printf(">");
bufer[buflen=0]='\0';
LeaveCriticalSection(&t_cs);
while(c=getch()){
if(c>31&&c<127){
EnterCriticalSection(&t_cs);
s[buflen++]=c;
s[buflen]='\0';
printf("%c",c);
LeaveCriticalSection(&t_cs);
}else{
switch(c){
case 13:
EnterCriticalSection(&t_cs);
while(buflen--)
printf("\b \b");
LeaveCriticalSection(&t_cs);
return;
case 8:
EnterCriticalSection(&t_cs);
if(buflen){
buflen--;
printf("\b \b");
s[buflen]='\0';
}
LeaveCriticalSection(&t_cs);
break;
}
}
}
}
七、 调试运行
本实例在同一ip和不同分别做了测试, 运行状况达到了预期的效果, 下面是相关的几张运行截图:
服务器以及客户端启动的画面
建立连接之后提示信息以及命令提示符
客户端向服务器发送信息
下面来做一系列的测试查看程序防交叉输出的功能:
1、 连接之后服务端在写一些内容
2、 客户端发送了一条消息, 服务端将消息插入, 并继续等待输入端输入
3、 服务器继续编辑文本并发送( 注意服务器在收到消息之后能够修改输入的内容)
断开连接
八、 心得体会
经过了两个星期的学习, 这次课程设计基本圆满完成了, 这次课程设计我收获了很多。在以前的程序设计中我从没有用到过网络连接, 这使我的代码在功能方面的限制非常严重, 我曾尝试着用其它的方式来实现网络功能, 可是多少都有些缺陷。写完了这个课程设计之后我对程序设计的兴趣更加浓烈了, 我感觉我能用它来实现的功能也扩大了很多。
这次课程设计给我带来了很多经验, 我以前单纯的以为网络编程就是用几个api就能够完成, 真正操作起来会发现网络编程的这种特性会带来很多单机程序不可能遇到的问题, 让我明白了只有真正的动手实践才是了解技术和学习技术的最佳途径。
这次课程设计中老师的耐心指导给我很大的帮助, 刚接触网络编程遇到了各种错误和问题, 老师都一一帮我指出并解决了。感觉我现在还很不足, 还要加倍努力才能实现我的程序梦想。另外老师也给我提出了很切合实际的几点要求, 让我有更明确的学习目标, 也有了更多的学习动力。
展开阅读全文