博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux C++/Java/Web/OC Socket网络编程
阅读量:6502 次
发布时间:2019-06-24

本文共 32688 字,大约阅读时间需要 108 分钟。

一,Linux C++ Socket网络编程

1.什么是TCP/IPUDP

  TCP/IPTransmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

  UDPUser Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
  下面的图表明了这些协议的关系。

 

2.Socket在哪里呢?

3.Socket是什么呢?

  Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

  门面模式,用自己的话说,就是系统对外界提供单一的接口,外部不需要了解内部的实现。

4.有很多的框架,为什么还在从Socket开始?

  现在的跨平台网络编程框架很多,如Java的SSH,C/C++的Boost等。

  现在的分布式框架很多,如Hadoop等。

  Socket是分布式、云计算、网络编程的基础,对Socket的学习有利于对其他框架的理解。

  下图是Socket编程的基本流程:

 

 

第一步:建立一个socket

int socket(int af, int type, int protocol) 

A. 'int af'代表地址族或者称为socket所代表的域,通常有两个选项: 

    1. AF_UNIX - 只在单机上使用。 
    2. AF_INET - 可以在单机或其他使用DARPA协议(UDP/TCP/IP)的异种机通信。 
B. 'int type'代表你所使用的连接类型,通常也有两种情况: 
    1. SOCK_STREAM - 用来建立面向连接的sockets,可以进行可靠无误的的数据传输 
    2. SOCK_DGRAM - 用来建立没有连接的sockets,不能保证数据传输的可靠性。
C. 'int protocol'通常设定为0。使系统选择默认的由协议族和连接类型所确定的协议。
D. 返回值是一个文件描述句柄,如果在此期间发生错误则返回-1并且设定了相应的errno。 

int sockfd;    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {        perror("socket 创建出错!");        exit(1);    }

 

第二步:绑定名字socket: bind() 

int bind(int sockfd, struct sockaddr *name, int namelen) 

A. sockfd是从socket()调用得到的文件描述句柄。

B. name是一个指向sockaddr类型结构的一个指针。

C. namelen给出了文件名的具体长度。

在使用AF_INET地址族的时候,类型定义如下:

struct sockaddr_in {    short int sin_family;    unsigned short int sin_port;    struct in_addr sin_addr;    unsigned char sin_zero[8];};

 

在这个结构种,name.sin_family应当被设定为AF_INET

struct sockaddr_in name;    int sockfd;    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {        exit(1);    }    name.sin_family = AF_INET;    name.sin_port = htons(8087);    name.sin_addr.s_addr = htonl(INADDR_ANY);    bzero(&(name.sin_zero), 8); /* zero out the rest of the space */    if (bind(sockfd, (struct sockaddr *) &name, sizeof(struct sockaddr))            == -1) {        exit(1);    }

 

现在,如果没有问题的话,我们建立的socket就有一个名字了!相反,如果不成功,它会设定相应的错误代码,并使程序退出。

 

第三步:远程连接: connect() 

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)

A. sockfd是我们建立的文件描述句柄

B. serv_addr是一个sockaddr结构,包含目的的地址和端口号

C. addrlen 被设定为sockaddr结构的大小。 

if (connect(sockfd, (struct sockaddr *) &name, sizeof(name)) < 0) {
exit(1); }

 

第四步:监听: listen() 

int listen(int sockfd, int backlog)

B. 参数backlog是指一次可以监听多少个连接 

if (listen(sockfd, 20) == -1) { exit(1); }

 

第五步:阻塞接受连接: accept()

当有人试图从我们打开的端口登陆进来时,我们应该响应他,这个时候就要用到accept()函数了。

int accept(int sockfd, void *addr, int *addrlen)

conn = accept(sockfd, (struct sockaddr*) &name, sizeof(name));    if (conn < 0) {        exit(1);    }

 

第六步:输入和输入的完成: send() and recv() 

int send(int sockfd, const void *msg, int len, int flags)

int recv(int sockfd, void *buf, int len, unsigned int flags)
send(): 
sockfd - socket file descriptor 
msg - message to send 
len - size of message to send 
flags - read 'man send' for more info, set it to 0 for now 
recv(): 
sockfd - socket file descriptor 
buf - data to receive 
len - size of buf 
flags - same as flags in send() 

注意:如果使用的连接类型是SOCK_DGRAM,那么应该使用sendto()和recvfrom()来实现数据传输。

char buffer[BUFFER_SIZE];while (1) {        memset(buffer, 0, sizeof(buffer));        int len = recv(conn, buffer, sizeof(buffer), 0);        if (strcmp(buffer, "exit\n") == 0)            break;        fputs(buffer, stdout);        send(conn, buffer, len, 0);    }

 

