TCP篇 套接字编程学习笔记(一)
一个服务器端程序,如果要编写一个后台服务程序,该后台服务侦听来自局域网内其它计算机的连接请求,处理相关操作或是调度或是进行文件传输。我们大概相要的情况是,服务器端及时响应客户端的请求,不要有阻塞的情况发生。
好,先写一个简单的服务器端的程序。
struct linger Linger;
static stNewSockInfo NewSockInfo; //
套接字线程函数参数
SOCKET Sock; //
侦听用套接字
int nNameLen = 0;
Linger.l_onoff = 1;
Linger.l_linger = 20;
int fBroadcast = 1;
struct sockaddr_in ssin;
WSADATA wsaData;
//
版本协商
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
ZeroMemory( &ssin , sizeof(ssin) );
ssin.sin_family = AF_INET;
//
设置套接字选项
ssin.sin_addr.s_addr = inet_addr( UnicodeToAnsi( NetInfo.wcIPAddress ));
ssin.sin_port = htons((unsigned short)NetInfo.iPort);
if ( ( Sock = socket ( AF_INET , SOCK_STREAM , IPPROTO_TCP ) ) < 0 ) {
WriteLog2(ERRORLOG,__WFILE__,__LINE__,
_T("Socket Create Error
,GetLastError=[%ld]"
),
GetLastError());
exit(0);
}
if (setsockopt ( Sock, SOL_SOCKET, SO_REUSEADDR, (char *)&Linger , sizeof(struct linger )) == SOCKET_ERROR )
{
WriteLog2(ERRORLOG,AnsiToUnicode(__FILE__),__LINE__,
_T("setsockopt Error
,GetLastError=[%ld]"
),
GetLastError() );
}
setsockopt(Sock, SOL_SOCKET, SO_REUSEADDR, (char *)0,0);
int sock_buf_size=1024*200;
setsockopt(Sock, SOL_SOCKET, SO_SNDBUF,
(char *)&sock_buf_size, sizeof(sock_buf_size) );
nNameLen = sizeof(ssin);
if ( bind ( Sock , (struct sockaddr*) &ssin , nNameLen ) < 0 ) {
WriteLog2(ERRORLOG,AnsiToUnicode(__FILE__),__LINE__,
_T("Bind Error
,GetLastError=[%ld]"
),
GetLastError() );
exit(-1);
}
if ( listen ( Sock , 50 ) < 0 ) {
WriteLog2(ERRORLOG,AnsiToUnicode(__FILE__),__LINE__,
_T("Listen Error
,GetLastError=[%ld]"
),
GetLastError() );
exit(-1);
}
|
我并不想写一个完整的程序,我只想说明白一些意思。在我忘了的时候可以拿出来看看。其实在这些基础上再加上个main 就差不多是一个完整的程序了。说一下,上面代码的含义。
l
WSAStartup版本协商,该函数要与
WSACleanup()配套使用。就像你申请了内存要释放内存一个道理。其实在上面的代码
int
iResult = WSAStartup( MAKEWORD(2,2), &wsaData );中要判断一个iResult的返回值,如果不成功就不能再继续了。我没有仔细看这个函数是干嘛用的。我的理解是socket有多个版本1.1与2.0……,调用一下确实可以支持的最高最低版本。只有协商成功后你才可以使用那些socket的API函数
l 版本协商成功后,接下来按照理解应该是先创建一个套接字。要创建一个套接字,得给要创建的套接字设置一些选项吧。或者说,你总应该设置一个你的服务器在哪个地址上进行侦听在哪个端口上侦听吧。
struct sockaddr_in ssin; 定义中出现了一个结构。那这个结构是干什么的呢?结构的具体内容转到相关的定义看看就知道了。此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。sa_family是协议地址类型,如果使用TCP/IP协议簇,那么你的地址类型应该为AF_INET。Sa_data中存储具体的协议地址,不同的协议簇有不同的地址格式。再下面的a_addr 与 port 一看就知道应该是设置IP地址与端口的。为什么还要用两个函数转换一下呢。这是因为要将主机字节顺序转换为网络字节顺序。IP地址可以有两种不同的表示方法。一种是字符串的一种是整型的。他们两者是可以相互转换的。稍后再说,并附上转换方法。
l 先不说setsockopt这个函数,咋一看我也说不好这个函数的具体作用,如果只是简单的做一个demo。把他注释掉,还是稍后再说。
l 版本协商完了,套接字也创建成功了,作为一个服务器程序还需要做什么样的工作呢?你的套接字是建完了,可是别人怎么知道呢?那怎么才能将你的套按字与服务器绑定在一起呢?为了客户机的连接,服务器必须公开自己的地址。(客户机就一般不需要绑定自己的地址)。Bind函数,这个函数会将服务器的地址和套接字绑定在一起。了解的函数的作用,从msdn上查一下各个参数的使用方式,很容易就能理解并使用了。
l 那套接字与地址进行了绑字,是不是感觉上还少了点东西?我们应该设置服务器的套接字为侦听套接字。可以侦听来自各个角落的套接字的连接。哪个函数可以实现这个功能呢?listen,该函数是将套按字转换为侦听套接字,这是只有服务器才需要执行的操作。Listen这个函数会告诉系统,这个套接字可以接收来自客户机的请求。
l 做完了这些工作以后是不是应该等待客户端来连接了呢?