本文主要介绍了C语言中socket编程的基础知识,包括最基本的客户端发送和服务器接收数据的实现。有需要的朋友可以参考一下。
什么是插座?
你经常听到人们谈论“插座”,也许你不知道它的确切含义。现在让我告诉你:它是一种通过使用标准Unix文件描述符与其他程序通信的方法。什么?你可能听过一些Unix黑客说,“啊,Unix中的一切都是文件!”那个家伙可能在谈论这样一个事实,当一个Unix程序执行任何形式的I/O时,这个程序都在读或写一个文件描述符。文件描述符只是一个与打开的文件相关联的整数。然而,这个文件可能是网络连接、FIFO、管道、终端、磁盘上的文件或其他东西。Unix中的一切都是文件!所以,当你想与互联网上的其他程序通信时,你需要使用文件描述符。你必须明白你刚才说的话。现在你可能会想,“那么我从哪里获得网络通信的文件描述符呢?”反正我要回答这个问题:你用系统调用socket(),系统返回socket描述符,然后你通过它进行send()和recv()调用。“但是……”,你可能会有很大的疑惑。"如果是文件描述符,为什么不调用read()和write()进行套接字通信?"简单的回答就是:“可以用!”。详细的回答是:“可以,但是使用send()和recv()可以更好地控制数据传输。”有这样一种情况:在我们的世界里,插座有很多种。有DARPA互联网地址(互联网套接字)、本地节点路径名(Unix套接字)、CCITT X.25地址(可以完全忽略X.25套接字)。也许在你的Unix机器上还有其他的。这里只说第一个:互联网插座。
两种类型的互联网插座:
你什么意思?互联网插座有两种?是的不,我在撒谎。其实还有很多,只是不想吓到你。这里只说两种。除了这些,我打算另外介绍的‘Raw Sockets’也很强大,值得参考。
那么这两种类型是什么呢?一个是“流套接字”(流格式),另一个是“数据报套接字”(包格式)。以后讲到' SOCK_STREAM '和' SOCK_DGRAM '我们也会用到。数据套接字有时被称为“无连接套接字”(如果你真的想连接,可以使用connect()。流式套接字是一种可靠的双向通信数据流。如果你按顺序向套接字输出“1,2”,那么它们会按顺序“1,2”到达另一端。它们交付时没有错误,并且有自己的错误控制,这里就不讨论了。
什么是使用流式套接字?你可能听说过telnet,是吗?它使用流式套接字。您需要输入的字符按顺序到达,不是吗?同样,WWW浏览器使用的HTTP协议也使用它们来下载页面。事实上,当你通过80端口telnet到一个WWW站点,然后输入“GET pagename”,你也可以获得HTML内容。为什么流式套接字可以实现高质量的数据传输?这是因为它使用“传输控制协议”,也称为“TCP”(详情请参考RFC-793。)TCP控制你的数据按顺序到达,没有错误。
错误。可能你听到“TCP”是因为你听到了“TCP/IP”。这里的IP指的是“互联网协议”(请参考RFC-791。)IP只是处理互联网路由。
数据报套接字呢?为什么叫无连接?为什么不靠谱?有一些事实:如果你发送一个数据报,它可能会到达,它可能会出现故障。如果它到达,那么在这个包裹里面没有错误。数据报也使用IP进行路由,但它不使用TCP。它使用用户数据报协议,也称为UDP(请参考RFC-768。)
为什么它们是断开的?主要是因为它不像streaming socket那样维护连接。您只需构建一个数据包,用目标信息构建一个IP报头,然后将它发送出去。不需要连接。它们通常用于传输数据包到数据包的信息。简单的应用有tftp,bootp等等。
你可能会想,“如果数据丢失了,这些程序怎么能正常工作呢?”我的朋友,每个程序在UDP上都有自己的协议。比如tftp协议发送的每一个包都被接受,接收方必须发回一个包说“我收到了!”(“命令正确响应”也称为“ACK”数据包)。如果发送方在一定时间内(如5秒)没有收到应答,它将重新发送应答,直到收到ACK。在实现SOCK_DGRAM应用程序时,这个ACK过程非常重要。
简单的发送和接收实现
服务器接收代码:
#包含Winsock2.h
#pragma注释(lib,' Ws2_32.lib ')
#包含stdio.h
#包含内存. h
void main()
{
WSAData wsd
WSAStartup(MAKEWORD(2,0),wsd);
套接字s=NULL
s=socket(AF_INET,SOCK_STREAM,IP proto _ TCP);
struct sockaddr _ in ch
memset(ch,0,sizeof(ch));
ch.sin _ family=AF _ INET
ch . sin _ addr . s _ addr=in addr _ ANY;
ch . sin _ port=htons(1041);
int b=bind(s,(struct sockaddr *) ch,sizeof(ch));
#定义队列大小5
int l=listen(s,QUEUE _ SIZE);
Printf('正在监听本机的1041端口!\ n’);
SOCKET sc=accept(s,0,0);
Printf('客户端已经连接到本机的1041端口!\ n’);
#定义BUF_SIZE 4096
int rece byt=0;
while(1)
{
char BUF[BUF _ SIZE];
receByt=recv(sc,buf,BUF_SIZE,0);
buf[rece byt]=' \ 0 ';
if(rece by 0)
{
Printf('收到的消息是:%s\n ',buf);
}
其他
{
Printf('接收消息结束!');
打破;
}
}
int IC=close socket(sc);
int is=close socket(s);
}
客户端发送的代码:
#包含Winsock2.h
#pragma注释(lib,' Ws2_32.lib ')
#包含stdio.h
#包含内存. h
#包含字符串. h
void main()
{
WSAData wsd
WSAStartup(MAKEWORD(2,0),wsd);
套接字s=NULL
s=socket(AF_INET,SOCK_STREAM,IP proto _ TCP);
struct sockaddr _ in ch
memset(ch,0,sizeof(ch));
ch.sin _ family=AF _ INET
ch . sin _ addr . s _ addr=inet _ addr(' 127 . 0 . 0 . 1 ');
ch . sin _ port=htons(1041);
int c=connect(s,(struct sockaddr *) ch,sizeof(ch));
Printf('已经连接到服务器的1041端口!现在您可以向服务器发送消息了!\ n’);
#定义BUF_SIZE 4096
char info[1024],BUF[BUF _ SIZE];
while(1)
{
获取(信息);
if(info[0]=='\0 ')
打破;
strcpy(buf,info);
int nsend=send(s,buf,strlen(buf),0);
}
int IC=close socket(s);
}
代码已经优化,集成了多线程。接收和发送放在同一个文件中,通过参数方式调用发送和接收模块。增加了s句柄(或对象)来判断创建socket时返回值是否为invalid _ SOCKET,SOCKET绑定操作的返回值是否为SOCKET_ERROR。其他socket操作也要判断SOCKET_ERROR,这样才能保证程序的稳定性。在这里,只是测试代码,所以不要写那么多,剩下的就看你自己了。
#包含Winsock2.h
#pragma注释(lib,' Ws2_32.lib ')
#包含stdio.h
#包含内存. h
#包含字符串. h
#include pthread.h
void Receive();
void Send();
void creat thread();
套接字s=NULL
pthread _ t t[1000];
int thread count=0;
void main(int argc,char* argv[])
{
Printf('本项目学号:713901040041 \ n ');
Printf('程序描述:服务器和客户端是同一个程序,请用不同的参数运行。\ n’);
Printf ('receiver请使用r参数;请对发送方使用s参数。\ n’);
//printf('len : %d\n ',argc);
//printf('count %d\n ',argc);
//printf('value: %s\n ',argv[1]);
//printf('%d ',argv[1][0]==' r ');
如果(argc=1)
{
printf('请输入程序参数.\ n’);
退出(0);
}
if(argc1 argv[1][0]=='r ')
{
printf('运行接收.\ n’);
receive();
}
if(argc1 argv[1][0]=='s ')
{
printf('运行发送.\ n’);
send();
}
}
void* receiveWork(void * args)
{
SOCKET sc=accept(s,0,0);
if(sc==INVALID_SOCKET)
{
printf('sc错误');
}
创建线程();
printf(' -客户端已经连接到本机的%d线程连接!\n ',线程计数-2);
#定义BUF_SIZE 4096
int rece byt=0;
while(1)
{
char BUF[BUF _ SIZE];
receByt=recv(sc,buf,BUF_SIZE,0);
buf[rece byt]=' \ 0 ';
如果(接收0)
{
printf('线程接收的消息是:%s\n ',buf);
}
其他
{
printf('客户端已退出,');
打破;
}
}
int IC=关闭套接字(sc);
printf('服务器结束连接!\ n’);
返回空
}
void creatThread()
{
pthread_create(t[threadCount ],NULL,receiveWork,NULL);
}
无效接收()
{
WSAData wsd
WSAStartup(MAKEWORD(2,0),wsd);
s=socket(AF_INET,SOCK_STREAM,IP proto _ TCP);
if(s==INVALID_SOCKET)
{
printf("套接字创建错误");
}
结构sockaddr _ in ch
memset(ch,0,sizeof(ch));
ch.sin _ family=AF _ INET
栗色sin _ addr。s _ addr=in addr _ ANY
栗色sin _ port=htons(1041);
int b=bind(s,(struct sockaddr *) ch,sizeof(ch));
if(b==套接字_错误)
{
printf('绑定失败,出错代码是:%d\n ',WSAGetLastError());
退出(0);
}
#定义队列大小5
int l=listen(s,QUEUE _ SIZE);
printf('正在监听本机的1041端口!\ n’);
创建线程();
for(int I=0;i1000我)
{
pthread_join(t[i],NULL);
}
int is=关闭套接字;
}
空的发送()
{
WSAData wsd
WSAStartup(MAKEWORD(2,0),wsd);
套接字s=空
s=socket(AF_INET,SOCK_STREAM,IP proto _ TCP);
if(s==INVALID_SOCKET)
{
printf("套接字创建错误");
}
结构sockaddr _ in ch
memset(ch,0,sizeof(ch));
ch.sin _ family=AF _ INET
栗色sin _ addr。s _ addr=inet _ addr(' 127。0 .0 .1 ');
栗色sin _ port=htons(1041);
int c=connect(s,(struct sockaddr *) ch,sizeof(ch));
printf('已经连接到服务器的1041端口!现在可以向服务器发送消息了!\ n’);
#定义BUF_SIZE 4096
char info[1024],BUF[BUF _ SIZE];
while(1)
{
获取(信息);
if(info[0]=='\0 ')
打破;
strcpy(buf,info);
int nsend=send(s,buf,strlen(buf),0);
}
int IC=关闭套接字;
}
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。