结束: close() and shutdown() 

当传输结束时,应当关闭连接。

 

一个服务端等待, 客户端上传文件到服务端,通过输入要上传的文件名,目前只做到仅对当前执行文件的目录下的文件,应该在服务端收到文件路径之后进行处理的。

服务端代码:

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
//for inet_ntoa#include
//for fork()#define SERVER_PORT 6666#define LISTEN_QUEUE 20#define BUFFER_SIZE 1024#define FILE_NAME_MAX_SIZE 512int main() { //设置一个socket地址结构server_addr,代表服务器internet地址, 端口 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); //把一段内存区的内容全部设置为0 server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htons(INADDR_ANY); server_addr.sin_port = htons(SERVER_PORT); //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket int server_socket = socket(PF_INET, SOCK_STREAM, 0); if (server_socket < 0) { printf("Create Socket Failed!"); exit(1); } //把socket和socket地址结构联系起来 if (bind(server_socket, (struct sockaddr*) &server_addr, sizeof(server_addr))) { printf("Server Bind Port: %d Failed!\n", SERVER_PORT); exit(1); } //server_socket用于监听 if (listen(server_socket, LISTEN_QUEUE)) { printf("Server Listen Failed!"); exit(1); } while (1) { //定义客户端的socket地址结构client_addr char buffer[BUFFER_SIZE]; struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); int client_socket = accept(server_socket, (struct sockaddr*) &client_addr, &length); if (client_socket < 0) { printf("Server Accept Failed!\n"); break; } bzero(buffer, BUFFER_SIZE); // 获取客户端要传输的文件名 length = recv(client_socket, buffer, BUFFER_SIZE, 0); if (length < 0) { printf("Server Recieve Data Failed!\n"); break; } char file_name[FILE_NAME_MAX_SIZE + 1]; bzero(file_name, FILE_NAME_MAX_SIZE + 1); strncpy(file_name, buffer, strlen(buffer) > FILE_NAME_MAX_SIZE ? FILE_NAME_MAX_SIZE : strlen(buffer)); // 新建文件 FILE * fp = fopen(file_name, "w"); if (NULL == fp) { printf("File: %s CAN NOT WRITE!\n", file_name); } else { bzero(buffer, BUFFER_SIZE); int file_block_length = 0; while ((file_block_length = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0) { if (file_block_length < 0) { printf("Recieve Data From Client Failed!\n"); break; } int write_length = fwrite(buffer, sizeof(char), file_block_length, fp); if (write_length < file_block_length) { printf("File: %s Write Failed\n", file_name); break; } bzero(buffer, BUFFER_SIZE); } fclose(fp); printf("File: %s Transfer Finished\n\n", file_name); } close(client_socket); } close(server_socket); return 0;}
View Code

客户端

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
//for inet_ntoa#include
//for fork()#define SERVER_PORT 6666#define BUFFER_SIZE 1024#define FILE_NAME_MAX_SIZE 512int main() { //设置一个socket地址结构client_addr,代表客户机internet地址, 端口 struct sockaddr_in client_addr; bzero(&client_addr, sizeof(client_addr)); //把一段内存区的内容全部设置为0 client_addr.sin_family = AF_INET; //internet协议族 client_addr.sin_addr.s_addr = htons(INADDR_ANY); //INADDR_ANY表示自动获取本机地址 client_addr.sin_port = htons(0); //0表示让系统自动分配一个空闲端口 //创建用于internet的流协议(TCP)socket,用client_socket代表客户机socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket < 0) { printf("Create Socket Failed!\n"); exit(1); } //把客户机的socket和客户机的socket地址结构联系起来 if (bind(client_socket, (struct sockaddr*) &client_addr, sizeof(client_addr))) { printf("Client Bind Port Failed!\n"); exit(1); } //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口 struct sockaddr_in server_addr; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); socklen_t server_addr_length = sizeof(server_addr); // 向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接 if (connect(client_socket, (struct sockaddr*) &server_addr, server_addr_length) < 0) { exit(1); } // 连接上服务器, 选择要上传的文件 char file_name[FILE_NAME_MAX_SIZE + 1]; bzero(file_name, FILE_NAME_MAX_SIZE + 1); printf("Please Input File Name Upload To Server: "); scanf("%s", file_name); char buffer[BUFFER_SIZE]; bzero(buffer, BUFFER_SIZE); strncpy(buffer, file_name, strlen(file_name) > BUFFER_SIZE ? BUFFER_SIZE : strlen(file_name)); FILE * fp = fopen(file_name, "r"); if (NULL == fp) { printf("File: %s NOT FOUND! \n", file_name); exit(1); } // 发送文件名 int nameLength = send(client_socket, buffer, BUFFER_SIZE, 0); if (nameLength < 0) { printf("File name Error! \n"); exit(0); } bzero(buffer, BUFFER_SIZE); int file_block_length = 0; while ((file_block_length = fread(buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) { if (send(client_socket, buffer, file_block_length, 0) < 0) { printf("Send File:\t%s Failed\n", file_name); break; } bzero(buffer, BUFFER_SIZE); } printf("File:\t%s Transfer Finished\n", file_name); fclose(fp); close(client_socket); return 0;}
View Code

 

