用C语言进行最基本的socket编程

来源:互联网 发布:淘宝上回收手机骗局 编辑:程序博客网 时间:2024/05/21 17:06

什么是socket
  你经常听到人们谈论着 “socket”,或许你还不知道它的确切含义。现在让我告诉你:它是使用 标准Unix 文件描述符 (file descriptor) 和其它程序通讯的方式。什么?你也许听到一些Unix高手(hacker)这样说过:“呀,Unix中的一切就是文件!”那个家伙也许正在说到一个事实:Unix 程序在执行任何形式的 I/O 的时候,程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。但是(注意后面的话),这个文件可能是一个网络连接,FIFO,管道,终端,磁盘上的文件或者什么其它的东西。Unix 中所有的东西就是文件!所以,你想和Internet上别的程序通讯的时候,你将要使用到文件描述符。你必须理解刚才的话。现在你脑海中或许冒出这样的念头:“那么我从哪里得到网络通讯的文件描述符呢?”,这个问题无论如何我都要回答:你利用系统调用 socket(),它返回套接字描述符 (socket descriptor),然后你再通过它来进行send() 和 recv()调用。“但是...”,你可能有很大的疑惑,“如果它是个文件描述符,那么为什 么不用一般调用read()和write()来进行套接字通讯?”简单的答案是:“你可以使用!”。详细的答案是:“你可以,但是使用send()和recv()让你更好的控制数据传输。”存在这样一个情况:在我们的世界上,有很多种套接字。有DARPA Internet 地址 (Internet 套接字),本地节点的路径名 (Unix套接字),CCITT X.25地址 (你可以将X.25 套接字完全忽略)。也许在你的Unix 机器上还有其它的。我们在这里只讲第一种:Internet 套接字。
Internet 套接字的两种类型 :
  什么意思?有两种类型的Internet 套接字?是的。不,我在撒谎。其实还有很多,但是我可不想吓着你。我们这里只讲两种。除了这些, 我打算另外介绍的 "Raw Sockets" 也是非常强大的,很值得查阅。
那么这两种类型是什么呢?一种是"Stream Sockets"(流格式),另外一种是"Datagram Sockets"(数据包格式)。我们以后谈到它们的时候也会用到"SOCK_STREAM" 和 "SOCK_DGRAM"。数据报套接字有时也叫“无连接套接字”(如果你确实要连接的时候可以用connect()。) 流式套接字是可靠的双向通讯的数据流。如果你向套接字按顺序输出“1,2”,那么它们将按顺序“1,2”到达另一边。它们是无错误的传递的,有自己的错误控制,在此不讨论。
    有什么在使用流式套接字?你可能听说过 telnet,不是吗?它就使用流式套接字。你需要你所输入的字符按顺序到达,不是吗?同样,WWW浏览器使用的 HTTP 协议也使用它们来下载页面。实际上,当你通过端口80 telnet 到一个 WWW 站点,然后输入 “GET pagename” 的时候,你也可以得到 HTML 的内容。为什么流式套接字可以达到高质量的数据传输?这是因为它使用了“传输控制协议 (The Transmission Control Protocol)”,也叫 “TCP” (请参考 RFC-793 获得详细资料。)TCP 控制你的数据按顺序到达并且没有错
误。你也许听到 “TCP” 是因为听到过 “TCP/IP”。这里的 IP 是指“Internet 协议”(请参考 RFC-791。) IP 只是处理Internet 路由而已。 
    那么数据报套接字呢?为什么它叫无连接呢?为什么它是不可靠的呢?有这样的一些事实:如果你发送一个数据报,它可能会到达,它可能次序颠倒了。如果它到达,那么在这个包的内部是无错误的。数据报也使用 IP 作路由,但是它不使用 TCP。它使用“用户数据报协议 (User Datagram Protocol)”,也叫 “UDP” (请参考 RFC-768。)
    为什么它们是无连接的呢?主要是因为它并不象流式套接字那样维持一个连接。你只要建立一个包,构造一个有目标信息的IP 头,然后发出去。无需连接。它们通常使用于传输包-包信息。简单的应用程序有:tftp, bootp等等。
    你也许会想:“假如数据丢失了这些程序如何正常工作?”我的朋友,每个程序在 UDP 上有自己的协议。例如,tftp 协议每发出的一个被接受到包,收到者必须发回一个包来说“我收到了!” (一个“命令正确应答”也叫“ACK” 包)。如果在一定时间内(例如5秒),发送方没有收到应答,它将重新发送,直到得到 ACK。这一ACK过程在实现SOCK_DGRAM 应用程序的时候非常重要。

简单的发送和接收实现

服务器端接收代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib")
#include <stdio.h>
#include <memory.h>
 