计算机网络实验:Linux Apache下模拟服务器解析验证HTTP-Get请求并发回网页数据

HTTP请求格式:

<request-line>
<headers>
<blank line>
[<request-body>]
说明:第一行必须是一个请求行(request-line),用来说明请求类型,要访问的资源以及所使用的HTTP版本.
      紧接着是一个首部(header)小节,用来说明服务器要使用的附加信息.
      之后是一个空行.
      再后面可以添加任意的其他数据[称之为主体(body)].
例1 GET请求:

GET / HTTP/1.1

Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Host: www.google.cn
Connection: Keep-Alive
说明:请求的第一部分说明了该请求是一个GET请求.该行的第二部分是一个斜杠(/),用来说明请求的是该域名的根目录.该行的最后一部分说明使用的是HTTP1.1版本(另一个可选荐是1.0).
      第2行是请求的第一个首部,HOST将指出请求的目的地.User-Agent,服务器端和客户端脚本都能访问它,它是浏览器类型检测逻辑的重要基础.该信息由你的浏览器来定义,并且在每个请求中自动发送.Connection,通常将浏览器操作设置为Keep-Alive
      第三部分,空行,即使不存在请求主体,这个空行也是必需的.

例2 POST请求:

POST / HTTP1.1

Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive
name=Professional%20Ajax&publisher=Wiley
说明:请求行开始处的GET改为POST,以表示不同的请求类型.
      Content-Type说明了请求主体的内容是如何编码的.浏览器始终以application/x-www-form-urlencoded的格式编码来传送数据,这是针对简单URL编码的MIME类型.Content-Length说明了请求主体的字节数.
      最后请求主体.名称-值对的形式.

HTTP响应格式:

<status-line>
<headers>
<blank line>
[<response-body>]

HTTP/1.1 200 OK

Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8

<html>

      <head></head>
      <body>
            <!--body goes here-->
      </body>
</html>
说明:HTTP状态码200,找到资源,并且一切正常.
      Date:生成响应的日期和时间.
      Content-Type:指定了MIME类型的HTML(text/html),编码类型是UTF-8
      HTML源文体.

 

 

程序代码与简要分析

数据包分析:得到请求的文件,用户名与密码 int analysis_packet(char *packet, char *file, char *ID, char *psw)
HTTP数据包头 202根据时间与数据包内容大小构建,404返回无法连接 int headerBuilder(char *msg, int status, int size)
与本机Java写成的运行的ServerSocket连接本机MySQL数据库校验用户名与密码 bool check(char *ID, char *psw)
主函数,作为服务端监听本机3334端口的数据请求,根据数据包请求校验与分析。 ServerSocket在本机8081端口监听,为Java与MySQL交互的服务端,对每个Socket请求开辟一个线程处理,返回校验结果。
#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
//for inet_ntoa#include
//for fork()#define MAXN 20480struct INFO { char ID[50], psw[50];};int analysis_packet(char *packet, char *file, char *ID, char *psw) { char *p, *q; if ((p = strtok(packet, " \r")) == NULL) return 0; if (strcmp(p, "GET") == 0) { //GET if ((p = strtok(NULL, " \r")) == NULL) return 1; q = p; if ((p = strtok(NULL, " \r")) != NULL && strcmp(p, "HTTP/1.1") == 0) { //协议HTTP/1.1 if ((q = strtok(q, "?")) != NULL) { strcpy(file, q); //文件名 if ((q = strtok(NULL, "?")) != NULL) { char *tmp; if ((tmp = strtok(q, "&")) != NULL) { strcpy(ID, tmp + 3); } if ((tmp = strtok(NULL, "&")) != NULL) { strcpy(psw, tmp + 3); } return 2; } } return 1; } else printf("未知的协议!\n"); } else printf("未知的请求!\n"); return 0;}int headerBuilder(char *msg, int status, int size) { char num[5]; char *wday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; char *wmon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; struct tm *ptr; time_t it; time(&it); ptr = localtime(&it); //取得本地时间 switch (status) { case 200: //状态行 strcat(msg, "HTTP/1.1 200 OK\r\n"); //首部 strcat(msg, "Date: "); strcat(msg, wday[ptr->tm_wday]); strcat(msg, ", "); sprintf(num, "%d", ptr->tm_mday); strcat(msg, num); strcat(msg, " "); strcat(msg, wmon[ptr->tm_mon]); strcat(msg, " "); sprintf(num, "%d", 1900 + ptr->tm_year); strcat(msg, num); strcat(msg, " "); sprintf(num, "%d", ptr->tm_hour); strcat(msg, num); strcat(msg, ":"); sprintf(num, "%d", ptr->tm_min); strcat(msg, num); strcat(msg, ":"); sprintf(num, "%d", ptr->tm_sec); strcat(msg, num); strcat(msg, " GMT\r\n"); strcat(msg, "Content-Type: text/html;charset=utf-8\r\n"); strcat(msg, "Content-Length: "); sprintf(num, "%d", size); strcat(msg, num); strcat(msg, "\r\n\r\n"); break; case 404: //状态行 strcat(msg, "HTTP/1.1 404 Not Found\r\n"); //首部 strcat(msg, "Date: "); strcat(msg, wday[ptr->tm_wday]); strcat(msg, ", "); sprintf(num, "%d", ptr->tm_mday); strcat(msg, num); strcat(msg, " "); strcat(msg, wmon[ptr->tm_mon]); strcat(msg, " "); sprintf(num, "%d", 1900 + ptr->tm_year); strcat(msg, num); strcat(msg, " "); sprintf(num, "%d", ptr->tm_hour); strcat(msg, num); strcat(msg, ":"); sprintf(num, "%d", ptr->tm_min); strcat(msg, num); strcat(msg, ":"); sprintf(num, "%d", ptr->tm_sec); strcat(msg, num); strcat(msg, " GMT\r\n\r\n"); break; } return 0;}bool check(char *ID, char *psw) { printf("check info %s %s\n", ID, psw); int cfd = socket(AF_INET, SOCK_STREAM, 0); int recbytes; char buffer[50] = { '\0' }; struct sockaddr_in s_add; if (-1 == cfd) { printf("socket fail ! \r\n"); return 0; } bzero(&s_add, sizeof(struct sockaddr_in)); s_add.sin_family = AF_INET; s_add.sin_addr.s_addr = inet_addr("127.0.0.1"); s_add.sin_port = htons(8083); if (connect(cfd, (struct sockaddr *) (&s_add), sizeof(s_add)) == -1) { printf("connect fail !\r\n"); return 0; } printf("connect ok !\r\n"); strcpy(buffer, "login"); printf("sending %s\n", buffer); if ((recbytes = send(cfd, buffer, 5, 0)) == -1) { return 0; } strcpy(buffer, ID); buffer[strlen(ID)] = '\0'; printf("sending %s\n", buffer); if ((recbytes = send(cfd, buffer, strlen(ID), 0)) == -1) { return 0; } strcpy(buffer, psw); buffer[strlen(psw)] = '\0'; printf("sending %s\n", buffer); if ((recbytes = send(cfd, buffer, strlen(psw), 0)) == -1) { return 0; } if (-1 == (recbytes = recv(cfd, buffer, 3, 0))) { return 0; } close(cfd); printf("receive %s\n", buffer); if (strcmp(buffer, "YES")) return 1; return 0;}int main() { int sockfd, client_fd; struct sockaddr_in my_addr; struct sockaddr_in remote_addr; char msg[MAXN] = { '\0' }; //创建套接字 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket create failed!"); exit(1); } my_addr.sin_family = AF_INET; //通信类型 my_addr.sin_port = htons(3334); //端口 my_addr.sin_addr.s_addr = INADDR_ANY; //本地地址 bzero(&(my_addr.sin_zero), 8); //绑定端口地址 if (bind(sockfd, (struct sockaddr*) &my_addr, sizeof(struct sockaddr)) == -1) { exit(1); } //开始监听 if (listen(sockfd, 10) == -1) { exit(1); } while (1) { socklen_t sin_size = sizeof(struct sockaddr_in); if ((client_fd = accept(sockfd, (struct sockaddr*) &remote_addr, &sin_size)) == -1) { break; } printf("收到%s的报文\n", (char*) inet_ntoa(remote_addr.sin_addr)); //子进程段 if (!fork()) { char buf[MAXN]; char file[50] = { '\0' }; char path[100] = "/Users/pcforsong/ksacm"; //服务器文件的路径 //读报文 if (read(client_fd, buf, MAXN) < 0) { perror("reading stream error!"); continue; } // printf("%s\n", buf); FILE *fp; char ID[50] = { '\0' }, psw[50] = { '\0' }; bool checked = false; int OP = analysis_packet(buf, file, ID, psw); if (OP) { //get //生成本地文件路径 strcat(path, file); printf("path: %s\n", path); printf("info: %s %s\n", ID, psw); if (!checked && OP == 2 && !check(ID, psw)) exit(0); checked = 1; if ((fp = fopen(path, "r")) != NULL) { //找到文件 //通过文件指针计算文件大小 fseek(fp, 0, SEEK_END); //到文件结尾 int size = ftell(fp); //与文件首偏移字节数 fseek(fp, 0, SEEK_SET); //移回开头 //填写msg添加200响应报文头 headerBuilder(msg, 200, size); while (fgets(buf, 2048, fp) != NULL) { strcat(msg, buf); }; fclose(fp); } else { //为msg添加404响应报文头 headerBuilder(msg, 404, 0); } //向client发送应答报文msg // printf("%s\n", msg); if (send(client_fd, msg, strlen(msg), 0) == -1) perror("send error!"); close(client_fd); } else printf("报文解析失败!\n"); exit(0); } close(client_fd); } return 0;}

 