voidmain()
{
WSAData wsd;
WSAStartup(MAKEWORD(2,0),&wsd);
 
SOCKET s =NULL;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
structsockaddr_in ch;
memset(&ch,0,sizeof(ch));
ch.sin_family=AF_INET;
ch.sin_addr.s_addr=INADDR_ANY;
ch.sin_port=htons(1041);
intb=bind(s,(structsockaddr *) &ch,sizeof(ch));
#define QUEUE_SIZE 5
intl=listen(s,QUEUE_SIZE);
printf("正在监听本机的1041端口!\n");
SOCKET sc=accept(s,0,0);
printf("客户端已经连接到本机的1041端口!\n");
#define BUF_SIZE 4096
intreceByt=0;
while(1)
{
charbuf[BUF_SIZE];
receByt=recv(sc,buf,BUF_SIZE,0);
buf[receByt]='\0';
if(receByt>0)
{
printf("接收的消息是:%s\n",buf);
}
else
{
printf("接收消息结束!");
break;
}
 
}
intic=closesocket(sc);
intis=closesocket(s);
 
}

客户端发送的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib")
#include <stdio.h>
#include <memory.h>
#include <string.h>
 
voidmain()
{
WSAData wsd;
WSAStartup(MAKEWORD(2,0),&wsd);
 
SOCKET s =NULL;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
structsockaddr_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);
 
intc=connect(s,(structsockaddr *) &ch,sizeof(ch));
printf("已经连接到服务器的1041端口!现在可以向服务器发送消息了!\n");
#define BUF_SIZE 4096
charinfo[1024],buf[BUF_SIZE];
 
while(1)
{
gets(info);
if(info[0]=='\0')
break;
strcpy(buf,info);
intnsend=send(s,buf,strlen(buf),0);
 
}
intic=closesocket(s);
}

程序代码经过了优化,并且整合多线程,把接收和发送放到同一个文件中,使用参数模式调用发送和接收模块。增加了创建SOCKET的创建的时候s句柄(或对象)判断返回值是否为INVALID_SOCKET,以及socket的bind操作的返回值是否为SOCKET_ERROR,其他socket的操作应该也判断SOCKET_ERROR,以保证程序的稳定性,这里只是测试代码就不去写这么多了,剩下的就由你个人发挥。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <Winsock2.h>
#pragma comment(lib,"Ws2_32.lib")
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <pthread.h>
 
 
voidReceive();
voidSend();
voidcreatThread();
 
SOCKET s =NULL;
pthread_t t[1000];
intthreadCount=0;
 
voidmain(intargc,char* argv[])
{
  printf("本程序制作人学号:713901040041\n");
  printf("程序说明:服务器端和客户端为同一个程序,请使用不同的参数运行.\n");
  printf("接收程序请使用 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');
 
  if(argc<=1)
  {
    printf("please input program arguments ...\n");
    exit(0);
  }
  if(argc>1 && argv[1][0]=='r')
  {
    printf("run receive ...\n");
    Receive();
  }
  if(argc>1 && argv[1][0]=='s')
  {
    printf("run send ...\n");
    Send();
  }
}
 
 
void* receiveWork(void* args)
{
  SOCKET sc=accept(s,0,0);
  if(sc==INVALID_SOCKET)
  {
    printf("sc Error");
  }
  creatThread();
 
  printf("----------客户端已经连接到本机的%d线程连接!\n",threadCount-2);
#define BUF_SIZE 4096
  intreceByt=0;
  while(1)
  {
    charbuf[BUF_SIZE];
    receByt=recv(sc,buf,BUF_SIZE,0);
    buf[receByt]='\0';
    if(receByt>0)
    {
      printf("线程接收的消息是:%s\n",buf);
    }
    else
    {
      printf("客户端已退出,");
      break;
    }
       
  }
  intic=closesocket(sc);
  printf("服务器结束连接!\n");
  returnNULL;
}
 
voidcreatThread()
{
  pthread_create(&t[threadCount++],NULL,receiveWork,NULL);
}
 
 
voidReceive()
{
  WSAData wsd;
  WSAStartup(MAKEWORD(2,0),&wsd); 
  s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(s==INVALID_SOCKET)
  {
    printf("socket created Error");
  }
  structsockaddr_in ch;
  memset(&ch,0,sizeof(ch));
  ch.sin_family=AF_INET;
  ch.sin_addr.s_addr=INADDR_ANY;
  ch.sin_port=htons(1041);
  intb=bind(s,(structsockaddr *) &ch,sizeof(ch));
  if(b==SOCKET_ERROR)
  {
    printf("bind 失败,出错代码是:%d\n",WSAGetLastError());
    exit(0);
  }
#define QUEUE_SIZE 5
  intl=listen(s,QUEUE_SIZE);
  printf("正在监听本机的1041端口!\n");
   
  creatThread();
 
  for(inti=0;i<1000;i++)
  {
    pthread_join(t[i],NULL);
  }
 
intis=closesocket(s);
}
 
 
 
voidSend()
{
  WSAData wsd;
  WSAStartup(MAKEWORD(2,0),&wsd);
 
  SOCKET s =NULL;
  s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
  if(s==INVALID_SOCKET)
  {
    printf("socket created Error");
  }
  structsockaddr_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);
 
  intc=connect(s,(structsockaddr *) &ch,sizeof(ch));
  printf("已经连接到服务器的1041端口!现在可以向服务器发送消息了!\n");
#define BUF_SIZE 4096
  charinfo[1024],buf[BUF_SIZE];
 
  while(1)
  {
    gets(info);
    if(info[0]=='\0')
      break;
    strcpy(buf,info);
    intnsend=send(s,buf,strlen(buf),0);
  }
  intic=closesocket(s);
}
原创粉丝点击