附上Windows Socket编程   

 

二,JAVA ServerSocket

声明一个ServerSocket打开一个未占用的套接口,并开始ServerSocket.aceept 监听端口。

其他程序可以Socket连接IP与端口,new BufferReader(new InputStreamReader(socket.getInputStream()))接收消息。

这里使用多线程处理Socket请求,利用继承自Thread类的自定义线程类重载它的run()方法,这样虽然每个Socket都被同样的代码处理,但彼此相互独立,各自拥有各自的资源互不干扰。

实现多线程有两个办法,一种采用线程类继承Thread来实现。

另一种做法是实现Runnable接口,通过 new Thread(Runnable的子类实例) 来实现多线程。要提一句,Runnable接口可以实现对同一实例的多线程处理,即线程资源共享,是继承Thread不能做到的,所以Runnable接口被广泛使用于多线程程序。 Thread类构造函数可以为Runnable target赋值,并且Thread类的run方法就是调用target.run(),所以用Runnable接口定义的run方法是父类的。

 

new Thread(new Runnable(){            public void run(){} // 重载Thread中Runnable target的run方法}) {            public void run(){} //子类重载继承自父类的run方法}

 

TCP协议

import java.io.*;import java.net.*;import java.sql.*;import java.util.Vector;class ServerThread implements Runnable {    private Socket socket;// 定义套接口    private BufferedReader in;// 定义输入流    private PrintWriter out;// 定义输出流    Connection conn;    int no;    public ServerThread(Socket s) throws IOException {
// 线程构造函数 socket = s;// 取得传递参数 in = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 创建输入流 out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( socket.getOutputStream())), true);// 创建输出流 } public void run() {
// 重写run方法 System.out.println("监听Socket中..."); try { Class.forName("com.mysql.jdbc.Driver");// 连接数据库 conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/javaicq", "root", ""); while (true) { String OP = in.readLine(); // 停止 if (OP.equals("end")) break; // 登录 else if (OP.equals("login")) { try { PreparedStatement prepare = conn .prepareStatement("select nickname,password from icq where icqno=?");// 设定数据库查寻条件 int g = Integer.parseInt(in.readLine()); String passwd = in.readLine().trim(); prepare.setInt(1, g);// 设定参数 ResultSet rs = prepare.executeQuery();// 执行数据库查寻 if (!rs.next()) out.println("用户不存在"); else {
// 以下比较输入的号码于密码是否相同 String pass = rs.getString("password").trim(); if (!passwd.equals(pass)) { out.println("密码错误"); } else { // 以及注册用户的ip 地址 PreparedStatement online = conn .prepareStatement("update icq set ip=? where icqno=?"); online.setString(1, socket.getInetAddress() .getHostAddress()); online.setInt(2, g); online.executeUpdate(); // set status online PreparedStatement status = conn .prepareStatement("update icq set status=1 where icqno=?"); status.setInt(1, g); status.executeUpdate(); out.println("YES"); } } rs.close(); } catch (Exception e) { e.printStackTrace(); out.println("Error"); } } // 新建 else if (OP.equals("new")) { try { // 准备接受用户的呢称,密码,email,个人资料,籍贯,头像等信息 String nickname = in.readLine().trim(); String password = in.readLine().trim(); String email = in.readLine().trim(); String info = in.readLine().trim(); String place = in.readLine().trim(); int picindex = Integer.parseInt(in.readLine()); PreparedStatement userInfo = conn .prepareStatement("insert into icq(nickname,password,email,info,place,pic,icqno) values(?,?,?,?,?,?,?)"); userInfo.setString(1, nickname); userInfo.setString(2, password); userInfo.setString(3, email); userInfo.setString(4, info); userInfo.setString(5, place); userInfo.setInt(6, picindex); userInfo.setInt(7, Integer.parseInt(Double.toString( Math.random()).substring(3, 8))); userInfo.executeUpdate();// 执行数据库添加 // 查询其注册的号码 PreparedStatement qNo = conn .prepareStatement("select icqno from icq where nickname=?"); qNo.setString(1, nickname); ResultSet rs = qNo.executeQuery(); while (rs.next()) { // 找到最近注册的号码 no = rs.getInt(1); } out.println("YES"); out.println(no); rs.close(); } catch (Exception e) { e.printStackTrace(); out.println("Error"); } } } } catch (IOException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { conn.close(); socket.close(); } catch (IOException e) { } catch (SQLException e) { } } }}public class Server {
// 主服务器类 public static void main(String args[]) throws IOException { ServerSocket so = new ServerSocket(8081);// 在8081端口创建套接口 so.setSoTimeout(100000); System.out.println("服务器已启动 " + so); Socket testServer = new Socket(InetAddress.getLocalHost(), 8081); PrintWriter out = new PrintWriter(new BufferedWriter( new OutputStreamWriter(testServer.getOutputStream())), true); try { while (true) { Socket socket = so.accept();// 无限监听客户的请求 System.out.println("建立Socket连接:" + socket); try {// out.println("login");// out.println(123);// out.println(123); ServerThread st = new ServerThread(socket);// 创建新线程处理Socket new Thread(st).start(); } catch (IOException e) { socket.close(); } } } catch (SocketTimeoutException e) { System.out.println("超时断开连接"); } finally { so.close(); } }}
View Code

 

UDP协议

// 创建UDP Sockettry {            DatagramSocket sendSocket = new DatagramSocket();            DatagramSocket receiveSocket = new DatagramSocket(udpPORT);            System.out.println("创建UDP数据报成功");        } catch (SocketException se) {            se.printStackTrace();            System.out.println("创建UDP数据报失败");        }// 接收数据报                byte array[] = new byte[512];                for (int x = 0; x < 512; x++)                    array[x] = ' ';                DatagramPacket  receivePacket = new DatagramPacket(array, array.length);                receiveSocket.receive(receivePacket);                byte[] data = receivePacket.getData();                String infofromip = receivePacket.getAddress().getHostAddress()                        .toString().trim();                received = new String(data, 0, data.length);// 发送数据报String info = "offline";                byte[] data = info.getBytes();                sendPacket = new DatagramPacket(data, s.length(),                            InetAddress                                    .getByName(whoaddmesip.get(i).toString()),                            sendPort);                sendSocket.send(sendPacket);

 

 

二,OpenSSL编程

 http://www.cnblogs.com/LittleHann/p/3741907.html

 

三,WebSocket

 WebSocket是为解决客户端与服务端实时通信而产生的技术。其本质是先通过HTTP/HTTPS协议进行握手后创建一个用于交换数据的TCP连接,此后服务端与客户端通过此TCP连接进行实时通信。

WebSocket规范当前还没有正式版本,草案变化也较为迅速。Tomcat7(本文中的例程来自7.0.42)当前支持RFC 6455定义的WebSocket,而RFC 6455目前还未冻结,将来可能会修复一些Bug,甚至协议本身也可能会产生一些变化。

RFC6455定义的WebSocket协议由握手和数据传输两个部分组成。

来自客户端的握手信息类似如下:

  1. GET /chat HTTP/1.1  
  2. Host: server.example.com  
  3. Upgrade: websocket  
  4. Connection: Upgrade  
  5. Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==  
  6. Origin: http://example.com  
  7. Sec-WebSocket-Protocol: chat, superchat  
  8. Sec-WebSocket-Version: 13  

服务端的握手信息类似如下:

 
  1. HTTP/1.1 101 Switching Protocols  
  2. Upgrade: websocket  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=  
  5. Sec-WebSocket-Protocol: chat  

一旦客户端和服务端都发送了握手信息并且成功握手,则数据传输部分将开始。数据传输对客户端和服务端而言都是一个双工通信通道,客户端和服务端来回传递的数据称之为“消息”。

客户端通过WebSocket URI发起WebSocket连接,WebSocket URIs模式定义如下:

  ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]  

  wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]  

  ws是普通的WebSocket通信协议,而wss是安全的WebSocket通信协议(就像HTTP与HTTPS之间的差异一样)。在缺省情况下,ws的端口是80而wss的端口是443。

  关于WebSocke协议规范的完整详尽说明,请参考RFC 6455。

  Tomcat7提供的与WebSocket相关的类均位于包org.apache.catalina.websocket之中(包org.apache.catalina.websocket的实现包含于文件catalina.jar之中),它包含有类Constants、MessageInbound、StreamInbound、WebSocketServlet、WsFrame、WsHttpServletRequestWrapper、WsInputStream、WsOutbound。这些类的关系如图 1所示。

 

                                                                 图1

        包org.apache.catalina.websocket中的这些类为WebSocket开发服务端提供了支持,这些类的主要功能简述如下:

        Constants:包org.apache.catalina.websocket中用到的常数定义在这个类中,它只包含静态常数定义,无任何逻辑实现。

        MessageInbound:基于消息的WebSocket实现类(带内消息),应用程序应当扩展这个类并实现其抽象方法onBinaryMessage和onTextMessage。

       StreamInbound:基于流的WebSocket实现类(带内流),应用程序应当扩展这个类并实现其抽象方法onBinaryData和onTextData。

       WebSocketServlet:提供遵循RFC6455的WebSocket连接的Servlet基本实现。客户端使用WebSocket连接服务端时,需要将WebSocketServlet的子类作为连接入口。同时,该子类应当实现WebSocketServlet的抽象方法createWebSocketInbound,以便创建一个inbound实例(MessageInbound或StreamInbound)。

       WsFrame:代表完整的WebSocket框架。

       WsHttpServletRequestWrapper:包装过的HttpServletRequest对象。

       WsInputStream:基于WebSocket框架底层的socket的输入流。

       WsOutbound:提供发送消息到客户端的功能。它提供的所有向客户端的写方法都是同步的,可以防止多线程同时向客户端写入数据。

 

Tomcat是怎样实现WebSocket的

1)要开始使用WebSocket,你必须继承Tomcat的WebSocket类
2)编写自己的类,它继承WebSocketServlet类(由于这是一个Servlet,因此必须把它映射到URL)
3)实现一个消息监听器类,由于它继承自WebSocketServlet类,因此需要自己实现createWebSocketInbound()方法

此方法能够用于监听事件。有两个必须有的方法:

一是 protected void onBinaryData(InputStream inStream);
二是protected void onTextData(Reader reader);

当WebSocket打开或关闭时,如果你希望收到通知,只需简单地重写onOpen()方法和onClose()方法。

把数据写到客户端,必须有StreamInbound实现类,它会引用发送器组件WsOutbound,可以简单地通过调用来取到它:myStreamInbound.getWsOutbound()  

还可以发送二进制数据writeBinaryData(int b);  

 

或者发送文本数据到客户端writeTextData(char c);  

注意:这些方法是互斥的。不要同时调用两种方法,以期待既发送二进制数据,又发送文本数据。

 

package com.ibcio;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServletRequest;import org.apache.catalina.websocket.StreamInbound;@WebServlet(urlPatterns = { "/message" })// 如果要接收浏览器的ws://协议的请求就必须实现WebSocketServlet这个类public class WebSocketMessageServlet extends        org.apache.catalina.websocket.WebSocketServlet {    private static final long serialVersionUID = 1L;    public static int ONLINE_USER_COUNT = 1;    public String getUser(HttpServletRequest request) {        return (String) request.getSession().getAttribute("user");    }    // 跟平常Servlet不同的是,需要实现createWebSocketInbound,在这里初始化自定义的WebSocket连接对象    @Override    protected StreamInbound createWebSocketInbound(String subProtocol,            HttpServletRequest request) {        return new WebSocketMessageInbound(this.getUser(request));    }}

JS

var websocket;            //初始话WebSocket            function initWebSocket() {                if (window.WebSocket) {                    websocket = new WebSocket(encodeURI('ws://localhost:8080/WebSocket/message'));                    websocket.onopen = function() {                        //连接成功                        win.setTitle(title + '  (已连接)');                    }                    websocket.onerror = function() {                        //连接失败                        win.setTitle(title + '  (连接发生错误)');                    }                    websocket.onclose = function() {                        //连接断开                        win.setTitle(title + '  (已经断开连接)');                    }                    //消息接收                    websocket.onmessage = function(message) {                        var message = JSON.parse(message.data);                        //接收用户发送的消息                        if (message.type == 'message') {                            output.receive(message);                        } else if (message.type == 'get_online_user') {                            //获取在线用户列表                            var root = onlineUser.getRootNode();                                                    } else if (message.type == 'user_join') {                            //用户上线                                var root = onlineUser.getRootNode();                                var user = message.user;                                var node = root.createNode({                                    id : user,                                    text : user,                                    iconCls : 'user',                                    leaf : true                                });                                root.appendChild(node);                        } else if (message.type == 'user_leave') {                                //用户下线                                var root = onlineUser.getRootNode();                                var user = message.user;                                var node = root.findChild('id',user);                                root.removeChild(node);                        }                    }                }            };            //发送消息            function send() {                var message = {};                if (websocket != null) {                    if (input.getValue()) {                        Ext.apply(message, {                                    from : user,                                    content : input.getValue(),                                    timestamp : new Date().getTime(),                                    type : 'message'                                });                        websocket.send(JSON.stringify(message));                        //output.receive(message);                        input.setValue('');                    }                } else {                    Ext.Msg.alert('提示', '您已经掉线,无法发送消息!');                }            }        });
View Code

 

四、Objective-C Socket编程

iPhone的标准推荐CFNetwork C库编程.但是编程比较烦躁。在其它OS往往用类来封装的对Socket函数的处理。比如MFC的CAsysncSocket.在iphone也有类似于开源项目.cocoa AsyncSocket库, 官方网站:http://code.google.com/p/cocoaasyncsocket/ 它用来简化CFnetwork的调用.

//建立基于UDP的Socket连接-(void)openUDPServer{    //初始化udp    AsyncUdpSocket *tempSocket=[[AsyncUdpSocket alloc] initWithDelegate:self];    self.udpSocket=tempSocket;    [tempSocket release];    //绑定端口    NSError *error = nil;    [self.udpSocket bindToPort:4333 error:&error];        //发送广播设置    [self.udpSocket enableBroadcast:YES error:&error];        //加入群里,能接收到群里其他客户端的消息    [self.udpSocket joinMulticastGroup:@"127.0.0.1" error:&error];           //启动接收线程    [self.udpSocket receiveWithTimeout:-1 tag:0];}//通过UDP,发送消息-(void)sendMassage:(NSString *)message{           NSDate *nowTime = [NSDate date];        NSMutableString *sendString=[NSMutableString stringWithCapacity:100];    [sendString appendString:message];    //开始发送    BOOL res = [self.udpSocket sendData:[sendString dataUsingEncoding:NSUTF8StringEncoding]                                  toHost:@"127.0.0.1"                                   port:4333                             withTimeout:-1                                        tag:0];           if (!res) {        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示"                                                        message:@"发送失败"                                                       delegate:self                                              cancelButtonTitle:@"取消"                                              otherButtonTitles:nil];        [alert show];        [alert release];    }}

其委托方法

#pragma mark UDP Delegate Methods- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{           [self.udpSocket receiveWithTimeout:-1 tag:0];    NSLog(@"host---->%@",host);       //接收到数据回调

    NSString *info=[[[NSString alloc] initWithData:data encoding: NSUTF8StringEncoding] autorelease];

return YES;}- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error{    //无法发送时,返回的异常提示信息}- (void)onUdpSocket:(AsyncUdpSocket *)sock didNotReceiveDataWithTag:(long)tag dueToError:(NSError *)error{       //无法接收时,返回异常提示信息}

 

转载于:https://www.cnblogs.com/updateofsimon/p/3567022.html

你可能感兴趣的文章
Lua(Codea) 中 table.insert 越界错误原因分析
查看>>
ELK 5.x日志分析 (二) Elasticserach 5.2 安装
查看>>
sbt配置nexus仓库
查看>>
一次奇怪的AP注册异常问题处理
查看>>
TableStore: 海量结构化数据分层存储方案
查看>>
Unity 4.x游戏开发技巧集锦(内部资料)
查看>>
自适应网页设计
查看>>
获取BT节点信息bittorrent-discovery
查看>>
Centos 7使用vsftpd搭建FTP服务器
查看>>
linux下SVN不允许空白日志提交
查看>>
第2周第1课
查看>>
docker制作镜像篇(基于容器)
查看>>
山寨c 标准库中的getline 函数
查看>>
shell时间
查看>>
pfSense book之2.4安装指南
查看>>
org.springframework.data.redis 一次连接获取特定key所有k-v(pipeline)
查看>>
[译稿]同步复制提议 2010-09
查看>>
windows 自动化目录大纲(各企业架构不一样,按需选择)
查看>>
我的友情链接
查看>>
【Visual C++】游戏开发笔记十三 游戏输入消息处理(二) 鼠标消息处理
查看>>